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