Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/common/model/linesTextBuffer/linesTextBuffer.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 { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
8
import { Range } from '../../../../common/core/range.js';
9
import { DefaultEndOfLine } from '../../../../common/model.js';
10
import { IValidatedEditOperation, PieceTreeTextBuffer } from '../../../../common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.js';
11
import { createTextBufferFactory } from '../../../../common/model/textModel.js';
12
13
suite('PieceTreeTextBuffer._getInverseEdits', () => {
14
15
ensureNoDisposablesAreLeakedInTestSuite();
16
17
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[] | null): IValidatedEditOperation {
18
return {
19
sortIndex: 0,
20
identifier: null,
21
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
22
rangeOffset: 0,
23
rangeLength: 0,
24
text: text ? text.join('\n') : '',
25
eolCount: text ? text.length - 1 : 0,
26
firstLineLength: text ? text[0].length : 0,
27
lastLineLength: text ? text[text.length - 1].length : 0,
28
forceMoveMarkers: false,
29
isAutoWhitespaceEdit: false
30
};
31
}
32
33
function inverseEditOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number): Range {
34
return new Range(startLineNumber, startColumn, endLineNumber, endColumn);
35
}
36
37
function assertInverseEdits(ops: IValidatedEditOperation[], expected: Range[]): void {
38
const actual = PieceTreeTextBuffer._getInverseEditRanges(ops);
39
assert.deepStrictEqual(actual, expected);
40
}
41
42
test('single insert', () => {
43
assertInverseEdits(
44
[
45
editOp(1, 1, 1, 1, ['hello'])
46
],
47
[
48
inverseEditOp(1, 1, 1, 6)
49
]
50
);
51
});
52
53
test('Bug 19872: Undo is funky', () => {
54
assertInverseEdits(
55
[
56
editOp(2, 1, 2, 2, ['']),
57
editOp(3, 1, 4, 2, [''])
58
],
59
[
60
inverseEditOp(2, 1, 2, 1),
61
inverseEditOp(3, 1, 3, 1)
62
]
63
);
64
});
65
66
test('two single unrelated inserts', () => {
67
assertInverseEdits(
68
[
69
editOp(1, 1, 1, 1, ['hello']),
70
editOp(2, 1, 2, 1, ['world'])
71
],
72
[
73
inverseEditOp(1, 1, 1, 6),
74
inverseEditOp(2, 1, 2, 6)
75
]
76
);
77
});
78
79
test('two single inserts 1', () => {
80
assertInverseEdits(
81
[
82
editOp(1, 1, 1, 1, ['hello']),
83
editOp(1, 2, 1, 2, ['world'])
84
],
85
[
86
inverseEditOp(1, 1, 1, 6),
87
inverseEditOp(1, 7, 1, 12)
88
]
89
);
90
});
91
92
test('two single inserts 2', () => {
93
assertInverseEdits(
94
[
95
editOp(1, 1, 1, 1, ['hello']),
96
editOp(1, 4, 1, 4, ['world'])
97
],
98
[
99
inverseEditOp(1, 1, 1, 6),
100
inverseEditOp(1, 9, 1, 14)
101
]
102
);
103
});
104
105
test('multiline insert', () => {
106
assertInverseEdits(
107
[
108
editOp(1, 1, 1, 1, ['hello', 'world'])
109
],
110
[
111
inverseEditOp(1, 1, 2, 6)
112
]
113
);
114
});
115
116
test('two unrelated multiline inserts', () => {
117
assertInverseEdits(
118
[
119
editOp(1, 1, 1, 1, ['hello', 'world']),
120
editOp(2, 1, 2, 1, ['how', 'are', 'you?']),
121
],
122
[
123
inverseEditOp(1, 1, 2, 6),
124
inverseEditOp(3, 1, 5, 5),
125
]
126
);
127
});
128
129
test('two multiline inserts 1', () => {
130
assertInverseEdits(
131
[
132
editOp(1, 1, 1, 1, ['hello', 'world']),
133
editOp(1, 2, 1, 2, ['how', 'are', 'you?']),
134
],
135
[
136
inverseEditOp(1, 1, 2, 6),
137
inverseEditOp(2, 7, 4, 5),
138
]
139
);
140
});
141
142
test('single delete', () => {
143
assertInverseEdits(
144
[
145
editOp(1, 1, 1, 6, null)
146
],
147
[
148
inverseEditOp(1, 1, 1, 1)
149
]
150
);
151
});
152
153
test('two single unrelated deletes', () => {
154
assertInverseEdits(
155
[
156
editOp(1, 1, 1, 6, null),
157
editOp(2, 1, 2, 6, null)
158
],
159
[
160
inverseEditOp(1, 1, 1, 1),
161
inverseEditOp(2, 1, 2, 1)
162
]
163
);
164
});
165
166
test('two single deletes 1', () => {
167
assertInverseEdits(
168
[
169
editOp(1, 1, 1, 6, null),
170
editOp(1, 7, 1, 12, null)
171
],
172
[
173
inverseEditOp(1, 1, 1, 1),
174
inverseEditOp(1, 2, 1, 2)
175
]
176
);
177
});
178
179
test('two single deletes 2', () => {
180
assertInverseEdits(
181
[
182
editOp(1, 1, 1, 6, null),
183
editOp(1, 9, 1, 14, null)
184
],
185
[
186
inverseEditOp(1, 1, 1, 1),
187
inverseEditOp(1, 4, 1, 4)
188
]
189
);
190
});
191
192
test('multiline delete', () => {
193
assertInverseEdits(
194
[
195
editOp(1, 1, 2, 6, null)
196
],
197
[
198
inverseEditOp(1, 1, 1, 1)
199
]
200
);
201
});
202
203
test('two unrelated multiline deletes', () => {
204
assertInverseEdits(
205
[
206
editOp(1, 1, 2, 6, null),
207
editOp(3, 1, 5, 5, null),
208
],
209
[
210
inverseEditOp(1, 1, 1, 1),
211
inverseEditOp(2, 1, 2, 1),
212
]
213
);
214
});
215
216
test('two multiline deletes 1', () => {
217
assertInverseEdits(
218
[
219
editOp(1, 1, 2, 6, null),
220
editOp(2, 7, 4, 5, null),
221
],
222
[
223
inverseEditOp(1, 1, 1, 1),
224
inverseEditOp(1, 2, 1, 2),
225
]
226
);
227
});
228
229
test('single replace', () => {
230
assertInverseEdits(
231
[
232
editOp(1, 1, 1, 6, ['Hello world'])
233
],
234
[
235
inverseEditOp(1, 1, 1, 12)
236
]
237
);
238
});
239
240
test('two replaces', () => {
241
assertInverseEdits(
242
[
243
editOp(1, 1, 1, 6, ['Hello world']),
244
editOp(1, 7, 1, 8, ['How are you?']),
245
],
246
[
247
inverseEditOp(1, 1, 1, 12),
248
inverseEditOp(1, 13, 1, 25)
249
]
250
);
251
});
252
253
test('many edits', () => {
254
assertInverseEdits(
255
[
256
editOp(1, 2, 1, 2, ['', ' ']),
257
editOp(1, 5, 1, 6, ['']),
258
editOp(1, 9, 1, 9, ['', ''])
259
],
260
[
261
inverseEditOp(1, 2, 2, 3),
262
inverseEditOp(2, 6, 2, 6),
263
inverseEditOp(2, 9, 3, 1)
264
]
265
);
266
});
267
});
268
269
suite('PieceTreeTextBuffer._toSingleEditOperation', () => {
270
271
ensureNoDisposablesAreLeakedInTestSuite();
272
273
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, rangeOffset: number, rangeLength: number, text: string[] | null): IValidatedEditOperation {
274
return {
275
sortIndex: 0,
276
identifier: null,
277
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
278
rangeOffset: rangeOffset,
279
rangeLength: rangeLength,
280
text: text ? text.join('\n') : '',
281
eolCount: text ? text.length - 1 : 0,
282
firstLineLength: text ? text[0].length : 0,
283
lastLineLength: text ? text[text.length - 1].length : 0,
284
forceMoveMarkers: false,
285
isAutoWhitespaceEdit: false
286
};
287
}
288
289
function testToSingleEditOperation(original: string[], edits: IValidatedEditOperation[], expected: IValidatedEditOperation): void {
290
const { disposable, textBuffer } = createTextBufferFactory(original.join('\n')).create(DefaultEndOfLine.LF);
291
292
const actual = (<PieceTreeTextBuffer>textBuffer)._toSingleEditOperation(edits);
293
assert.deepStrictEqual(actual, expected);
294
disposable.dispose();
295
}
296
297
test('one edit op is unchanged', () => {
298
testToSingleEditOperation(
299
[
300
'My First Line',
301
'\t\tMy Second Line',
302
' Third Line',
303
'',
304
'1'
305
],
306
[
307
editOp(1, 3, 1, 3, 2, 0, [' new line', 'No longer'])
308
],
309
editOp(1, 3, 1, 3, 2, 0, [' new line', 'No longer'])
310
);
311
});
312
313
test('two edits on one line', () => {
314
testToSingleEditOperation([
315
'My First Line',
316
'\t\tMy Second Line',
317
' Third Line',
318
'',
319
'1'
320
], [
321
editOp(1, 1, 1, 3, 0, 2, ['Your']),
322
editOp(1, 4, 1, 4, 3, 0, ['Interesting ']),
323
editOp(2, 3, 2, 6, 16, 3, null)
324
],
325
editOp(1, 1, 2, 6, 0, 19, [
326
'Your Interesting First Line',
327
'\t\t'
328
]));
329
});
330
331
test('insert multiple newlines', () => {
332
testToSingleEditOperation(
333
[
334
'My First Line',
335
'\t\tMy Second Line',
336
' Third Line',
337
'',
338
'1'
339
],
340
[
341
editOp(1, 3, 1, 3, 2, 0, ['', '', '', '', '']),
342
editOp(3, 15, 3, 15, 45, 0, ['a', 'b'])
343
],
344
editOp(1, 3, 3, 15, 2, 43, [
345
'',
346
'',
347
'',
348
'',
349
' First Line',
350
'\t\tMy Second Line',
351
' Third Linea',
352
'b'
353
])
354
);
355
});
356
357
test('delete empty text', () => {
358
testToSingleEditOperation(
359
[
360
'My First Line',
361
'\t\tMy Second Line',
362
' Third Line',
363
'',
364
'1'
365
],
366
[
367
editOp(1, 1, 1, 1, 0, 0, [''])
368
],
369
editOp(1, 1, 1, 1, 0, 0, [''])
370
);
371
});
372
373
test('two unrelated edits', () => {
374
testToSingleEditOperation(
375
[
376
'My First Line',
377
'\t\tMy Second Line',
378
' Third Line',
379
'',
380
'123'
381
],
382
[
383
editOp(2, 1, 2, 3, 14, 2, ['\t']),
384
editOp(3, 1, 3, 5, 31, 4, [''])
385
],
386
editOp(2, 1, 3, 5, 14, 21, ['\tMy Second Line', ''])
387
);
388
});
389
390
test('many edits', () => {
391
testToSingleEditOperation(
392
[
393
'{"x" : 1}'
394
],
395
[
396
editOp(1, 2, 1, 2, 1, 0, ['\n ']),
397
editOp(1, 5, 1, 6, 4, 1, ['']),
398
editOp(1, 9, 1, 9, 8, 0, ['\n'])
399
],
400
editOp(1, 2, 1, 9, 1, 7, [
401
'',
402
' "x": 1',
403
''
404
])
405
);
406
});
407
408
test('many edits reversed', () => {
409
testToSingleEditOperation(
410
[
411
'{',
412
' "x": 1',
413
'}'
414
],
415
[
416
editOp(1, 2, 2, 3, 1, 3, ['']),
417
editOp(2, 6, 2, 6, 7, 0, [' ']),
418
editOp(2, 9, 3, 1, 10, 1, [''])
419
],
420
editOp(1, 2, 3, 1, 1, 10, ['"x" : 1'])
421
);
422
});
423
424
test('replacing newlines 1', () => {
425
testToSingleEditOperation(
426
[
427
'{',
428
'"a": true,',
429
'',
430
'"b": true',
431
'}'
432
],
433
[
434
editOp(1, 2, 2, 1, 1, 1, ['', '\t']),
435
editOp(2, 11, 4, 1, 12, 2, ['', '\t'])
436
],
437
editOp(1, 2, 4, 1, 1, 13, [
438
'',
439
'\t"a": true,',
440
'\t'
441
])
442
);
443
});
444
445
test('replacing newlines 2', () => {
446
testToSingleEditOperation(
447
[
448
'some text',
449
'some more text',
450
'now comes an empty line',
451
'',
452
'after empty line',
453
'and the last line'
454
],
455
[
456
editOp(1, 5, 3, 1, 4, 21, [' text', 'some more text', 'some more text']),
457
editOp(3, 2, 4, 1, 26, 23, ['o more lines', 'asd', 'asd', 'asd']),
458
editOp(5, 1, 5, 6, 50, 5, ['zzzzzzzz']),
459
editOp(5, 11, 6, 16, 60, 22, ['1', '2', '3', '4'])
460
],
461
editOp(1, 5, 6, 16, 4, 78, [
462
' text',
463
'some more text',
464
'some more textno more lines',
465
'asd',
466
'asd',
467
'asd',
468
'zzzzzzzz empt1',
469
'2',
470
'3',
471
'4'
472
])
473
);
474
});
475
476
test('advanced', () => {
477
testToSingleEditOperation(
478
[
479
' { "d": [',
480
' null',
481
' ] /*comment*/',
482
' ,"e": /*comment*/ [null] }',
483
],
484
[
485
editOp(1, 1, 1, 2, 0, 1, ['']),
486
editOp(1, 3, 1, 10, 2, 7, ['', ' ']),
487
editOp(1, 16, 2, 14, 15, 14, ['', ' ']),
488
editOp(2, 18, 3, 9, 33, 9, ['', ' ']),
489
editOp(3, 22, 4, 9, 55, 9, ['']),
490
editOp(4, 10, 4, 10, 65, 0, ['', ' ']),
491
editOp(4, 28, 4, 28, 83, 0, ['', ' ']),
492
editOp(4, 32, 4, 32, 87, 0, ['', ' ']),
493
editOp(4, 33, 4, 34, 88, 1, ['', ''])
494
],
495
editOp(1, 1, 4, 34, 0, 89, [
496
'{',
497
' "d": [',
498
' null',
499
' ] /*comment*/,',
500
' "e": /*comment*/ [',
501
' null',
502
' ]',
503
''
504
])
505
);
506
});
507
508
test('advanced simplified', () => {
509
testToSingleEditOperation(
510
[
511
' abc',
512
' ,def'
513
],
514
[
515
editOp(1, 1, 1, 4, 0, 3, ['']),
516
editOp(1, 7, 2, 2, 6, 2, ['']),
517
editOp(2, 3, 2, 3, 9, 0, ['', ''])
518
],
519
editOp(1, 1, 2, 3, 0, 9, [
520
'abc,',
521
''
522
])
523
);
524
});
525
});
526
527