Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/inlineCompletions/test/browser/editKind.test.ts
4798 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 { OffsetRange } from '../../../../common/core/ranges/offsetRange.js';
10
import { StringEdit } from '../../../../common/core/edits/stringEdit.js';
11
import { createTextModel } from '../../../../test/common/testTextModel.js';
12
import { computeEditKind, InsertProperties, DeleteProperties, ReplaceProperties } from '../../browser/model/editKind.js';
13
14
suite('computeEditKind', () => {
15
ensureNoDisposablesAreLeakedInTestSuite();
16
17
suite('Insert operations', () => {
18
test('single character insert - syntactical', () => {
19
const model = createTextModel('hello world');
20
const edit = StringEdit.insert(5, ';');
21
const result = computeEditKind(edit, model);
22
23
assert.ok(result);
24
assert.strictEqual(result.edits.length, 1);
25
assert.strictEqual(result.edits[0].operation, 'insert');
26
assert.strictEqual(result.edits[0].charactersInserted, 1);
27
assert.strictEqual(result.edits[0].charactersDeleted, 0);
28
assert.strictEqual(result.edits[0].linesInserted, 0);
29
assert.strictEqual(result.edits[0].linesDeleted, 0);
30
const props = result.edits[0].properties as InsertProperties;
31
assert.strictEqual(props.textShape.kind, 'singleLine');
32
if (props.textShape.kind === 'singleLine') {
33
assert.strictEqual(props.textShape.isSingleCharacter, true);
34
assert.strictEqual(props.textShape.singleCharacterKind, 'syntactical');
35
}
36
model.dispose();
37
});
38
39
test('single character insert - identifier', () => {
40
const model = createTextModel('hello world');
41
const edit = StringEdit.insert(5, 'a');
42
const result = computeEditKind(edit, model);
43
44
assert.ok(result);
45
assert.strictEqual(result.edits.length, 1);
46
assert.strictEqual(result.edits[0].operation, 'insert');
47
const props = result.edits[0].properties as InsertProperties;
48
if (props.textShape.kind === 'singleLine') {
49
assert.strictEqual(props.textShape.isSingleCharacter, true);
50
assert.strictEqual(props.textShape.singleCharacterKind, 'identifier');
51
}
52
model.dispose();
53
});
54
55
test('single character insert - whitespace', () => {
56
const model = createTextModel('hello world');
57
const edit = StringEdit.insert(5, ' ');
58
const result = computeEditKind(edit, model);
59
60
assert.ok(result);
61
assert.strictEqual(result.edits.length, 1);
62
assert.strictEqual(result.edits[0].operation, 'insert');
63
const props = result.edits[0].properties as InsertProperties;
64
if (props.textShape.kind === 'singleLine') {
65
assert.strictEqual(props.textShape.isSingleCharacter, true);
66
assert.strictEqual(props.textShape.singleCharacterKind, 'whitespace');
67
}
68
model.dispose();
69
});
70
71
test('word insert', () => {
72
const model = createTextModel('hello world');
73
const edit = StringEdit.insert(5, 'foo');
74
const result = computeEditKind(edit, model);
75
76
assert.ok(result);
77
assert.strictEqual(result.edits.length, 1);
78
assert.strictEqual(result.edits[0].operation, 'insert');
79
const props = result.edits[0].properties as InsertProperties;
80
if (props.textShape.kind === 'singleLine') {
81
assert.strictEqual(props.textShape.isWord, true);
82
assert.strictEqual(props.textShape.isMultipleWords, false);
83
}
84
model.dispose();
85
});
86
87
test('multiple words insert', () => {
88
const model = createTextModel('hello world');
89
const edit = StringEdit.insert(5, 'foo bar baz');
90
const result = computeEditKind(edit, model);
91
92
assert.ok(result);
93
assert.strictEqual(result.edits.length, 1);
94
assert.strictEqual(result.edits[0].operation, 'insert');
95
const props = result.edits[0].properties as InsertProperties;
96
if (props.textShape.kind === 'singleLine') {
97
assert.strictEqual(props.textShape.isMultipleWords, true);
98
}
99
model.dispose();
100
});
101
102
test('multi-line insert', () => {
103
const model = createTextModel('hello world');
104
const edit = StringEdit.insert(5, 'line1\nline2\nline3');
105
const result = computeEditKind(edit, model);
106
107
assert.ok(result);
108
assert.strictEqual(result.edits.length, 1);
109
assert.strictEqual(result.edits[0].operation, 'insert');
110
assert.strictEqual(result.edits[0].charactersInserted, 17);
111
assert.strictEqual(result.edits[0].charactersDeleted, 0);
112
assert.strictEqual(result.edits[0].linesInserted, 2);
113
assert.strictEqual(result.edits[0].linesDeleted, 0);
114
const props = result.edits[0].properties as InsertProperties;
115
assert.strictEqual(props.textShape.kind, 'multiLine');
116
if (props.textShape.kind === 'multiLine') {
117
assert.strictEqual(props.textShape.lineCount, 3);
118
}
119
model.dispose();
120
});
121
122
test('insert at end of line', () => {
123
const model = createTextModel('hello');
124
const edit = StringEdit.insert(5, ' world');
125
const result = computeEditKind(edit, model);
126
127
assert.ok(result);
128
assert.strictEqual(result.edits.length, 1);
129
assert.strictEqual(result.edits[0].operation, 'insert');
130
const props = result.edits[0].properties as InsertProperties;
131
assert.strictEqual(props.locationShape, 'endOfLine');
132
model.dispose();
133
});
134
135
test('insert on empty line', () => {
136
const model = createTextModel('hello\n\nworld');
137
const edit = StringEdit.insert(6, 'text');
138
const result = computeEditKind(edit, model);
139
140
assert.ok(result);
141
assert.strictEqual(result.edits.length, 1);
142
assert.strictEqual(result.edits[0].operation, 'insert');
143
const props = result.edits[0].properties as InsertProperties;
144
assert.strictEqual(props.locationShape, 'emptyLine');
145
model.dispose();
146
});
147
148
test('insert at start of line', () => {
149
const model = createTextModel('hello world');
150
const edit = StringEdit.insert(0, 'prefix');
151
const result = computeEditKind(edit, model);
152
153
assert.ok(result);
154
assert.strictEqual(result.edits.length, 1);
155
assert.strictEqual(result.edits[0].operation, 'insert');
156
const props = result.edits[0].properties as InsertProperties;
157
assert.strictEqual(props.locationShape, 'startOfLine');
158
model.dispose();
159
});
160
161
test('insert in middle of line', () => {
162
const model = createTextModel('hello world');
163
const edit = StringEdit.insert(5, '_');
164
const result = computeEditKind(edit, model);
165
166
assert.ok(result);
167
assert.strictEqual(result.edits.length, 1);
168
assert.strictEqual(result.edits[0].operation, 'insert');
169
const props = result.edits[0].properties as InsertProperties;
170
assert.strictEqual(props.locationShape, 'middleOfLine');
171
model.dispose();
172
});
173
174
test('insert relative to cursor - at cursor', () => {
175
const model = createTextModel('hello world');
176
const edit = StringEdit.insert(5, 'text');
177
const cursor = new Position(1, 6); // column is 1-based
178
const result = computeEditKind(edit, model, cursor);
179
180
assert.ok(result);
181
assert.strictEqual(result.edits.length, 1);
182
assert.strictEqual(result.edits[0].operation, 'insert');
183
const props = result.edits[0].properties as InsertProperties;
184
assert.ok(props.relativeToCursor);
185
assert.strictEqual(props.relativeToCursor.atCursor, true);
186
model.dispose();
187
});
188
189
test('insert relative to cursor - before cursor on same line', () => {
190
const model = createTextModel('hello world');
191
const edit = StringEdit.insert(2, 'text');
192
const cursor = new Position(1, 8);
193
const result = computeEditKind(edit, model, cursor);
194
195
assert.ok(result);
196
assert.strictEqual(result.edits.length, 1);
197
assert.strictEqual(result.edits[0].operation, 'insert');
198
const props = result.edits[0].properties as InsertProperties;
199
assert.ok(props.relativeToCursor);
200
assert.strictEqual(props.relativeToCursor.beforeCursorOnSameLine, true);
201
model.dispose();
202
});
203
204
test('insert relative to cursor - after cursor on same line', () => {
205
const model = createTextModel('hello world');
206
const edit = StringEdit.insert(8, 'text');
207
const cursor = new Position(1, 4);
208
const result = computeEditKind(edit, model, cursor);
209
210
assert.ok(result);
211
assert.strictEqual(result.edits.length, 1);
212
assert.strictEqual(result.edits[0].operation, 'insert');
213
const props = result.edits[0].properties as InsertProperties;
214
assert.ok(props.relativeToCursor);
215
assert.strictEqual(props.relativeToCursor.afterCursorOnSameLine, true);
216
model.dispose();
217
});
218
219
test('insert relative to cursor - lines above', () => {
220
const model = createTextModel('line1\nline2\nline3');
221
const edit = StringEdit.insert(0, 'text');
222
const cursor = new Position(3, 1);
223
const result = computeEditKind(edit, model, cursor);
224
225
assert.ok(result);
226
assert.strictEqual(result.edits.length, 1);
227
assert.strictEqual(result.edits[0].operation, 'insert');
228
const props = result.edits[0].properties as InsertProperties;
229
assert.ok(props.relativeToCursor);
230
assert.strictEqual(props.relativeToCursor.linesAbove, 2);
231
model.dispose();
232
});
233
234
test('insert relative to cursor - lines below', () => {
235
const model = createTextModel('line1\nline2\nline3');
236
const edit = StringEdit.insert(12, 'text'); // after 'line2\n'
237
const cursor = new Position(1, 1);
238
const result = computeEditKind(edit, model, cursor);
239
240
assert.ok(result);
241
assert.strictEqual(result.edits.length, 1);
242
assert.strictEqual(result.edits[0].operation, 'insert');
243
const props = result.edits[0].properties as InsertProperties;
244
assert.ok(props.relativeToCursor);
245
assert.strictEqual(props.relativeToCursor.linesBelow, 2);
246
model.dispose();
247
});
248
249
test('duplicated whitespace insert', () => {
250
const model = createTextModel('hello');
251
const edit = StringEdit.insert(5, ' ');
252
const result = computeEditKind(edit, model);
253
254
assert.ok(result);
255
assert.strictEqual(result.edits.length, 1);
256
assert.strictEqual(result.edits[0].operation, 'insert');
257
const props = result.edits[0].properties as InsertProperties;
258
if (props.textShape.kind === 'singleLine') {
259
assert.strictEqual(props.textShape.hasDuplicatedWhitespace, true);
260
}
261
model.dispose();
262
});
263
});
264
265
suite('Delete operations', () => {
266
test('single character delete - identifier', () => {
267
const model = createTextModel('hello world');
268
const edit = StringEdit.delete(new OffsetRange(4, 5)); // delete 'o'
269
const result = computeEditKind(edit, model);
270
271
assert.ok(result);
272
assert.strictEqual(result.edits.length, 1);
273
assert.strictEqual(result.edits[0].operation, 'delete');
274
const props = result.edits[0].properties as DeleteProperties;
275
if (props.textShape.kind === 'singleLine') {
276
assert.strictEqual(props.textShape.isSingleCharacter, true);
277
assert.strictEqual(props.textShape.singleCharacterKind, 'identifier');
278
}
279
model.dispose();
280
});
281
282
test('single character delete - syntactical', () => {
283
const model = createTextModel('hello;world');
284
const edit = StringEdit.delete(new OffsetRange(5, 6)); // delete ';'
285
const result = computeEditKind(edit, model);
286
287
assert.ok(result);
288
assert.strictEqual(result.edits.length, 1);
289
assert.strictEqual(result.edits[0].operation, 'delete');
290
const props = result.edits[0].properties as DeleteProperties;
291
if (props.textShape.kind === 'singleLine') {
292
assert.strictEqual(props.textShape.isSingleCharacter, true);
293
assert.strictEqual(props.textShape.singleCharacterKind, 'syntactical');
294
}
295
model.dispose();
296
});
297
298
test('word delete', () => {
299
const model = createTextModel('hello world');
300
const edit = StringEdit.delete(new OffsetRange(0, 5)); // delete 'hello'
301
const result = computeEditKind(edit, model);
302
303
assert.ok(result);
304
assert.strictEqual(result.edits.length, 1);
305
assert.strictEqual(result.edits[0].operation, 'delete');
306
assert.strictEqual(result.edits[0].charactersInserted, 0);
307
assert.strictEqual(result.edits[0].charactersDeleted, 5);
308
assert.strictEqual(result.edits[0].linesInserted, 0);
309
assert.strictEqual(result.edits[0].linesDeleted, 0);
310
const props = result.edits[0].properties as DeleteProperties;
311
if (props.textShape.kind === 'singleLine') {
312
assert.strictEqual(props.textShape.isWord, true);
313
}
314
model.dispose();
315
});
316
317
test('multi-line delete', () => {
318
const model = createTextModel('line1\nline2\nline3');
319
const edit = StringEdit.delete(new OffsetRange(0, 12)); // delete 'line1\nline2\n'
320
const result = computeEditKind(edit, model);
321
322
assert.ok(result);
323
assert.strictEqual(result.edits.length, 1);
324
assert.strictEqual(result.edits[0].operation, 'delete');
325
assert.strictEqual(result.edits[0].charactersInserted, 0);
326
assert.strictEqual(result.edits[0].charactersDeleted, 12);
327
assert.strictEqual(result.edits[0].linesInserted, 0);
328
assert.strictEqual(result.edits[0].linesDeleted, 2);
329
const props = result.edits[0].properties as DeleteProperties;
330
assert.strictEqual(props.textShape.kind, 'multiLine');
331
model.dispose();
332
});
333
334
test('delete entire line content', () => {
335
const model = createTextModel('hello');
336
const edit = StringEdit.delete(new OffsetRange(0, 5));
337
const result = computeEditKind(edit, model);
338
339
assert.ok(result);
340
assert.strictEqual(result.edits.length, 1);
341
assert.strictEqual(result.edits[0].operation, 'delete');
342
const props = result.edits[0].properties as DeleteProperties;
343
assert.strictEqual(props.deletesEntireLineContent, true);
344
model.dispose();
345
});
346
});
347
348
suite('Replace operations', () => {
349
test('word to word replacement', () => {
350
const model = createTextModel('hello world');
351
const edit = StringEdit.replace(new OffsetRange(0, 5), 'goodbye');
352
const result = computeEditKind(edit, model);
353
354
assert.ok(result);
355
assert.strictEqual(result.edits.length, 1);
356
assert.strictEqual(result.edits[0].operation, 'replace');
357
assert.strictEqual(result.edits[0].charactersInserted, 7);
358
assert.strictEqual(result.edits[0].charactersDeleted, 5);
359
assert.strictEqual(result.edits[0].linesInserted, 0);
360
assert.strictEqual(result.edits[0].linesDeleted, 0);
361
const props = result.edits[0].properties as ReplaceProperties;
362
assert.strictEqual(props.isWordToWordReplacement, true);
363
model.dispose();
364
});
365
366
test('additive replacement', () => {
367
const model = createTextModel('hi world');
368
const edit = StringEdit.replace(new OffsetRange(0, 2), 'hello');
369
const result = computeEditKind(edit, model);
370
371
assert.ok(result);
372
assert.strictEqual(result.edits.length, 1);
373
assert.strictEqual(result.edits[0].operation, 'replace');
374
assert.strictEqual(result.edits[0].charactersInserted, 5);
375
assert.strictEqual(result.edits[0].charactersDeleted, 2);
376
const props = result.edits[0].properties as ReplaceProperties;
377
assert.strictEqual(props.isAdditive, true);
378
assert.strictEqual(props.isSubtractive, false);
379
model.dispose();
380
});
381
382
test('subtractive replacement', () => {
383
const model = createTextModel('hello world');
384
const edit = StringEdit.replace(new OffsetRange(0, 5), 'hi');
385
const result = computeEditKind(edit, model);
386
387
assert.ok(result);
388
assert.strictEqual(result.edits.length, 1);
389
assert.strictEqual(result.edits[0].operation, 'replace');
390
assert.strictEqual(result.edits[0].charactersInserted, 2);
391
assert.strictEqual(result.edits[0].charactersDeleted, 5);
392
const props = result.edits[0].properties as ReplaceProperties;
393
assert.strictEqual(props.isSubtractive, true);
394
assert.strictEqual(props.isAdditive, false);
395
model.dispose();
396
});
397
398
test('single line to multi-line replacement', () => {
399
const model = createTextModel('hello world');
400
const edit = StringEdit.replace(new OffsetRange(0, 5), 'line1\nline2');
401
const result = computeEditKind(edit, model);
402
403
assert.ok(result);
404
assert.strictEqual(result.edits.length, 1);
405
assert.strictEqual(result.edits[0].operation, 'replace');
406
assert.strictEqual(result.edits[0].linesInserted, 1);
407
assert.strictEqual(result.edits[0].linesDeleted, 0);
408
const props = result.edits[0].properties as ReplaceProperties;
409
assert.strictEqual(props.isSingleLineToMultiLine, true);
410
model.dispose();
411
});
412
413
test('multi-line to single line replacement', () => {
414
const model = createTextModel('line1\nline2\nline3');
415
const edit = StringEdit.replace(new OffsetRange(0, 12), 'hello');
416
const result = computeEditKind(edit, model);
417
418
assert.ok(result);
419
assert.strictEqual(result.edits.length, 1);
420
assert.strictEqual(result.edits[0].operation, 'replace');
421
assert.strictEqual(result.edits[0].linesInserted, 0);
422
assert.strictEqual(result.edits[0].linesDeleted, 2);
423
const props = result.edits[0].properties as ReplaceProperties;
424
assert.strictEqual(props.isMultiLineToSingleLine, true);
425
model.dispose();
426
});
427
428
test('single line to single line replacement', () => {
429
const model = createTextModel('hello world');
430
const edit = StringEdit.replace(new OffsetRange(0, 5), 'goodbye');
431
const result = computeEditKind(edit, model);
432
433
assert.ok(result);
434
assert.strictEqual(result.edits.length, 1);
435
assert.strictEqual(result.edits[0].operation, 'replace');
436
const props = result.edits[0].properties as ReplaceProperties;
437
assert.strictEqual(props.isSingleLineToSingleLine, true);
438
model.dispose();
439
});
440
});
441
442
suite('Empty edit', () => {
443
test('empty edit returns undefined', () => {
444
const model = createTextModel('hello world');
445
const edit = StringEdit.empty;
446
const result = computeEditKind(edit, model);
447
448
assert.strictEqual(result, undefined);
449
model.dispose();
450
});
451
});
452
453
suite('Multiple replacements', () => {
454
test('multiple inserts', () => {
455
const model = createTextModel('hello world');
456
const edit = new StringEdit([
457
StringEdit.insert(0, 'A').replacements[0],
458
StringEdit.insert(5, 'B').replacements[0],
459
]);
460
const result = computeEditKind(edit, model);
461
462
assert.ok(result);
463
assert.strictEqual(result.edits.length, 2);
464
assert.strictEqual(result.edits[0].operation, 'insert');
465
assert.strictEqual(result.edits[1].operation, 'insert');
466
model.dispose();
467
});
468
469
test('mixed operations', () => {
470
const model = createTextModel('hello world');
471
const edit = new StringEdit([
472
StringEdit.insert(0, 'prefix').replacements[0],
473
StringEdit.delete(new OffsetRange(5, 6)).replacements[0],
474
]);
475
const result = computeEditKind(edit, model);
476
477
assert.ok(result);
478
assert.strictEqual(result.edits.length, 2);
479
assert.strictEqual(result.edits[0].operation, 'insert');
480
assert.strictEqual(result.edits[1].operation, 'delete');
481
model.dispose();
482
});
483
});
484
});
485
486