Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/snippet/test/browser/snippetSession.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
import assert from 'assert';
6
import { mock } from '../../../../../base/test/common/mock.js';
7
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
8
import { IActiveCodeEditor } from '../../../../browser/editorBrowser.js';
9
import { IPosition, Position } from '../../../../common/core/position.js';
10
import { Range } from '../../../../common/core/range.js';
11
import { Selection } from '../../../../common/core/selection.js';
12
import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js';
13
import { TextModel } from '../../../../common/model/textModel.js';
14
import { SnippetParser } from '../../browser/snippetParser.js';
15
import { SnippetSession } from '../../browser/snippetSession.js';
16
import { createTestCodeEditor } from '../../../../test/browser/testCodeEditor.js';
17
import { TestLanguageConfigurationService } from '../../../../test/common/modes/testLanguageConfigurationService.js';
18
import { createTextModel } from '../../../../test/common/testTextModel.js';
19
import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js';
20
import { ILabelService } from '../../../../../platform/label/common/label.js';
21
import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';
22
23
suite('SnippetSession', function () {
24
25
let languageConfigurationService: ILanguageConfigurationService;
26
let editor: IActiveCodeEditor;
27
let model: TextModel;
28
29
function assertSelections(editor: IActiveCodeEditor, ...s: Selection[]) {
30
for (const selection of editor.getSelections()) {
31
const actual = s.shift()!;
32
assert.ok(selection.equalsSelection(actual), `actual=${selection.toString()} <> expected=${actual.toString()}`);
33
}
34
assert.strictEqual(s.length, 0);
35
}
36
37
setup(function () {
38
model = createTextModel('function foo() {\n console.log(a);\n}');
39
languageConfigurationService = new TestLanguageConfigurationService();
40
const serviceCollection = new ServiceCollection(
41
[ILabelService, new class extends mock<ILabelService>() { }],
42
[ILanguageConfigurationService, languageConfigurationService],
43
[IWorkspaceContextService, new class extends mock<IWorkspaceContextService>() {
44
override getWorkspace() {
45
return {
46
id: 'workspace-id',
47
folders: [],
48
};
49
}
50
}],
51
);
52
editor = createTestCodeEditor(model, { serviceCollection }) as IActiveCodeEditor;
53
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]);
54
assert.strictEqual(model.getEOL(), '\n');
55
});
56
57
teardown(function () {
58
model.dispose();
59
editor.dispose();
60
});
61
62
ensureNoDisposablesAreLeakedInTestSuite();
63
64
test('normalize whitespace', function () {
65
66
function assertNormalized(position: IPosition, input: string, expected: string): void {
67
const snippet = new SnippetParser().parse(input);
68
SnippetSession.adjustWhitespace(model, position, true, snippet);
69
assert.strictEqual(snippet.toTextmateString(), expected);
70
}
71
72
assertNormalized(new Position(1, 1), 'foo', 'foo');
73
assertNormalized(new Position(1, 1), 'foo\rbar', 'foo\nbar');
74
assertNormalized(new Position(1, 1), 'foo\rbar', 'foo\nbar');
75
assertNormalized(new Position(2, 5), 'foo\r\tbar', 'foo\n bar');
76
assertNormalized(new Position(2, 3), 'foo\r\tbar', 'foo\n bar');
77
assertNormalized(new Position(2, 5), 'foo\r\tbar\nfoo', 'foo\n bar\n foo');
78
79
//Indentation issue with choice elements that span multiple lines #46266
80
assertNormalized(new Position(2, 5), 'a\nb${1|foo,\nbar|}', 'a\n b${1|foo,\nbar|}');
81
});
82
83
test('adjust selection (overwrite[Before|After])', function () {
84
85
let range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 1, 0);
86
assert.ok(range.equalsRange(new Range(1, 1, 1, 2)));
87
range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 1111, 0);
88
assert.ok(range.equalsRange(new Range(1, 1, 1, 2)));
89
range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 0, 10);
90
assert.ok(range.equalsRange(new Range(1, 2, 1, 12)));
91
range = SnippetSession.adjustSelection(model, new Selection(1, 2, 1, 2), 0, 10111);
92
assert.ok(range.equalsRange(new Range(1, 2, 1, 17)));
93
94
});
95
96
test('text edits & selection', function () {
97
const session = new SnippetSession(editor, 'foo${1:bar}foo$0', undefined, languageConfigurationService);
98
session.insert();
99
assert.strictEqual(editor.getModel()!.getValue(), 'foobarfoofunction foo() {\n foobarfooconsole.log(a);\n}');
100
101
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
102
session.next();
103
assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14));
104
});
105
106
test('text edit with reversed selection', function () {
107
108
const session = new SnippetSession(editor, '${1:bar}$0', undefined, languageConfigurationService);
109
editor.setSelections([new Selection(2, 5, 2, 5), new Selection(1, 1, 1, 1)]);
110
111
session.insert();
112
assert.strictEqual(model.getValue(), 'barfunction foo() {\n barconsole.log(a);\n}');
113
assertSelections(editor, new Selection(2, 5, 2, 8), new Selection(1, 1, 1, 4));
114
});
115
116
test('snippets, repeated tabstops', function () {
117
const session = new SnippetSession(editor, '${1:abc}foo${1:abc}$0', undefined, languageConfigurationService);
118
session.insert();
119
assertSelections(editor,
120
new Selection(1, 1, 1, 4), new Selection(1, 7, 1, 10),
121
new Selection(2, 5, 2, 8), new Selection(2, 11, 2, 14),
122
);
123
session.next();
124
assertSelections(editor,
125
new Selection(1, 10, 1, 10),
126
new Selection(2, 14, 2, 14),
127
);
128
});
129
130
test('snippets, just text', function () {
131
const session = new SnippetSession(editor, 'foobar', undefined, languageConfigurationService);
132
session.insert();
133
assert.strictEqual(model.getValue(), 'foobarfunction foo() {\n foobarconsole.log(a);\n}');
134
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
135
});
136
137
test('snippets, selections and new text with newlines', () => {
138
139
const session = new SnippetSession(editor, 'foo\n\t${1:bar}\n$0', undefined, languageConfigurationService);
140
session.insert();
141
142
assert.strictEqual(editor.getModel()!.getValue(), 'foo\n bar\nfunction foo() {\n foo\n bar\n console.log(a);\n}');
143
144
assertSelections(editor, new Selection(2, 5, 2, 8), new Selection(5, 9, 5, 12));
145
146
session.next();
147
assertSelections(editor, new Selection(3, 1, 3, 1), new Selection(6, 5, 6, 5));
148
});
149
150
test('snippets, newline NO whitespace adjust', () => {
151
152
editor.setSelection(new Selection(2, 5, 2, 5));
153
const session = new SnippetSession(editor, 'abc\n foo\n bar\n$0', { overwriteBefore: 0, overwriteAfter: 0, adjustWhitespace: false, clipboardText: undefined, overtypingCapturer: undefined }, languageConfigurationService);
154
session.insert();
155
assert.strictEqual(editor.getModel()!.getValue(), 'function foo() {\n abc\n foo\n bar\nconsole.log(a);\n}');
156
});
157
158
test('snippets, selections -> next/prev', () => {
159
160
const session = new SnippetSession(editor, 'f$1oo${2:bar}foo$0', undefined, languageConfigurationService);
161
session.insert();
162
163
// @ $2
164
assertSelections(editor, new Selection(1, 2, 1, 2), new Selection(2, 6, 2, 6));
165
// @ $1
166
session.next();
167
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
168
// @ $2
169
session.prev();
170
assertSelections(editor, new Selection(1, 2, 1, 2), new Selection(2, 6, 2, 6));
171
// @ $1
172
session.next();
173
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
174
// @ $0
175
session.next();
176
assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14));
177
});
178
179
test('snippets, selections & typing', function () {
180
const session = new SnippetSession(editor, 'f${1:oo}_$2_$0', undefined, languageConfigurationService);
181
session.insert();
182
183
editor.trigger('test', 'type', { text: 'X' });
184
session.next();
185
editor.trigger('test', 'type', { text: 'bar' });
186
187
// go back to ${2:oo} which is now just 'X'
188
session.prev();
189
assertSelections(editor, new Selection(1, 2, 1, 3), new Selection(2, 6, 2, 7));
190
191
// go forward to $1 which is now 'bar'
192
session.next();
193
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
194
195
// go to final tabstop
196
session.next();
197
assert.strictEqual(model.getValue(), 'fX_bar_function foo() {\n fX_bar_console.log(a);\n}');
198
assertSelections(editor, new Selection(1, 8, 1, 8), new Selection(2, 12, 2, 12));
199
});
200
201
test('snippets, insert shorter snippet into non-empty selection', function () {
202
model.setValue('foo_bar_foo');
203
editor.setSelections([new Selection(1, 1, 1, 4), new Selection(1, 9, 1, 12)]);
204
205
new SnippetSession(editor, 'x$0', undefined, languageConfigurationService).insert();
206
assert.strictEqual(model.getValue(), 'x_bar_x');
207
assertSelections(editor, new Selection(1, 2, 1, 2), new Selection(1, 8, 1, 8));
208
});
209
210
test('snippets, insert longer snippet into non-empty selection', function () {
211
model.setValue('foo_bar_foo');
212
editor.setSelections([new Selection(1, 1, 1, 4), new Selection(1, 9, 1, 12)]);
213
214
new SnippetSession(editor, 'LONGER$0', undefined, languageConfigurationService).insert();
215
assert.strictEqual(model.getValue(), 'LONGER_bar_LONGER');
216
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(1, 18, 1, 18));
217
});
218
219
test('snippets, don\'t grow final tabstop', function () {
220
model.setValue('foo_zzz_foo');
221
editor.setSelection(new Selection(1, 5, 1, 8));
222
const session = new SnippetSession(editor, '$1bar$0', undefined, languageConfigurationService);
223
session.insert();
224
225
assertSelections(editor, new Selection(1, 5, 1, 5));
226
editor.trigger('test', 'type', { text: 'foo-' });
227
228
session.next();
229
assert.strictEqual(model.getValue(), 'foo_foo-bar_foo');
230
assertSelections(editor, new Selection(1, 12, 1, 12));
231
232
editor.trigger('test', 'type', { text: 'XXX' });
233
assert.strictEqual(model.getValue(), 'foo_foo-barXXX_foo');
234
session.prev();
235
assertSelections(editor, new Selection(1, 5, 1, 9));
236
session.next();
237
assertSelections(editor, new Selection(1, 15, 1, 15));
238
});
239
240
test('snippets, don\'t merge touching tabstops 1/2', function () {
241
242
const session = new SnippetSession(editor, '$1$2$3$0', undefined, languageConfigurationService);
243
session.insert();
244
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
245
246
session.next();
247
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
248
249
session.next();
250
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
251
252
session.next();
253
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
254
255
session.prev();
256
session.prev();
257
session.prev();
258
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
259
editor.trigger('test', 'type', { text: '111' });
260
261
session.next();
262
editor.trigger('test', 'type', { text: '222' });
263
264
session.next();
265
editor.trigger('test', 'type', { text: '333' });
266
267
session.next();
268
assert.strictEqual(model.getValue(), '111222333function foo() {\n 111222333console.log(a);\n}');
269
assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14));
270
271
session.prev();
272
assertSelections(editor, new Selection(1, 7, 1, 10), new Selection(2, 11, 2, 14));
273
session.prev();
274
assertSelections(editor, new Selection(1, 4, 1, 7), new Selection(2, 8, 2, 11));
275
session.prev();
276
assertSelections(editor, new Selection(1, 1, 1, 4), new Selection(2, 5, 2, 8));
277
});
278
test('snippets, don\'t merge touching tabstops 2/2', function () {
279
280
const session = new SnippetSession(editor, '$1$2$3$0', undefined, languageConfigurationService);
281
session.insert();
282
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
283
284
editor.trigger('test', 'type', { text: '111' });
285
286
session.next();
287
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
288
editor.trigger('test', 'type', { text: '222' });
289
290
session.next();
291
assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
292
editor.trigger('test', 'type', { text: '333' });
293
294
session.next();
295
assert.strictEqual(session.isAtLastPlaceholder, true);
296
});
297
298
test('snippets, gracefully move over final tabstop', function () {
299
const session = new SnippetSession(editor, '${1}bar$0', undefined, languageConfigurationService);
300
session.insert();
301
302
assert.strictEqual(session.isAtLastPlaceholder, false);
303
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5));
304
305
session.next();
306
assert.strictEqual(session.isAtLastPlaceholder, true);
307
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
308
309
session.next();
310
assert.strictEqual(session.isAtLastPlaceholder, true);
311
assertSelections(editor, new Selection(1, 4, 1, 4), new Selection(2, 8, 2, 8));
312
});
313
314
test('snippets, overwriting nested placeholder', function () {
315
const session = new SnippetSession(editor, 'log(${1:"$2"});$0', undefined, languageConfigurationService);
316
session.insert();
317
assertSelections(editor, new Selection(1, 5, 1, 7), new Selection(2, 9, 2, 11));
318
319
editor.trigger('test', 'type', { text: 'XXX' });
320
assert.strictEqual(model.getValue(), 'log(XXX);function foo() {\n log(XXX);console.log(a);\n}');
321
322
session.next();
323
assert.strictEqual(session.isAtLastPlaceholder, false);
324
// assertSelections(editor, new Selection(1, 7, 1, 7), new Selection(2, 11, 2, 11));
325
326
session.next();
327
assert.strictEqual(session.isAtLastPlaceholder, true);
328
assertSelections(editor, new Selection(1, 10, 1, 10), new Selection(2, 14, 2, 14));
329
});
330
331
test('snippets, selections and snippet ranges', function () {
332
const session = new SnippetSession(editor, '${1:foo}farboo${2:bar}$0', undefined, languageConfigurationService);
333
session.insert();
334
assert.strictEqual(model.getValue(), 'foofarboobarfunction foo() {\n foofarboobarconsole.log(a);\n}');
335
assertSelections(editor, new Selection(1, 1, 1, 4), new Selection(2, 5, 2, 8));
336
337
assert.strictEqual(session.isSelectionWithinPlaceholders(), true);
338
339
editor.setSelections([new Selection(1, 1, 1, 1)]);
340
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
341
342
editor.setSelections([new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10)]);
343
assert.strictEqual(session.isSelectionWithinPlaceholders(), false); // in snippet, outside placeholder
344
345
editor.setSelections([new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10), new Selection(1, 1, 1, 1)]);
346
assert.strictEqual(session.isSelectionWithinPlaceholders(), false); // in snippet, outside placeholder
347
348
editor.setSelections([new Selection(1, 6, 1, 6), new Selection(2, 10, 2, 10), new Selection(2, 20, 2, 21)]);
349
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
350
351
// reset selection to placeholder
352
session.next();
353
assert.strictEqual(session.isSelectionWithinPlaceholders(), true);
354
assertSelections(editor, new Selection(1, 10, 1, 13), new Selection(2, 14, 2, 17));
355
356
// reset selection to placeholder
357
session.next();
358
assert.strictEqual(session.isSelectionWithinPlaceholders(), true);
359
assert.strictEqual(session.isAtLastPlaceholder, true);
360
assertSelections(editor, new Selection(1, 13, 1, 13), new Selection(2, 17, 2, 17));
361
});
362
363
test('snippets, nested sessions', function () {
364
365
model.setValue('');
366
editor.setSelection(new Selection(1, 1, 1, 1));
367
368
const first = new SnippetSession(editor, 'foo${2:bar}foo$0', undefined, languageConfigurationService);
369
first.insert();
370
assert.strictEqual(model.getValue(), 'foobarfoo');
371
assertSelections(editor, new Selection(1, 4, 1, 7));
372
373
const second = new SnippetSession(editor, 'ba${1:zzzz}$0', undefined, languageConfigurationService);
374
second.insert();
375
assert.strictEqual(model.getValue(), 'foobazzzzfoo');
376
assertSelections(editor, new Selection(1, 6, 1, 10));
377
378
second.next();
379
assert.strictEqual(second.isAtLastPlaceholder, true);
380
assertSelections(editor, new Selection(1, 10, 1, 10));
381
382
first.next();
383
assert.strictEqual(first.isAtLastPlaceholder, true);
384
assertSelections(editor, new Selection(1, 13, 1, 13));
385
});
386
387
test('snippets, typing at final tabstop', function () {
388
389
const session = new SnippetSession(editor, 'farboo$0', undefined, languageConfigurationService);
390
session.insert();
391
assert.strictEqual(session.isAtLastPlaceholder, true);
392
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
393
394
editor.trigger('test', 'type', { text: 'XXX' });
395
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
396
});
397
398
test('snippets, typing at beginning', function () {
399
400
editor.setSelection(new Selection(1, 2, 1, 2));
401
const session = new SnippetSession(editor, 'farboo$0', undefined, languageConfigurationService);
402
session.insert();
403
404
editor.setSelection(new Selection(1, 2, 1, 2));
405
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
406
assert.strictEqual(session.isAtLastPlaceholder, true);
407
408
editor.trigger('test', 'type', { text: 'XXX' });
409
assert.strictEqual(model.getLineContent(1), 'fXXXfarboounction foo() {');
410
assert.strictEqual(session.isSelectionWithinPlaceholders(), false);
411
412
session.next();
413
assertSelections(editor, new Selection(1, 11, 1, 11));
414
});
415
416
test('snippets, typing with nested placeholder', function () {
417
418
editor.setSelection(new Selection(1, 1, 1, 1));
419
const session = new SnippetSession(editor, 'This ${1:is ${2:nested}}.$0', undefined, languageConfigurationService);
420
session.insert();
421
assertSelections(editor, new Selection(1, 6, 1, 15));
422
423
session.next();
424
assertSelections(editor, new Selection(1, 9, 1, 15));
425
426
editor.trigger('test', 'cut', {});
427
assertSelections(editor, new Selection(1, 9, 1, 9));
428
429
editor.trigger('test', 'type', { text: 'XXX' });
430
session.prev();
431
assertSelections(editor, new Selection(1, 6, 1, 12));
432
});
433
434
test('snippets, snippet with variables', function () {
435
const session = new SnippetSession(editor, '@line=$TM_LINE_NUMBER$0', undefined, languageConfigurationService);
436
session.insert();
437
438
assert.strictEqual(model.getValue(), '@line=1function foo() {\n @line=2console.log(a);\n}');
439
assertSelections(editor, new Selection(1, 8, 1, 8), new Selection(2, 12, 2, 12));
440
});
441
442
test('snippets, merge', function () {
443
editor.setSelection(new Selection(1, 1, 1, 1));
444
const session = new SnippetSession(editor, 'This ${1:is ${2:nested}}.$0', undefined, languageConfigurationService);
445
session.insert();
446
session.next();
447
assertSelections(editor, new Selection(1, 9, 1, 15));
448
449
session.merge('really ${1:nested}$0');
450
assertSelections(editor, new Selection(1, 16, 1, 22));
451
452
session.next();
453
assertSelections(editor, new Selection(1, 22, 1, 22));
454
assert.strictEqual(session.isAtLastPlaceholder, false);
455
456
session.next();
457
assert.strictEqual(session.isAtLastPlaceholder, true);
458
assertSelections(editor, new Selection(1, 23, 1, 23));
459
460
session.prev();
461
editor.trigger('test', 'type', { text: 'AAA' });
462
463
// back to `really ${1:nested}`
464
session.prev();
465
assertSelections(editor, new Selection(1, 16, 1, 22));
466
467
// back to `${1:is ...}` which now grew
468
session.prev();
469
assertSelections(editor, new Selection(1, 6, 1, 25));
470
});
471
472
test('snippets, transform', function () {
473
editor.getModel()!.setValue('');
474
editor.setSelection(new Selection(1, 1, 1, 1));
475
const session = new SnippetSession(editor, '${1/foo/bar/}$0', undefined, languageConfigurationService);
476
session.insert();
477
assertSelections(editor, new Selection(1, 1, 1, 1));
478
479
editor.trigger('test', 'type', { text: 'foo' });
480
session.next();
481
482
assert.strictEqual(model.getValue(), 'bar');
483
assert.strictEqual(session.isAtLastPlaceholder, true);
484
assertSelections(editor, new Selection(1, 4, 1, 4));
485
});
486
487
test('snippets, multi placeholder same index one transform', function () {
488
editor.getModel()!.setValue('');
489
editor.setSelection(new Selection(1, 1, 1, 1));
490
const session = new SnippetSession(editor, '$1 baz ${1/foo/bar/}$0', undefined, languageConfigurationService);
491
session.insert();
492
assertSelections(editor, new Selection(1, 1, 1, 1), new Selection(1, 6, 1, 6));
493
494
editor.trigger('test', 'type', { text: 'foo' });
495
session.next();
496
497
assert.strictEqual(model.getValue(), 'foo baz bar');
498
assert.strictEqual(session.isAtLastPlaceholder, true);
499
assertSelections(editor, new Selection(1, 12, 1, 12));
500
});
501
502
test('snippets, transform example', function () {
503
editor.getModel()!.setValue('');
504
editor.setSelection(new Selection(1, 1, 1, 1));
505
const session = new SnippetSession(editor, '${1:name} : ${2:type}${3/\\s:=(.*)/${1:+ :=}${1}/};\n$0', undefined, languageConfigurationService);
506
session.insert();
507
508
assertSelections(editor, new Selection(1, 1, 1, 5));
509
editor.trigger('test', 'type', { text: 'clk' });
510
session.next();
511
512
assertSelections(editor, new Selection(1, 7, 1, 11));
513
editor.trigger('test', 'type', { text: 'std_logic' });
514
session.next();
515
516
assertSelections(editor, new Selection(1, 16, 1, 16));
517
session.next();
518
519
assert.strictEqual(model.getValue(), 'clk : std_logic;\n');
520
assert.strictEqual(session.isAtLastPlaceholder, true);
521
assertSelections(editor, new Selection(2, 1, 2, 1));
522
});
523
524
test('snippets, transform with indent', function () {
525
const snippet = [
526
'private readonly ${1} = new Emitter<$2>();',
527
'readonly ${1/^_(.*)/$1/}: Event<$2> = this.$1.event;',
528
'$0'
529
].join('\n');
530
const expected = [
531
'{',
532
'\tprivate readonly _prop = new Emitter<string>();',
533
'\treadonly prop: Event<string> = this._prop.event;',
534
'\t',
535
'}'
536
].join('\n');
537
const base = [
538
'{',
539
'\t',
540
'}'
541
].join('\n');
542
543
editor.getModel()!.setValue(base);
544
editor.getModel()!.updateOptions({ insertSpaces: false });
545
editor.setSelection(new Selection(2, 2, 2, 2));
546
547
const session = new SnippetSession(editor, snippet, undefined, languageConfigurationService);
548
session.insert();
549
550
assertSelections(editor, new Selection(2, 19, 2, 19), new Selection(3, 11, 3, 11), new Selection(3, 28, 3, 28));
551
editor.trigger('test', 'type', { text: '_prop' });
552
session.next();
553
554
assertSelections(editor, new Selection(2, 39, 2, 39), new Selection(3, 23, 3, 23));
555
editor.trigger('test', 'type', { text: 'string' });
556
session.next();
557
558
assert.strictEqual(model.getValue(), expected);
559
assert.strictEqual(session.isAtLastPlaceholder, true);
560
assertSelections(editor, new Selection(4, 2, 4, 2));
561
562
});
563
564
test('snippets, transform example hit if', function () {
565
editor.getModel()!.setValue('');
566
editor.setSelection(new Selection(1, 1, 1, 1));
567
const session = new SnippetSession(editor, '${1:name} : ${2:type}${3/\\s:=(.*)/${1:+ :=}${1}/};\n$0', undefined, languageConfigurationService);
568
session.insert();
569
570
assertSelections(editor, new Selection(1, 1, 1, 5));
571
editor.trigger('test', 'type', { text: 'clk' });
572
session.next();
573
574
assertSelections(editor, new Selection(1, 7, 1, 11));
575
editor.trigger('test', 'type', { text: 'std_logic' });
576
session.next();
577
578
assertSelections(editor, new Selection(1, 16, 1, 16));
579
editor.trigger('test', 'type', { text: ' := \'1\'' });
580
session.next();
581
582
assert.strictEqual(model.getValue(), 'clk : std_logic := \'1\';\n');
583
assert.strictEqual(session.isAtLastPlaceholder, true);
584
assertSelections(editor, new Selection(2, 1, 2, 1));
585
});
586
587
test('Snippet tab stop selection issue #96545, snippets, transform adjacent to previous placeholder', function () {
588
editor.getModel()!.setValue('');
589
editor.setSelection(new Selection(1, 1, 1, 1));
590
const session = new SnippetSession(editor, '${1:{}${2:fff}${1/{/}/}', undefined, languageConfigurationService);
591
session.insert();
592
593
assertSelections(editor, new Selection(1, 1, 1, 2), new Selection(1, 5, 1, 6));
594
session.next();
595
596
assert.strictEqual(model.getValue(), '{fff}');
597
assertSelections(editor, new Selection(1, 2, 1, 5));
598
editor.trigger('test', 'type', { text: 'ggg' });
599
session.next();
600
601
assert.strictEqual(model.getValue(), '{ggg}');
602
assert.strictEqual(session.isAtLastPlaceholder, true);
603
assertSelections(editor, new Selection(1, 6, 1, 6));
604
});
605
606
test('Snippet tab stop selection issue #96545', function () {
607
editor.getModel().setValue('');
608
const session = new SnippetSession(editor, '${1:{}${2:fff}${1/[\\{]/}/}$0', undefined, languageConfigurationService);
609
session.insert();
610
assert.strictEqual(editor.getModel().getValue(), '{fff{');
611
612
assertSelections(editor, new Selection(1, 1, 1, 2), new Selection(1, 5, 1, 6));
613
session.next();
614
assertSelections(editor, new Selection(1, 2, 1, 5));
615
});
616
617
test('Snippet placeholder index incorrect after using 2+ snippets in a row that each end with a placeholder, #30769', function () {
618
editor.getModel()!.setValue('');
619
editor.setSelection(new Selection(1, 1, 1, 1));
620
const session = new SnippetSession(editor, 'test ${1:replaceme}', undefined, languageConfigurationService);
621
session.insert();
622
623
editor.trigger('test', 'type', { text: '1' });
624
editor.trigger('test', 'type', { text: '\n' });
625
assert.strictEqual(editor.getModel()!.getValue(), 'test 1\n');
626
627
session.merge('test ${1:replaceme}');
628
editor.trigger('test', 'type', { text: '2' });
629
editor.trigger('test', 'type', { text: '\n' });
630
631
assert.strictEqual(editor.getModel()!.getValue(), 'test 1\ntest 2\n');
632
633
session.merge('test ${1:replaceme}');
634
editor.trigger('test', 'type', { text: '3' });
635
editor.trigger('test', 'type', { text: '\n' });
636
637
assert.strictEqual(editor.getModel()!.getValue(), 'test 1\ntest 2\ntest 3\n');
638
639
session.merge('test ${1:replaceme}');
640
editor.trigger('test', 'type', { text: '4' });
641
editor.trigger('test', 'type', { text: '\n' });
642
643
assert.strictEqual(editor.getModel()!.getValue(), 'test 1\ntest 2\ntest 3\ntest 4\n');
644
});
645
646
test('Snippet variable text isn\'t whitespace normalised, #31124', function () {
647
editor.getModel()!.setValue([
648
'start',
649
'\t\t-one',
650
'\t\t-two',
651
'end'
652
].join('\n'));
653
654
editor.getModel()!.updateOptions({ insertSpaces: false });
655
editor.setSelection(new Selection(2, 2, 3, 7));
656
657
new SnippetSession(editor, '<div>\n\t$TM_SELECTED_TEXT\n</div>$0', undefined, languageConfigurationService).insert();
658
659
let expected = [
660
'start',
661
'\t<div>',
662
'\t\t\t-one',
663
'\t\t\t-two',
664
'\t</div>',
665
'end'
666
].join('\n');
667
668
assert.strictEqual(editor.getModel()!.getValue(), expected);
669
670
editor.getModel()!.setValue([
671
'start',
672
'\t\t-one',
673
'\t-two',
674
'end'
675
].join('\n'));
676
677
editor.getModel()!.updateOptions({ insertSpaces: false });
678
editor.setSelection(new Selection(2, 2, 3, 7));
679
680
new SnippetSession(editor, '<div>\n\t$TM_SELECTED_TEXT\n</div>$0', undefined, languageConfigurationService).insert();
681
682
expected = [
683
'start',
684
'\t<div>',
685
'\t\t\t-one',
686
'\t\t-two',
687
'\t</div>',
688
'end'
689
].join('\n');
690
691
assert.strictEqual(editor.getModel()!.getValue(), expected);
692
});
693
694
test('Selecting text from left to right, and choosing item messes up code, #31199', function () {
695
const model = editor.getModel()!;
696
model.setValue('console.log');
697
698
let actual = SnippetSession.adjustSelection(model, new Selection(1, 12, 1, 9), 3, 0);
699
assert.ok(actual.equalsSelection(new Selection(1, 9, 1, 6)));
700
701
actual = SnippetSession.adjustSelection(model, new Selection(1, 9, 1, 12), 3, 0);
702
assert.ok(actual.equalsSelection(new Selection(1, 9, 1, 12)));
703
704
editor.setSelections([new Selection(1, 9, 1, 12)]);
705
new SnippetSession(editor, 'far', { overwriteBefore: 3, overwriteAfter: 0, adjustWhitespace: true, clipboardText: undefined, overtypingCapturer: undefined }, languageConfigurationService).insert();
706
assert.strictEqual(model.getValue(), 'console.far');
707
});
708
709
test('Tabs don\'t get replaced with spaces in snippet transformations #103818', function () {
710
const model = editor.getModel()!;
711
model.setValue('\n{\n \n}');
712
model.updateOptions({ insertSpaces: true, indentSize: 2 });
713
editor.setSelections([new Selection(1, 1, 1, 1), new Selection(3, 6, 3, 6)]);
714
const session = new SnippetSession(editor, [
715
'function animate () {',
716
'\tvar ${1:a} = 12;',
717
'\tconsole.log(${1/(.*)/\n\t\t$1\n\t/})',
718
'}'
719
].join('\n'), undefined, languageConfigurationService);
720
721
session.insert();
722
723
assert.strictEqual(model.getValue(), [
724
'function animate () {',
725
' var a = 12;',
726
' console.log(a)',
727
'}',
728
'{',
729
' function animate () {',
730
' var a = 12;',
731
' console.log(a)',
732
' }',
733
'}',
734
].join('\n'));
735
736
editor.trigger('test', 'type', { text: 'bbb' });
737
session.next();
738
739
assert.strictEqual(model.getValue(), [
740
'function animate () {',
741
' var bbb = 12;',
742
' console.log(',
743
' bbb',
744
' )',
745
'}',
746
'{',
747
' function animate () {',
748
' var bbb = 12;',
749
' console.log(',
750
' bbb',
751
' )',
752
' }',
753
'}',
754
].join('\n'));
755
});
756
757
758
suite('createEditsAndSnippetsFromEdits', function () {
759
760
test('empty', function () {
761
762
const result = SnippetSession.createEditsAndSnippetsFromEdits(editor, [], true, true, undefined, undefined, languageConfigurationService);
763
764
assert.deepStrictEqual(result.edits, []);
765
assert.deepStrictEqual(result.snippets, []);
766
});
767
768
test('basic', function () {
769
770
editor.getModel().setValue('foo("bar")');
771
772
const result = SnippetSession.createEditsAndSnippetsFromEdits(
773
editor,
774
[{ range: new Range(1, 5, 1, 9), template: '$1' }, { range: new Range(1, 1, 1, 1), template: 'const ${1:new_const} = "bar"' }],
775
true, true, undefined, undefined, languageConfigurationService
776
);
777
778
assert.strictEqual(result.edits.length, 2);
779
assert.deepStrictEqual(result.edits[0].range, new Range(1, 1, 1, 1));
780
assert.deepStrictEqual(result.edits[0].text, 'const new_const = "bar"');
781
assert.deepStrictEqual(result.edits[1].range, new Range(1, 5, 1, 9));
782
assert.deepStrictEqual(result.edits[1].text, 'new_const');
783
784
assert.strictEqual(result.snippets.length, 1);
785
assert.strictEqual(result.snippets[0].isTrivialSnippet, false);
786
});
787
788
test('with $SELECTION variable', function () {
789
editor.getModel().setValue('Some text and a selection');
790
editor.setSelections([new Selection(1, 17, 1, 26)]);
791
792
const result = SnippetSession.createEditsAndSnippetsFromEdits(
793
editor,
794
[{ range: new Range(1, 17, 1, 26), template: 'wrapped <$SELECTION>' }],
795
true, true, undefined, undefined, languageConfigurationService
796
);
797
798
assert.strictEqual(result.edits.length, 1);
799
assert.deepStrictEqual(result.edits[0].range, new Range(1, 17, 1, 26));
800
assert.deepStrictEqual(result.edits[0].text, 'wrapped <selection>');
801
802
assert.strictEqual(result.snippets.length, 1);
803
assert.strictEqual(result.snippets[0].isTrivialSnippet, true);
804
});
805
});
806
});
807
808