Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/browser/viewModel/viewModelImpl.test.ts
5242 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 { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
8
import { Position } from '../../../common/core/position.js';
9
import { Range } from '../../../common/core/range.js';
10
import { EndOfLineSequence, PositionAffinity } from '../../../common/model.js';
11
import { ViewEventHandler } from '../../../common/viewEventHandler.js';
12
import { ViewEvent } from '../../../common/viewEvents.js';
13
import { testViewModel } from './testViewModel.js';
14
import { DisposableStore } from '../../../../base/common/lifecycle.js';
15
import { createTextModel } from '../../common/testTextModel.js';
16
import { createCodeEditorServices, instantiateTestCodeEditor } from '../testCodeEditor.js';
17
18
suite('ViewModel', () => {
19
20
ensureNoDisposablesAreLeakedInTestSuite();
21
22
test('issue #21073: SplitLinesCollection: attempt to access a \'newer\' model', () => {
23
const text = [''];
24
const opts = {
25
lineNumbersMinChars: 1
26
};
27
testViewModel(text, opts, (viewModel, model) => {
28
assert.strictEqual(viewModel.getLineCount(), 1);
29
30
viewModel.setViewport(1, 1, 1);
31
32
model.applyEdits([{
33
range: new Range(1, 1, 1, 1),
34
text: [
35
'line01',
36
'line02',
37
'line03',
38
'line04',
39
'line05',
40
'line06',
41
'line07',
42
'line08',
43
'line09',
44
'line10',
45
].join('\n')
46
}]);
47
48
assert.strictEqual(viewModel.getLineCount(), 10);
49
});
50
});
51
52
test('issue #44805: SplitLinesCollection: attempt to access a \'newer\' model', () => {
53
const text = [''];
54
testViewModel(text, {}, (viewModel, model) => {
55
assert.strictEqual(viewModel.getLineCount(), 1);
56
57
model.pushEditOperations([], [{
58
range: new Range(1, 1, 1, 1),
59
text: '\ninsert1'
60
}], () => ([]));
61
62
model.pushEditOperations([], [{
63
range: new Range(1, 1, 1, 1),
64
text: '\ninsert2'
65
}], () => ([]));
66
67
model.pushEditOperations([], [{
68
range: new Range(1, 1, 1, 1),
69
text: '\ninsert3'
70
}], () => ([]));
71
72
const viewLineCount: number[] = [];
73
74
viewLineCount.push(viewModel.getLineCount());
75
const eventHandler = new class extends ViewEventHandler {
76
override handleEvents(events: ViewEvent[]): void {
77
// Access the view model
78
viewLineCount.push(viewModel.getLineCount());
79
}
80
};
81
viewModel.addViewEventHandler(eventHandler);
82
model.undo();
83
viewLineCount.push(viewModel.getLineCount());
84
85
assert.deepStrictEqual(viewLineCount, [4, 1, 1, 1, 1]);
86
87
viewModel.removeViewEventHandler(eventHandler);
88
eventHandler.dispose();
89
});
90
});
91
92
test('view models react first to model changes', () => {
93
const initialText = [
94
'Hello',
95
'world'
96
];
97
const disposables = new DisposableStore();
98
99
const model = disposables.add(createTextModel(initialText.join('\n')));
100
const instantiationService = createCodeEditorServices(disposables);
101
const ed1 = disposables.add(instantiateTestCodeEditor(instantiationService, model));
102
disposables.add(instantiateTestCodeEditor(instantiationService, model));
103
104
// Add a nasty listener which modifies the model during the model change event
105
let isFirst = true;
106
disposables.add(ed1.onDidChangeModelContent((e) => {
107
if (isFirst) {
108
isFirst = false;
109
// delete the \n
110
model.applyEdits([{ range: new Range(1, 6, 2, 1), text: '' }]);
111
}
112
}));
113
114
model.applyEdits([{ range: new Range(2, 6, 2, 6), text: '!' }]);
115
116
disposables.dispose();
117
});
118
119
test('issue #44805: No visible lines via API call', () => {
120
const text = [
121
'line1',
122
'line2',
123
'line3'
124
];
125
testViewModel(text, {}, (viewModel, model) => {
126
assert.strictEqual(viewModel.getLineCount(), 3);
127
viewModel.setHiddenAreas([new Range(1, 1, 3, 1)]);
128
assert.ok(viewModel.getVisibleRanges() !== null);
129
});
130
});
131
132
test('issue #44805: No visible lines via undoing', () => {
133
const text = [
134
''
135
];
136
testViewModel(text, {}, (viewModel, model) => {
137
assert.strictEqual(viewModel.getLineCount(), 1);
138
139
model.pushEditOperations([], [{
140
range: new Range(1, 1, 1, 1),
141
text: 'line1\nline2\nline3'
142
}], () => ([]));
143
144
viewModel.setHiddenAreas([new Range(1, 1, 1, 1)]);
145
assert.strictEqual(viewModel.getLineCount(), 2);
146
147
model.undo();
148
assert.ok(viewModel.getVisibleRanges() !== null);
149
});
150
});
151
152
function assertGetPlainTextToCopy(text: string[], ranges: Range[], emptySelectionClipboard: boolean, expected: string | string[]): void {
153
testViewModel(text, {}, (viewModel, model) => {
154
const actual = viewModel.getPlainTextToCopy(ranges, emptySelectionClipboard, false);
155
assert.deepStrictEqual(actual.sourceText, expected);
156
});
157
}
158
159
const USUAL_TEXT = [
160
'',
161
'line2',
162
'line3',
163
'line4',
164
''
165
];
166
167
test('getPlainTextToCopy 0/1', () => {
168
assertGetPlainTextToCopy(
169
USUAL_TEXT,
170
[
171
new Range(2, 2, 2, 2)
172
],
173
false,
174
''
175
);
176
});
177
178
test('getPlainTextToCopy 0/1 - emptySelectionClipboard', () => {
179
assertGetPlainTextToCopy(
180
USUAL_TEXT,
181
[
182
new Range(2, 2, 2, 2)
183
],
184
true,
185
'line2\n'
186
);
187
});
188
189
test('getPlainTextToCopy 1/1', () => {
190
assertGetPlainTextToCopy(
191
USUAL_TEXT,
192
[
193
new Range(2, 2, 2, 6)
194
],
195
false,
196
'ine2'
197
);
198
});
199
200
test('getPlainTextToCopy 1/1 - emptySelectionClipboard', () => {
201
assertGetPlainTextToCopy(
202
USUAL_TEXT,
203
[
204
new Range(2, 2, 2, 6)
205
],
206
true,
207
'ine2'
208
);
209
});
210
211
test('getPlainTextToCopy 0/2', () => {
212
assertGetPlainTextToCopy(
213
USUAL_TEXT,
214
[
215
new Range(2, 2, 2, 2),
216
new Range(3, 2, 3, 2),
217
],
218
false,
219
''
220
);
221
});
222
223
test('getPlainTextToCopy 0/2 - emptySelectionClipboard', () => {
224
assertGetPlainTextToCopy(
225
USUAL_TEXT,
226
[
227
new Range(2, 2, 2, 2),
228
new Range(3, 2, 3, 2),
229
],
230
true,
231
[
232
'line2\n',
233
'line3\n'
234
]
235
);
236
});
237
238
test('issue #256039: getPlainTextToCopy with multiple cursors and empty selections should return array', () => {
239
// Bug: When copying with multiple cursors (empty selections) with emptySelectionClipboard enabled,
240
// the result should be an array so that pasting with "editor.multiCursorPaste": "full"
241
// correctly distributes each line to the corresponding cursor.
242
// Without the fix, this returns 'line2\nline3\n' (a single string).
243
// With the fix, this returns ['line2\n', 'line3\n'] (an array).
244
assertGetPlainTextToCopy(
245
USUAL_TEXT,
246
[
247
new Range(2, 1, 2, 1),
248
new Range(3, 1, 3, 1),
249
],
250
true,
251
['line2\n', 'line3\n']
252
);
253
});
254
255
test('getPlainTextToCopy 1/2', () => {
256
assertGetPlainTextToCopy(
257
USUAL_TEXT,
258
[
259
new Range(2, 2, 2, 6),
260
new Range(3, 2, 3, 2),
261
],
262
false,
263
'ine2'
264
);
265
});
266
267
test('getPlainTextToCopy 1/2 - emptySelectionClipboard', () => {
268
assertGetPlainTextToCopy(
269
USUAL_TEXT,
270
[
271
new Range(2, 2, 2, 6),
272
new Range(3, 2, 3, 2),
273
],
274
true,
275
['ine2', 'line3\n']
276
);
277
});
278
279
test('getPlainTextToCopy 2/2', () => {
280
assertGetPlainTextToCopy(
281
USUAL_TEXT,
282
[
283
new Range(2, 2, 2, 6),
284
new Range(3, 2, 3, 6),
285
],
286
false,
287
['ine2', 'ine3']
288
);
289
});
290
291
test('getPlainTextToCopy 2/2 reversed', () => {
292
assertGetPlainTextToCopy(
293
USUAL_TEXT,
294
[
295
new Range(3, 2, 3, 6),
296
new Range(2, 2, 2, 6),
297
],
298
false,
299
['ine2', 'ine3']
300
);
301
});
302
303
test('getPlainTextToCopy 0/3 - emptySelectionClipboard', () => {
304
assertGetPlainTextToCopy(
305
USUAL_TEXT,
306
[
307
new Range(2, 2, 2, 2),
308
new Range(2, 3, 2, 3),
309
new Range(3, 2, 3, 2),
310
],
311
true,
312
[
313
'line2\n',
314
'line3\n'
315
]
316
);
317
});
318
319
test('issue #22688 - always use CRLF for clipboard on Windows', () => {
320
testViewModel(USUAL_TEXT, {}, (viewModel, model) => {
321
model.setEOL(EndOfLineSequence.LF);
322
const actual = viewModel.getPlainTextToCopy([new Range(2, 1, 5, 1)], true, true);
323
assert.deepStrictEqual(actual.sourceText, 'line2\r\nline3\r\nline4\r\n');
324
});
325
});
326
327
test('issue #40926: Incorrect spacing when inserting new line after multiple folded blocks of code', () => {
328
testViewModel(
329
[
330
'foo = {',
331
' foobar: function() {',
332
' this.foobar();',
333
' },',
334
' foobar: function() {',
335
' this.foobar();',
336
' },',
337
' foobar: function() {',
338
' this.foobar();',
339
' },',
340
'}',
341
], {}, (viewModel, model) => {
342
viewModel.setHiddenAreas([
343
new Range(3, 1, 3, 1),
344
new Range(6, 1, 6, 1),
345
new Range(9, 1, 9, 1),
346
]);
347
348
model.applyEdits([
349
{ range: new Range(4, 7, 4, 7), text: '\n ' },
350
{ range: new Range(7, 7, 7, 7), text: '\n ' },
351
{ range: new Range(10, 7, 10, 7), text: '\n ' }
352
]);
353
354
assert.strictEqual(viewModel.getLineCount(), 11);
355
}
356
);
357
});
358
359
test('normalizePosition with multiple touching injected text', () => {
360
testViewModel(
361
[
362
'just some text'
363
],
364
{},
365
(viewModel, model) => {
366
model.deltaDecorations([], [
367
{
368
range: new Range(1, 8, 1, 8),
369
options: {
370
description: 'test',
371
before: {
372
content: 'bar'
373
},
374
showIfCollapsed: true
375
}
376
},
377
{
378
range: new Range(1, 8, 1, 8),
379
options: {
380
description: 'test',
381
before: {
382
content: 'bz'
383
},
384
showIfCollapsed: true
385
}
386
},
387
]);
388
389
// just sobarbzme text
390
391
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 8), PositionAffinity.None), new Position(1, 8));
392
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 9), PositionAffinity.None), new Position(1, 8));
393
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 11), PositionAffinity.None), new Position(1, 11));
394
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 12), PositionAffinity.None), new Position(1, 11));
395
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 13), PositionAffinity.None), new Position(1, 13));
396
397
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 8), PositionAffinity.Left), new Position(1, 8));
398
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 9), PositionAffinity.Left), new Position(1, 8));
399
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 11), PositionAffinity.Left), new Position(1, 8));
400
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 12), PositionAffinity.Left), new Position(1, 8));
401
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 13), PositionAffinity.Left), new Position(1, 8));
402
403
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 8), PositionAffinity.Right), new Position(1, 13));
404
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 9), PositionAffinity.Right), new Position(1, 13));
405
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 11), PositionAffinity.Right), new Position(1, 13));
406
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 12), PositionAffinity.Right), new Position(1, 13));
407
assert.deepStrictEqual(viewModel.normalizePosition(new Position(1, 13), PositionAffinity.Right), new Position(1, 13));
408
}
409
);
410
});
411
412
test('issue #193262: Incorrect implementation of modifyPosition', () => {
413
testViewModel(
414
[
415
'just some text'
416
],
417
{
418
wordWrap: 'wordWrapColumn',
419
wordWrapColumn: 5
420
},
421
(viewModel, model) => {
422
assert.deepStrictEqual(
423
new Position(3, 1),
424
viewModel.modifyPosition(new Position(3, 2), -1)
425
);
426
}
427
);
428
});
429
});
430
431