Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { IDisposable } from '../../../../base/common/lifecycle.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
9
import { EditorOption } from '../../../common/config/editorOptions.js';
10
import { Position } from '../../../common/core/position.js';
11
import { IRange, Range } from '../../../common/core/range.js';
12
import { MetadataConsts } from '../../../common/encodedTokenAttributes.js';
13
import * as languages from '../../../common/languages.js';
14
import { NullState } from '../../../common/languages/nullTokenize.js';
15
import { EndOfLinePreference } from '../../../common/model.js';
16
import { TextModel } from '../../../common/model/textModel.js';
17
import { ModelLineProjectionData } from '../../../common/modelLineProjectionData.js';
18
import { IViewLineTokens } from '../../../common/tokens/lineTokens.js';
19
import { ViewLineData } from '../../../common/viewModel.js';
20
import { IModelLineProjection, ISimpleModel, createModelLineProjection } from '../../../common/viewModel/modelLineProjection.js';
21
import { MonospaceLineBreaksComputerFactory } from '../../../common/viewModel/monospaceLineBreaksComputer.js';
22
import { ViewModelLinesFromProjectedModel } from '../../../common/viewModel/viewModelLines.js';
23
import { TestConfiguration } from '../config/testConfiguration.js';
24
import { createTextModel } from '../../common/testTextModel.js';
25
26
suite('Editor ViewModel - SplitLinesCollection', () => {
27
28
ensureNoDisposablesAreLeakedInTestSuite();
29
30
test('SplitLine', () => {
31
let model1 = createModel('My First LineMy Second LineAnd another one');
32
let line1 = createSplitLine([13, 14, 15], [13, 13 + 14, 13 + 14 + 15], 0);
33
34
assert.strictEqual(line1.getViewLineCount(), 3);
35
assert.strictEqual(line1.getViewLineContent(model1, 1, 0), 'My First Line');
36
assert.strictEqual(line1.getViewLineContent(model1, 1, 1), 'My Second Line');
37
assert.strictEqual(line1.getViewLineContent(model1, 1, 2), 'And another one');
38
assert.strictEqual(line1.getViewLineMaxColumn(model1, 1, 0), 14);
39
assert.strictEqual(line1.getViewLineMaxColumn(model1, 1, 1), 15);
40
assert.strictEqual(line1.getViewLineMaxColumn(model1, 1, 2), 16);
41
for (let col = 1; col <= 14; col++) {
42
assert.strictEqual(line1.getModelColumnOfViewPosition(0, col), col, 'getInputColumnOfOutputPosition(0, ' + col + ')');
43
}
44
for (let col = 1; col <= 15; col++) {
45
assert.strictEqual(line1.getModelColumnOfViewPosition(1, col), 13 + col, 'getInputColumnOfOutputPosition(1, ' + col + ')');
46
}
47
for (let col = 1; col <= 16; col++) {
48
assert.strictEqual(line1.getModelColumnOfViewPosition(2, col), 13 + 14 + col, 'getInputColumnOfOutputPosition(2, ' + col + ')');
49
}
50
for (let col = 1; col <= 13; col++) {
51
assert.deepStrictEqual(line1.getViewPositionOfModelPosition(0, col), pos(0, col), 'getOutputPositionOfInputPosition(' + col + ')');
52
}
53
for (let col = 1 + 13; col <= 14 + 13; col++) {
54
assert.deepStrictEqual(line1.getViewPositionOfModelPosition(0, col), pos(1, col - 13), 'getOutputPositionOfInputPosition(' + col + ')');
55
}
56
for (let col = 1 + 13 + 14; col <= 15 + 14 + 13; col++) {
57
assert.deepStrictEqual(line1.getViewPositionOfModelPosition(0, col), pos(2, col - 13 - 14), 'getOutputPositionOfInputPosition(' + col + ')');
58
}
59
60
model1 = createModel('My First LineMy Second LineAnd another one');
61
line1 = createSplitLine([13, 14, 15], [13, 13 + 14, 13 + 14 + 15], 4);
62
63
assert.strictEqual(line1.getViewLineCount(), 3);
64
assert.strictEqual(line1.getViewLineContent(model1, 1, 0), 'My First Line');
65
assert.strictEqual(line1.getViewLineContent(model1, 1, 1), ' My Second Line');
66
assert.strictEqual(line1.getViewLineContent(model1, 1, 2), ' And another one');
67
assert.strictEqual(line1.getViewLineMaxColumn(model1, 1, 0), 14);
68
assert.strictEqual(line1.getViewLineMaxColumn(model1, 1, 1), 19);
69
assert.strictEqual(line1.getViewLineMaxColumn(model1, 1, 2), 20);
70
71
const actualViewColumnMapping: number[][] = [];
72
for (let lineIndex = 0; lineIndex < line1.getViewLineCount(); lineIndex++) {
73
const actualLineViewColumnMapping: number[] = [];
74
for (let col = 1; col <= line1.getViewLineMaxColumn(model1, 1, lineIndex); col++) {
75
actualLineViewColumnMapping.push(line1.getModelColumnOfViewPosition(lineIndex, col));
76
}
77
actualViewColumnMapping.push(actualLineViewColumnMapping);
78
}
79
assert.deepStrictEqual(actualViewColumnMapping, [
80
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14],
81
[14, 14, 14, 14, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28],
82
[28, 28, 28, 28, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
83
]);
84
85
for (let col = 1; col <= 13; col++) {
86
assert.deepStrictEqual(line1.getViewPositionOfModelPosition(0, col), pos(0, col), '6.getOutputPositionOfInputPosition(' + col + ')');
87
}
88
for (let col = 1 + 13; col <= 14 + 13; col++) {
89
assert.deepStrictEqual(line1.getViewPositionOfModelPosition(0, col), pos(1, 4 + col - 13), '7.getOutputPositionOfInputPosition(' + col + ')');
90
}
91
for (let col = 1 + 13 + 14; col <= 15 + 14 + 13; col++) {
92
assert.deepStrictEqual(line1.getViewPositionOfModelPosition(0, col), pos(2, 4 + col - 13 - 14), '8.getOutputPositionOfInputPosition(' + col + ')');
93
}
94
});
95
96
function withSplitLinesCollection(text: string, callback: (model: TextModel, linesCollection: ViewModelLinesFromProjectedModel) => void): void {
97
const config = new TestConfiguration({});
98
const wrappingInfo = config.options.get(EditorOption.wrappingInfo);
99
const fontInfo = config.options.get(EditorOption.fontInfo);
100
const wordWrapBreakAfterCharacters = config.options.get(EditorOption.wordWrapBreakAfterCharacters);
101
const wordWrapBreakBeforeCharacters = config.options.get(EditorOption.wordWrapBreakBeforeCharacters);
102
const wrappingIndent = config.options.get(EditorOption.wrappingIndent);
103
const wordBreak = config.options.get(EditorOption.wordBreak);
104
const wrapOnEscapedLineFeeds = config.options.get(EditorOption.wrapOnEscapedLineFeeds);
105
const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters);
106
107
const model = createTextModel([
108
'int main() {',
109
'\tprintf("Hello world!");',
110
'}',
111
'int main() {',
112
'\tprintf("Hello world!");',
113
'}',
114
].join('\n'));
115
116
const linesCollection = new ViewModelLinesFromProjectedModel(
117
1,
118
model,
119
lineBreaksComputerFactory,
120
lineBreaksComputerFactory,
121
fontInfo,
122
model.getOptions().tabSize,
123
'simple',
124
wrappingInfo.wrappingColumn,
125
wrappingIndent,
126
wordBreak,
127
wrapOnEscapedLineFeeds
128
);
129
130
callback(model, linesCollection);
131
132
linesCollection.dispose();
133
model.dispose();
134
config.dispose();
135
}
136
137
test('Invalid line numbers', () => {
138
139
const text = [
140
'int main() {',
141
'\tprintf("Hello world!");',
142
'}',
143
'int main() {',
144
'\tprintf("Hello world!");',
145
'}',
146
].join('\n');
147
148
withSplitLinesCollection(text, (model, linesCollection) => {
149
assert.strictEqual(linesCollection.getViewLineCount(), 6);
150
151
// getOutputIndentGuide
152
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(-1, -1), [0]);
153
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(0, 0), [0]);
154
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(1, 1), [0]);
155
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(2, 2), [1]);
156
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(3, 3), [0]);
157
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(4, 4), [0]);
158
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(5, 5), [1]);
159
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(6, 6), [0]);
160
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(7, 7), [0]);
161
162
assert.deepStrictEqual(linesCollection.getViewLinesIndentGuides(0, 7), [0, 1, 0, 0, 1, 0]);
163
164
// getOutputLineContent
165
assert.strictEqual(linesCollection.getViewLineContent(-1), 'int main() {');
166
assert.strictEqual(linesCollection.getViewLineContent(0), 'int main() {');
167
assert.strictEqual(linesCollection.getViewLineContent(1), 'int main() {');
168
assert.strictEqual(linesCollection.getViewLineContent(2), '\tprintf("Hello world!");');
169
assert.strictEqual(linesCollection.getViewLineContent(3), '}');
170
assert.strictEqual(linesCollection.getViewLineContent(4), 'int main() {');
171
assert.strictEqual(linesCollection.getViewLineContent(5), '\tprintf("Hello world!");');
172
assert.strictEqual(linesCollection.getViewLineContent(6), '}');
173
assert.strictEqual(linesCollection.getViewLineContent(7), '}');
174
175
// getOutputLineMinColumn
176
assert.strictEqual(linesCollection.getViewLineMinColumn(-1), 1);
177
assert.strictEqual(linesCollection.getViewLineMinColumn(0), 1);
178
assert.strictEqual(linesCollection.getViewLineMinColumn(1), 1);
179
assert.strictEqual(linesCollection.getViewLineMinColumn(2), 1);
180
assert.strictEqual(linesCollection.getViewLineMinColumn(3), 1);
181
assert.strictEqual(linesCollection.getViewLineMinColumn(4), 1);
182
assert.strictEqual(linesCollection.getViewLineMinColumn(5), 1);
183
assert.strictEqual(linesCollection.getViewLineMinColumn(6), 1);
184
assert.strictEqual(linesCollection.getViewLineMinColumn(7), 1);
185
186
// getOutputLineMaxColumn
187
assert.strictEqual(linesCollection.getViewLineMaxColumn(-1), 13);
188
assert.strictEqual(linesCollection.getViewLineMaxColumn(0), 13);
189
assert.strictEqual(linesCollection.getViewLineMaxColumn(1), 13);
190
assert.strictEqual(linesCollection.getViewLineMaxColumn(2), 25);
191
assert.strictEqual(linesCollection.getViewLineMaxColumn(3), 2);
192
assert.strictEqual(linesCollection.getViewLineMaxColumn(4), 13);
193
assert.strictEqual(linesCollection.getViewLineMaxColumn(5), 25);
194
assert.strictEqual(linesCollection.getViewLineMaxColumn(6), 2);
195
assert.strictEqual(linesCollection.getViewLineMaxColumn(7), 2);
196
197
// convertOutputPositionToInputPosition
198
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(-1, 1), new Position(1, 1));
199
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(0, 1), new Position(1, 1));
200
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(1, 1), new Position(1, 1));
201
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(2, 1), new Position(2, 1));
202
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(3, 1), new Position(3, 1));
203
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(4, 1), new Position(4, 1));
204
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(5, 1), new Position(5, 1));
205
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(6, 1), new Position(6, 1));
206
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(7, 1), new Position(6, 1));
207
assert.deepStrictEqual(linesCollection.convertViewPositionToModelPosition(8, 1), new Position(6, 1));
208
});
209
});
210
211
test('issue #3662', () => {
212
213
const text = [
214
'int main() {',
215
'\tprintf("Hello world!");',
216
'}',
217
'int main() {',
218
'\tprintf("Hello world!");',
219
'}',
220
].join('\n');
221
222
withSplitLinesCollection(text, (model, linesCollection) => {
223
linesCollection.setHiddenAreas([
224
new Range(1, 1, 3, 1),
225
new Range(5, 1, 6, 1)
226
]);
227
228
const viewLineCount = linesCollection.getViewLineCount();
229
assert.strictEqual(viewLineCount, 1, 'getOutputLineCount()');
230
231
const modelLineCount = model.getLineCount();
232
for (let lineNumber = 0; lineNumber <= modelLineCount + 1; lineNumber++) {
233
const lineMinColumn = (lineNumber >= 1 && lineNumber <= modelLineCount) ? model.getLineMinColumn(lineNumber) : 1;
234
const lineMaxColumn = (lineNumber >= 1 && lineNumber <= modelLineCount) ? model.getLineMaxColumn(lineNumber) : 1;
235
for (let column = lineMinColumn - 1; column <= lineMaxColumn + 1; column++) {
236
const viewPosition = linesCollection.convertModelPositionToViewPosition(lineNumber, column);
237
238
// validate view position
239
let viewLineNumber = viewPosition.lineNumber;
240
let viewColumn = viewPosition.column;
241
if (viewLineNumber < 1) {
242
viewLineNumber = 1;
243
}
244
const lineCount = linesCollection.getViewLineCount();
245
if (viewLineNumber > lineCount) {
246
viewLineNumber = lineCount;
247
}
248
const viewMinColumn = linesCollection.getViewLineMinColumn(viewLineNumber);
249
const viewMaxColumn = linesCollection.getViewLineMaxColumn(viewLineNumber);
250
if (viewColumn < viewMinColumn) {
251
viewColumn = viewMinColumn;
252
}
253
if (viewColumn > viewMaxColumn) {
254
viewColumn = viewMaxColumn;
255
}
256
const validViewPosition = new Position(viewLineNumber, viewColumn);
257
assert.strictEqual(viewPosition.toString(), validViewPosition.toString(), 'model->view for ' + lineNumber + ', ' + column);
258
}
259
}
260
261
for (let lineNumber = 0; lineNumber <= viewLineCount + 1; lineNumber++) {
262
const lineMinColumn = linesCollection.getViewLineMinColumn(lineNumber);
263
const lineMaxColumn = linesCollection.getViewLineMaxColumn(lineNumber);
264
for (let column = lineMinColumn - 1; column <= lineMaxColumn + 1; column++) {
265
const modelPosition = linesCollection.convertViewPositionToModelPosition(lineNumber, column);
266
const validModelPosition = model.validatePosition(modelPosition);
267
assert.strictEqual(modelPosition.toString(), validModelPosition.toString(), 'view->model for ' + lineNumber + ', ' + column);
268
}
269
}
270
});
271
});
272
273
});
274
275
suite('SplitLinesCollection', () => {
276
277
const _text = [
278
'class Nice {',
279
' function hi() {',
280
' console.log("Hello world");',
281
' }',
282
' function hello() {',
283
' console.log("Hello world, this is a somewhat longer line");',
284
' }',
285
'}',
286
];
287
288
const _tokens = [
289
[
290
{ startIndex: 0, value: 1 },
291
{ startIndex: 5, value: 2 },
292
{ startIndex: 6, value: 3 },
293
{ startIndex: 10, value: 4 },
294
],
295
[
296
{ startIndex: 0, value: 5 },
297
{ startIndex: 1, value: 6 },
298
{ startIndex: 9, value: 7 },
299
{ startIndex: 10, value: 8 },
300
{ startIndex: 12, value: 9 },
301
],
302
[
303
{ startIndex: 0, value: 10 },
304
{ startIndex: 2, value: 11 },
305
{ startIndex: 9, value: 12 },
306
{ startIndex: 10, value: 13 },
307
{ startIndex: 13, value: 14 },
308
{ startIndex: 14, value: 15 },
309
{ startIndex: 27, value: 16 },
310
],
311
[
312
{ startIndex: 0, value: 17 },
313
],
314
[
315
{ startIndex: 0, value: 18 },
316
{ startIndex: 1, value: 19 },
317
{ startIndex: 9, value: 20 },
318
{ startIndex: 10, value: 21 },
319
{ startIndex: 15, value: 22 },
320
],
321
[
322
{ startIndex: 0, value: 23 },
323
{ startIndex: 2, value: 24 },
324
{ startIndex: 9, value: 25 },
325
{ startIndex: 10, value: 26 },
326
{ startIndex: 13, value: 27 },
327
{ startIndex: 14, value: 28 },
328
{ startIndex: 59, value: 29 },
329
],
330
[
331
{ startIndex: 0, value: 30 },
332
],
333
[
334
{ startIndex: 0, value: 31 },
335
]
336
];
337
338
let model: TextModel;
339
let languageRegistration: IDisposable;
340
341
setup(() => {
342
let _lineIndex = 0;
343
const tokenizationSupport: languages.ITokenizationSupport = {
344
getInitialState: () => NullState,
345
tokenize: undefined!,
346
tokenizeEncoded: (line: string, hasEOL: boolean, state: languages.IState): languages.EncodedTokenizationResult => {
347
const tokens = _tokens[_lineIndex++];
348
349
const result = new Uint32Array(2 * tokens.length);
350
for (let i = 0; i < tokens.length; i++) {
351
result[2 * i] = tokens[i].startIndex;
352
result[2 * i + 1] = (
353
tokens[i].value << MetadataConsts.FOREGROUND_OFFSET
354
);
355
}
356
return new languages.EncodedTokenizationResult(result, state);
357
}
358
};
359
const LANGUAGE_ID = 'modelModeTest1';
360
languageRegistration = languages.TokenizationRegistry.register(LANGUAGE_ID, tokenizationSupport);
361
model = createTextModel(_text.join('\n'), LANGUAGE_ID);
362
// force tokenization
363
model.tokenization.forceTokenization(model.getLineCount());
364
});
365
366
teardown(() => {
367
model.dispose();
368
languageRegistration.dispose();
369
});
370
371
ensureNoDisposablesAreLeakedInTestSuite();
372
373
interface ITestViewLineToken {
374
endIndex: number;
375
value: number;
376
}
377
378
function assertViewLineTokens(_actual: IViewLineTokens, expected: ITestViewLineToken[]): void {
379
const actual: ITestViewLineToken[] = [];
380
for (let i = 0, len = _actual.getCount(); i < len; i++) {
381
actual[i] = {
382
endIndex: _actual.getEndOffset(i),
383
value: _actual.getForeground(i)
384
};
385
}
386
assert.deepStrictEqual(actual, expected);
387
}
388
389
interface ITestMinimapLineRenderingData {
390
content: string;
391
minColumn: number;
392
maxColumn: number;
393
tokens: ITestViewLineToken[];
394
}
395
396
function assertMinimapLineRenderingData(actual: ViewLineData, expected: ITestMinimapLineRenderingData | null): void {
397
if (actual === null && expected === null) {
398
assert.ok(true);
399
return;
400
}
401
if (expected === null) {
402
assert.ok(false);
403
}
404
assert.strictEqual(actual.content, expected.content);
405
assert.strictEqual(actual.minColumn, expected.minColumn);
406
assert.strictEqual(actual.maxColumn, expected.maxColumn);
407
assertViewLineTokens(actual.tokens, expected.tokens);
408
}
409
410
function assertMinimapLinesRenderingData(actual: ViewLineData[], expected: Array<ITestMinimapLineRenderingData | null>): void {
411
assert.strictEqual(actual.length, expected.length);
412
for (let i = 0; i < expected.length; i++) {
413
assertMinimapLineRenderingData(actual[i], expected[i]);
414
}
415
}
416
417
function assertAllMinimapLinesRenderingData(splitLinesCollection: ViewModelLinesFromProjectedModel, all: ITestMinimapLineRenderingData[]): void {
418
const lineCount = all.length;
419
for (let line = 1; line <= lineCount; line++) {
420
assert.strictEqual(splitLinesCollection.getViewLineData(line).content, splitLinesCollection.getViewLineContent(line));
421
}
422
423
for (let start = 1; start <= lineCount; start++) {
424
for (let end = start; end <= lineCount; end++) {
425
const count = end - start + 1;
426
for (let desired = Math.pow(2, count) - 1; desired >= 0; desired--) {
427
const needed: boolean[] = [];
428
const expected: Array<ITestMinimapLineRenderingData | null> = [];
429
for (let i = 0; i < count; i++) {
430
needed[i] = (desired & (1 << i)) ? true : false;
431
expected[i] = (needed[i] ? all[start - 1 + i] : null);
432
}
433
const actual = splitLinesCollection.getViewLinesData(start, end, needed);
434
435
assertMinimapLinesRenderingData(actual, expected);
436
// Comment out next line to test all possible combinations
437
break;
438
}
439
}
440
}
441
}
442
443
test('getViewLinesData - no wrapping', () => {
444
withSplitLinesCollection(model, 'off', 0, false, (splitLinesCollection) => {
445
assert.strictEqual(splitLinesCollection.getViewLineCount(), 8);
446
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(1, 1), true);
447
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(2, 1), true);
448
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(3, 1), true);
449
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(4, 1), true);
450
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(5, 1), true);
451
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(6, 1), true);
452
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(7, 1), true);
453
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(8, 1), true);
454
455
const _expected: ITestMinimapLineRenderingData[] = [
456
{
457
content: 'class Nice {',
458
minColumn: 1,
459
maxColumn: 13,
460
tokens: [
461
{ endIndex: 5, value: 1 },
462
{ endIndex: 6, value: 2 },
463
{ endIndex: 10, value: 3 },
464
{ endIndex: 12, value: 4 },
465
]
466
},
467
{
468
content: ' function hi() {',
469
minColumn: 1,
470
maxColumn: 17,
471
tokens: [
472
{ endIndex: 1, value: 5 },
473
{ endIndex: 9, value: 6 },
474
{ endIndex: 10, value: 7 },
475
{ endIndex: 12, value: 8 },
476
{ endIndex: 16, value: 9 },
477
]
478
},
479
{
480
content: ' console.log("Hello world");',
481
minColumn: 1,
482
maxColumn: 30,
483
tokens: [
484
{ endIndex: 2, value: 10 },
485
{ endIndex: 9, value: 11 },
486
{ endIndex: 10, value: 12 },
487
{ endIndex: 13, value: 13 },
488
{ endIndex: 14, value: 14 },
489
{ endIndex: 27, value: 15 },
490
{ endIndex: 29, value: 16 },
491
]
492
},
493
{
494
content: ' }',
495
minColumn: 1,
496
maxColumn: 3,
497
tokens: [
498
{ endIndex: 2, value: 17 },
499
]
500
},
501
{
502
content: ' function hello() {',
503
minColumn: 1,
504
maxColumn: 20,
505
tokens: [
506
{ endIndex: 1, value: 18 },
507
{ endIndex: 9, value: 19 },
508
{ endIndex: 10, value: 20 },
509
{ endIndex: 15, value: 21 },
510
{ endIndex: 19, value: 22 },
511
]
512
},
513
{
514
content: ' console.log("Hello world, this is a somewhat longer line");',
515
minColumn: 1,
516
maxColumn: 62,
517
tokens: [
518
{ endIndex: 2, value: 23 },
519
{ endIndex: 9, value: 24 },
520
{ endIndex: 10, value: 25 },
521
{ endIndex: 13, value: 26 },
522
{ endIndex: 14, value: 27 },
523
{ endIndex: 59, value: 28 },
524
{ endIndex: 61, value: 29 },
525
]
526
},
527
{
528
minColumn: 1,
529
maxColumn: 3,
530
content: ' }',
531
tokens: [
532
{ endIndex: 2, value: 30 },
533
]
534
},
535
{
536
minColumn: 1,
537
maxColumn: 2,
538
content: '}',
539
tokens: [
540
{ endIndex: 1, value: 31 },
541
]
542
}
543
];
544
545
assertAllMinimapLinesRenderingData(splitLinesCollection, [
546
_expected[0],
547
_expected[1],
548
_expected[2],
549
_expected[3],
550
_expected[4],
551
_expected[5],
552
_expected[6],
553
_expected[7],
554
]);
555
556
splitLinesCollection.setHiddenAreas([new Range(2, 1, 4, 1)]);
557
assert.strictEqual(splitLinesCollection.getViewLineCount(), 5);
558
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(1, 1), true);
559
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(2, 1), false);
560
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(3, 1), false);
561
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(4, 1), false);
562
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(5, 1), true);
563
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(6, 1), true);
564
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(7, 1), true);
565
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(8, 1), true);
566
567
assertAllMinimapLinesRenderingData(splitLinesCollection, [
568
_expected[0],
569
_expected[4],
570
_expected[5],
571
_expected[6],
572
_expected[7],
573
]);
574
});
575
});
576
577
test('getViewLinesData - with wrapping', () => {
578
withSplitLinesCollection(model, 'wordWrapColumn', 30, false, (splitLinesCollection) => {
579
assert.strictEqual(splitLinesCollection.getViewLineCount(), 12);
580
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(1, 1), true);
581
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(2, 1), true);
582
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(3, 1), true);
583
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(4, 1), true);
584
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(5, 1), true);
585
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(6, 1), true);
586
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(7, 1), true);
587
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(8, 1), true);
588
589
const _expected: ITestMinimapLineRenderingData[] = [
590
{
591
content: 'class Nice {',
592
minColumn: 1,
593
maxColumn: 13,
594
tokens: [
595
{ endIndex: 5, value: 1 },
596
{ endIndex: 6, value: 2 },
597
{ endIndex: 10, value: 3 },
598
{ endIndex: 12, value: 4 },
599
]
600
},
601
{
602
content: ' function hi() {',
603
minColumn: 1,
604
maxColumn: 17,
605
tokens: [
606
{ endIndex: 1, value: 5 },
607
{ endIndex: 9, value: 6 },
608
{ endIndex: 10, value: 7 },
609
{ endIndex: 12, value: 8 },
610
{ endIndex: 16, value: 9 },
611
]
612
},
613
{
614
content: ' console.log("Hello ',
615
minColumn: 1,
616
maxColumn: 22,
617
tokens: [
618
{ endIndex: 2, value: 10 },
619
{ endIndex: 9, value: 11 },
620
{ endIndex: 10, value: 12 },
621
{ endIndex: 13, value: 13 },
622
{ endIndex: 14, value: 14 },
623
{ endIndex: 21, value: 15 },
624
]
625
},
626
{
627
content: ' world");',
628
minColumn: 13,
629
maxColumn: 21,
630
tokens: [
631
{ endIndex: 18, value: 15 },
632
{ endIndex: 20, value: 16 },
633
]
634
},
635
{
636
content: ' }',
637
minColumn: 1,
638
maxColumn: 3,
639
tokens: [
640
{ endIndex: 2, value: 17 },
641
]
642
},
643
{
644
content: ' function hello() {',
645
minColumn: 1,
646
maxColumn: 20,
647
tokens: [
648
{ endIndex: 1, value: 18 },
649
{ endIndex: 9, value: 19 },
650
{ endIndex: 10, value: 20 },
651
{ endIndex: 15, value: 21 },
652
{ endIndex: 19, value: 22 },
653
]
654
},
655
{
656
content: ' console.log("Hello ',
657
minColumn: 1,
658
maxColumn: 22,
659
tokens: [
660
{ endIndex: 2, value: 23 },
661
{ endIndex: 9, value: 24 },
662
{ endIndex: 10, value: 25 },
663
{ endIndex: 13, value: 26 },
664
{ endIndex: 14, value: 27 },
665
{ endIndex: 21, value: 28 },
666
]
667
},
668
{
669
content: ' world, this is a ',
670
minColumn: 13,
671
maxColumn: 30,
672
tokens: [
673
{ endIndex: 29, value: 28 },
674
]
675
},
676
{
677
content: ' somewhat longer ',
678
minColumn: 13,
679
maxColumn: 29,
680
tokens: [
681
{ endIndex: 28, value: 28 },
682
]
683
},
684
{
685
content: ' line");',
686
minColumn: 13,
687
maxColumn: 20,
688
tokens: [
689
{ endIndex: 17, value: 28 },
690
{ endIndex: 19, value: 29 },
691
]
692
},
693
{
694
content: ' }',
695
minColumn: 1,
696
maxColumn: 3,
697
tokens: [
698
{ endIndex: 2, value: 30 },
699
]
700
},
701
{
702
content: '}',
703
minColumn: 1,
704
maxColumn: 2,
705
tokens: [
706
{ endIndex: 1, value: 31 },
707
]
708
}
709
];
710
711
assertAllMinimapLinesRenderingData(splitLinesCollection, [
712
_expected[0],
713
_expected[1],
714
_expected[2],
715
_expected[3],
716
_expected[4],
717
_expected[5],
718
_expected[6],
719
_expected[7],
720
_expected[8],
721
_expected[9],
722
_expected[10],
723
_expected[11],
724
]);
725
726
splitLinesCollection.setHiddenAreas([new Range(2, 1, 4, 1)]);
727
assert.strictEqual(splitLinesCollection.getViewLineCount(), 8);
728
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(1, 1), true);
729
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(2, 1), false);
730
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(3, 1), false);
731
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(4, 1), false);
732
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(5, 1), true);
733
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(6, 1), true);
734
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(7, 1), true);
735
assert.strictEqual(splitLinesCollection.modelPositionIsVisible(8, 1), true);
736
737
assertAllMinimapLinesRenderingData(splitLinesCollection, [
738
_expected[0],
739
_expected[5],
740
_expected[6],
741
_expected[7],
742
_expected[8],
743
_expected[9],
744
_expected[10],
745
_expected[11],
746
]);
747
});
748
});
749
750
test('getViewLinesData - with wrapping and injected text', () => {
751
model.deltaDecorations([], [{
752
range: new Range(1, 9, 1, 9),
753
options: {
754
description: 'example',
755
after: {
756
content: 'very very long injected text that causes a line break',
757
inlineClassName: 'myClassName'
758
},
759
showIfCollapsed: true,
760
}
761
}]);
762
763
withSplitLinesCollection(model, 'wordWrapColumn', 30, false, (splitLinesCollection) => {
764
assert.strictEqual(splitLinesCollection.getViewLineCount(), 14);
765
766
assert.strictEqual(splitLinesCollection.getViewLineMaxColumn(1), 24);
767
768
const _expected: ITestMinimapLineRenderingData[] = [
769
{
770
content: 'class Nivery very long ',
771
minColumn: 1,
772
maxColumn: 24,
773
tokens: [
774
{ endIndex: 5, value: 1 },
775
{ endIndex: 6, value: 2 },
776
{ endIndex: 8, value: 3 },
777
{ endIndex: 23, value: 1 },
778
]
779
},
780
{
781
content: ' injected text that causes ',
782
minColumn: 5,
783
maxColumn: 31,
784
tokens: [{ endIndex: 30, value: 1 }]
785
},
786
{
787
content: ' a line breakce {',
788
minColumn: 5,
789
maxColumn: 21,
790
tokens: [
791
{ endIndex: 16, value: 1 },
792
{ endIndex: 18, value: 3 },
793
{ endIndex: 20, value: 4 }
794
]
795
},
796
{
797
content: ' function hi() {',
798
minColumn: 1,
799
maxColumn: 17,
800
tokens: [
801
{ endIndex: 1, value: 5 },
802
{ endIndex: 9, value: 6 },
803
{ endIndex: 10, value: 7 },
804
{ endIndex: 12, value: 8 },
805
{ endIndex: 16, value: 9 },
806
]
807
},
808
{
809
content: ' console.log("Hello ',
810
minColumn: 1,
811
maxColumn: 22,
812
tokens: [
813
{ endIndex: 2, value: 10 },
814
{ endIndex: 9, value: 11 },
815
{ endIndex: 10, value: 12 },
816
{ endIndex: 13, value: 13 },
817
{ endIndex: 14, value: 14 },
818
{ endIndex: 21, value: 15 },
819
]
820
},
821
{
822
content: ' world");',
823
minColumn: 13,
824
maxColumn: 21,
825
tokens: [
826
{ endIndex: 18, value: 15 },
827
{ endIndex: 20, value: 16 },
828
]
829
},
830
{
831
content: ' }',
832
minColumn: 1,
833
maxColumn: 3,
834
tokens: [
835
{ endIndex: 2, value: 17 },
836
]
837
},
838
{
839
content: ' function hello() {',
840
minColumn: 1,
841
maxColumn: 20,
842
tokens: [
843
{ endIndex: 1, value: 18 },
844
{ endIndex: 9, value: 19 },
845
{ endIndex: 10, value: 20 },
846
{ endIndex: 15, value: 21 },
847
{ endIndex: 19, value: 22 },
848
]
849
},
850
{
851
content: ' console.log("Hello ',
852
minColumn: 1,
853
maxColumn: 22,
854
tokens: [
855
{ endIndex: 2, value: 23 },
856
{ endIndex: 9, value: 24 },
857
{ endIndex: 10, value: 25 },
858
{ endIndex: 13, value: 26 },
859
{ endIndex: 14, value: 27 },
860
{ endIndex: 21, value: 28 },
861
]
862
},
863
{
864
content: ' world, this is a ',
865
minColumn: 13,
866
maxColumn: 30,
867
tokens: [
868
{ endIndex: 29, value: 28 },
869
]
870
},
871
{
872
content: ' somewhat longer ',
873
minColumn: 13,
874
maxColumn: 29,
875
tokens: [
876
{ endIndex: 28, value: 28 },
877
]
878
},
879
{
880
content: ' line");',
881
minColumn: 13,
882
maxColumn: 20,
883
tokens: [
884
{ endIndex: 17, value: 28 },
885
{ endIndex: 19, value: 29 },
886
]
887
},
888
{
889
content: ' }',
890
minColumn: 1,
891
maxColumn: 3,
892
tokens: [
893
{ endIndex: 2, value: 30 },
894
]
895
},
896
{
897
content: '}',
898
minColumn: 1,
899
maxColumn: 2,
900
tokens: [
901
{ endIndex: 1, value: 31 },
902
]
903
}
904
];
905
906
assertAllMinimapLinesRenderingData(splitLinesCollection, [
907
_expected[0],
908
_expected[1],
909
_expected[2],
910
_expected[3],
911
_expected[4],
912
_expected[5],
913
_expected[6],
914
_expected[7],
915
_expected[8],
916
_expected[9],
917
_expected[10],
918
_expected[11],
919
]);
920
921
const data = splitLinesCollection.getViewLinesData(1, 14, new Array(14).fill(true));
922
assert.deepStrictEqual(
923
data.map((d) => ({
924
inlineDecorations: d.inlineDecorations?.map((d) => ({
925
startOffset: d.startOffset,
926
endOffset: d.endOffset,
927
})),
928
})),
929
[
930
{ inlineDecorations: [{ startOffset: 8, endOffset: 23 }] },
931
{ inlineDecorations: [{ startOffset: 4, endOffset: 30 }] },
932
{ inlineDecorations: [{ startOffset: 4, endOffset: 16 }] },
933
{ inlineDecorations: undefined },
934
{ inlineDecorations: undefined },
935
{ inlineDecorations: undefined },
936
{ inlineDecorations: undefined },
937
{ inlineDecorations: undefined },
938
{ inlineDecorations: undefined },
939
{ inlineDecorations: undefined },
940
{ inlineDecorations: undefined },
941
{ inlineDecorations: undefined },
942
{ inlineDecorations: undefined },
943
{ inlineDecorations: undefined },
944
]
945
);
946
});
947
});
948
949
function withSplitLinesCollection(model: TextModel, wordWrap: 'on' | 'off' | 'wordWrapColumn' | 'bounded', wordWrapColumn: number, wrapOnEscapedLineFeeds: boolean, callback: (splitLinesCollection: ViewModelLinesFromProjectedModel) => void): void {
950
const configuration = new TestConfiguration({
951
wordWrap: wordWrap,
952
wordWrapColumn: wordWrapColumn,
953
wrappingIndent: 'indent'
954
});
955
const wrappingInfo = configuration.options.get(EditorOption.wrappingInfo);
956
const fontInfo = configuration.options.get(EditorOption.fontInfo);
957
const wordWrapBreakAfterCharacters = configuration.options.get(EditorOption.wordWrapBreakAfterCharacters);
958
const wordWrapBreakBeforeCharacters = configuration.options.get(EditorOption.wordWrapBreakBeforeCharacters);
959
const wrappingIndent = configuration.options.get(EditorOption.wrappingIndent);
960
const wordBreak = configuration.options.get(EditorOption.wordBreak);
961
962
const lineBreaksComputerFactory = new MonospaceLineBreaksComputerFactory(wordWrapBreakBeforeCharacters, wordWrapBreakAfterCharacters);
963
964
const linesCollection = new ViewModelLinesFromProjectedModel(
965
1,
966
model,
967
lineBreaksComputerFactory,
968
lineBreaksComputerFactory,
969
fontInfo,
970
model.getOptions().tabSize,
971
'simple',
972
wrappingInfo.wrappingColumn,
973
wrappingIndent,
974
wordBreak,
975
wrapOnEscapedLineFeeds
976
);
977
978
callback(linesCollection);
979
980
configuration.dispose();
981
}
982
});
983
984
985
function pos(lineNumber: number, column: number): Position {
986
return new Position(lineNumber, column);
987
}
988
989
function createSplitLine(splitLengths: number[], breakingOffsetsVisibleColumn: number[], wrappedTextIndentWidth: number, isVisible: boolean = true): IModelLineProjection {
990
return createModelLineProjection(createLineBreakData(splitLengths, breakingOffsetsVisibleColumn, wrappedTextIndentWidth), isVisible);
991
}
992
993
function createLineBreakData(breakingLengths: number[], breakingOffsetsVisibleColumn: number[], wrappedTextIndentWidth: number): ModelLineProjectionData {
994
const sums: number[] = [];
995
for (let i = 0; i < breakingLengths.length; i++) {
996
sums[i] = (i > 0 ? sums[i - 1] : 0) + breakingLengths[i];
997
}
998
return new ModelLineProjectionData(null, null, sums, breakingOffsetsVisibleColumn, wrappedTextIndentWidth);
999
}
1000
1001
function createModel(text: string): ISimpleModel {
1002
return {
1003
tokenization: {
1004
getLineTokens: (lineNumber: number) => {
1005
return null!;
1006
},
1007
},
1008
getLineContent: (lineNumber: number) => {
1009
return text;
1010
},
1011
getLineLength: (lineNumber: number) => {
1012
return text.length;
1013
},
1014
getLineMinColumn: (lineNumber: number) => {
1015
return 1;
1016
},
1017
getLineMaxColumn: (lineNumber: number) => {
1018
return text.length + 1;
1019
},
1020
getValueInRange: (range: IRange, eol?: EndOfLinePreference) => {
1021
return text.substring(range.startColumn - 1, range.endColumn - 1);
1022
}
1023
};
1024
}
1025
1026