Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletions.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 { timeout } from '../../../../../base/common/async.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
9
import { Range } from '../../../../common/core/range.js';
10
import { InlineCompletionsModel } from '../../browser/model/inlineCompletionsModel.js';
11
import { IWithAsyncTestCodeEditorAndInlineCompletionsModel, MockInlineCompletionsProvider, withAsyncTestCodeEditorAndInlineCompletionsModel } from './utils.js';
12
import { ITestCodeEditor } from '../../../../test/browser/testCodeEditor.js';
13
import { Selection } from '../../../../common/core/selection.js';
14
15
suite('Inline Completions', () => {
16
ensureNoDisposablesAreLeakedInTestSuite();
17
18
test('Does not trigger automatically if disabled', async function () {
19
const provider = new MockInlineCompletionsProvider();
20
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
21
{ fakeClock: true, provider, inlineSuggest: { enabled: false } },
22
async ({ editor, editorViewModel, model, context }) => {
23
context.keyboardType('foo');
24
await timeout(1000);
25
26
// Provider is not called, no ghost text is shown.
27
assert.deepStrictEqual(provider.getAndClearCallHistory(), []);
28
assert.deepStrictEqual(context.getAndClearViewStates(), ['']);
29
}
30
);
31
});
32
33
test('Ghost text is shown after trigger', async function () {
34
const provider = new MockInlineCompletionsProvider();
35
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
36
{ fakeClock: true, provider },
37
async ({ editor, editorViewModel, model, context }) => {
38
context.keyboardType('foo');
39
provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) });
40
model.triggerExplicitly();
41
await timeout(1000);
42
43
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
44
{ position: '(1,4)', text: 'foo', triggerKind: 1, }
45
]);
46
assert.deepStrictEqual(context.getAndClearViewStates(), ['', 'foo[bar]']);
47
}
48
);
49
});
50
51
test('Ghost text is shown automatically when configured', async function () {
52
const provider = new MockInlineCompletionsProvider();
53
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
54
{ fakeClock: true, provider, inlineSuggest: { enabled: true } },
55
async ({ editor, editorViewModel, model, context }) => {
56
context.keyboardType('foo');
57
58
provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) });
59
await timeout(1000);
60
61
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
62
{ position: '(1,4)', text: 'foo', triggerKind: 0, }
63
]);
64
assert.deepStrictEqual(context.getAndClearViewStates(), ['', 'foo[bar]']);
65
}
66
);
67
});
68
69
test('Ghost text is updated automatically', async function () {
70
const provider = new MockInlineCompletionsProvider();
71
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
72
{ fakeClock: true, provider },
73
async ({ editor, editorViewModel, model, context }) => {
74
provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) });
75
context.keyboardType('foo');
76
model.triggerExplicitly();
77
await timeout(1000);
78
79
provider.setReturnValue({ insertText: 'foobizz', range: new Range(1, 1, 1, 6) });
80
context.keyboardType('b');
81
context.keyboardType('i');
82
await timeout(1000);
83
84
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
85
{ position: '(1,4)', text: 'foo', triggerKind: 1, },
86
{ position: '(1,6)', text: 'foobi', triggerKind: 0, }
87
]);
88
assert.deepStrictEqual(
89
context.getAndClearViewStates(),
90
['', 'foo[bar]', 'foob[ar]', 'foobi', 'foobi[zz]']
91
);
92
}
93
);
94
});
95
96
test('Unindent whitespace', async function () {
97
const provider = new MockInlineCompletionsProvider();
98
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
99
{ fakeClock: true, provider },
100
async ({ editor, editorViewModel, model, context }) => {
101
context.keyboardType(' ');
102
provider.setReturnValue({ insertText: 'foo', range: new Range(1, 2, 1, 3) });
103
model.triggerExplicitly();
104
await timeout(1000);
105
106
assert.deepStrictEqual(context.getAndClearViewStates(), ['', ' [foo]']);
107
108
model.accept(editor);
109
110
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
111
{ position: '(1,3)', text: ' ', triggerKind: 1, },
112
]);
113
114
assert.deepStrictEqual(context.getAndClearViewStates(), [' foo']);
115
}
116
);
117
});
118
119
test('Unindent tab', async function () {
120
const provider = new MockInlineCompletionsProvider();
121
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
122
{ fakeClock: true, provider },
123
async ({ editor, editorViewModel, model, context }) => {
124
context.keyboardType('\t\t');
125
provider.setReturnValue({ insertText: 'foo', range: new Range(1, 2, 1, 3) });
126
model.triggerExplicitly();
127
await timeout(1000);
128
129
assert.deepStrictEqual(context.getAndClearViewStates(), ['', '\t\t[foo]']);
130
131
model.accept(editor);
132
133
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
134
{ position: '(1,3)', text: '\t\t', triggerKind: 1, },
135
]);
136
137
assert.deepStrictEqual(context.getAndClearViewStates(), ['\tfoo']);
138
}
139
);
140
});
141
142
test('No unindent after indentation', async function () {
143
const provider = new MockInlineCompletionsProvider();
144
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
145
{ fakeClock: true, provider },
146
async ({ editor, editorViewModel, model, context }) => {
147
context.keyboardType('buzz ');
148
provider.setReturnValue({ insertText: 'foo', range: new Range(1, 6, 1, 7) });
149
model.triggerExplicitly();
150
await timeout(1000);
151
152
assert.deepStrictEqual(context.getAndClearViewStates(), ['']);
153
154
model.accept(editor);
155
156
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
157
{ position: '(1,7)', text: 'buzz ', triggerKind: 1, },
158
]);
159
160
assert.deepStrictEqual(context.getAndClearViewStates(), []);
161
}
162
);
163
});
164
165
test('Next/previous', async function () {
166
const provider = new MockInlineCompletionsProvider();
167
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
168
{ fakeClock: true, provider },
169
async ({ editor, editorViewModel, model, context }) => {
170
context.keyboardType('foo');
171
provider.setReturnValue({ insertText: 'foobar1', range: new Range(1, 1, 1, 4) });
172
model.trigger();
173
await timeout(1000);
174
175
assert.deepStrictEqual(
176
context.getAndClearViewStates(),
177
['', 'foo[bar1]']
178
);
179
180
provider.setReturnValues([
181
{ insertText: 'foobar1', range: new Range(1, 1, 1, 4) },
182
{ insertText: 'foobizz2', range: new Range(1, 1, 1, 4) },
183
{ insertText: 'foobuzz3', range: new Range(1, 1, 1, 4) }
184
]);
185
186
model.next();
187
await timeout(1000);
188
assert.deepStrictEqual(context.getAndClearViewStates(), ['foo[bizz2]']);
189
190
model.next();
191
await timeout(1000);
192
assert.deepStrictEqual(context.getAndClearViewStates(), ['foo[buzz3]']);
193
194
model.next();
195
await timeout(1000);
196
assert.deepStrictEqual(context.getAndClearViewStates(), ['foo[bar1]']);
197
198
model.previous();
199
await timeout(1000);
200
assert.deepStrictEqual(context.getAndClearViewStates(), ['foo[buzz3]']);
201
202
model.previous();
203
await timeout(1000);
204
assert.deepStrictEqual(context.getAndClearViewStates(), ['foo[bizz2]']);
205
206
model.previous();
207
await timeout(1000);
208
assert.deepStrictEqual(context.getAndClearViewStates(), ['foo[bar1]']);
209
210
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
211
{ position: '(1,4)', text: 'foo', triggerKind: 0, },
212
{ position: '(1,4)', text: 'foo', triggerKind: 1, },
213
]);
214
}
215
);
216
});
217
218
test('Calling the provider is debounced', async function () {
219
const provider = new MockInlineCompletionsProvider();
220
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
221
{ fakeClock: true, provider },
222
async ({ editor, editorViewModel, model, context }) => {
223
model.trigger();
224
225
context.keyboardType('f');
226
await timeout(40);
227
context.keyboardType('o');
228
await timeout(40);
229
context.keyboardType('o');
230
await timeout(40);
231
232
// The provider is not called
233
assert.deepStrictEqual(provider.getAndClearCallHistory(), []);
234
235
await timeout(400);
236
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
237
{ position: '(1,4)', text: 'foo', triggerKind: 0, }
238
]);
239
240
provider.assertNotCalledTwiceWithin50ms();
241
}
242
);
243
});
244
245
test('Backspace is debounced', async function () {
246
const provider = new MockInlineCompletionsProvider();
247
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
248
{ fakeClock: true, provider, inlineSuggest: { enabled: true } },
249
async ({ editor, editorViewModel, model, context }) => {
250
context.keyboardType('foo');
251
252
provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) });
253
await timeout(1000);
254
255
for (let j = 0; j < 2; j++) {
256
for (let i = 0; i < 3; i++) {
257
context.leftDelete();
258
await timeout(5);
259
}
260
261
context.keyboardType('bar');
262
}
263
264
await timeout(400);
265
266
provider.assertNotCalledTwiceWithin50ms();
267
}
268
);
269
});
270
271
272
suite('Forward Stability', () => {
273
test('Typing agrees', async function () {
274
// The user types the text as suggested and the provider is forward-stable
275
const provider = new MockInlineCompletionsProvider();
276
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
277
{ fakeClock: true, provider },
278
async ({ editor, editorViewModel, model, context }) => {
279
provider.setReturnValue({ insertText: 'foobar', });
280
context.keyboardType('foo');
281
model.trigger();
282
await timeout(1000);
283
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
284
{ position: '(1,4)', text: 'foo', triggerKind: 0, }
285
]);
286
assert.deepStrictEqual(context.getAndClearViewStates(), ['', 'foo[bar]']);
287
288
context.keyboardType('b');
289
assert.deepStrictEqual(context.getAndClearViewStates(), (['foob[ar]']));
290
await timeout(1000);
291
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
292
{ position: '(1,5)', text: 'foob', triggerKind: 0, }
293
]);
294
assert.deepStrictEqual(context.getAndClearViewStates(), []);
295
296
context.keyboardType('a');
297
assert.deepStrictEqual(context.getAndClearViewStates(), (['fooba[r]']));
298
await timeout(1000);
299
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
300
{ position: '(1,6)', text: 'fooba', triggerKind: 0, }
301
]);
302
assert.deepStrictEqual(context.getAndClearViewStates(), []);
303
}
304
);
305
});
306
307
async function setupScenario({ editor, editorViewModel, model, context, store }: IWithAsyncTestCodeEditorAndInlineCompletionsModel, provider: MockInlineCompletionsProvider): Promise<void> {
308
assert.deepStrictEqual(context.getAndClearViewStates(), ['']);
309
provider.setReturnValue({ insertText: 'foo bar' });
310
context.keyboardType('f');
311
model.triggerExplicitly();
312
await timeout(10000);
313
assert.deepStrictEqual(provider.getAndClearCallHistory(), ([{ position: '(1,2)', triggerKind: 1, text: 'f' }]));
314
assert.deepStrictEqual(context.getAndClearViewStates(), (['f[oo bar]']));
315
316
provider.setReturnValue({ insertText: 'foo baz' });
317
await timeout(10000);
318
}
319
320
test('Support forward instability', async function () {
321
// The user types the text as suggested and the provider reports a different suggestion.
322
const provider = new MockInlineCompletionsProvider();
323
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
324
{ fakeClock: true, provider },
325
async (ctx) => {
326
await setupScenario(ctx, provider);
327
328
ctx.context.keyboardType('o');
329
assert.deepStrictEqual(ctx.context.getAndClearViewStates(), ['fo[o bar]']);
330
await timeout(10000);
331
332
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
333
{ position: '(1,3)', text: 'fo', triggerKind: 0, }
334
]);
335
assert.deepStrictEqual(ctx.context.getAndClearViewStates(), ['fo[o baz]']);
336
}
337
);
338
});
339
340
341
test('when accepting word by word', async function () {
342
// The user types the text as suggested and the provider reports a different suggestion.
343
// Even when triggering explicitly, we want to keep the suggestion.
344
345
const provider = new MockInlineCompletionsProvider();
346
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
347
{ fakeClock: true, provider },
348
async (ctx) => {
349
await setupScenario(ctx, provider);
350
351
await ctx.model.acceptNextWord();
352
assert.deepStrictEqual(ctx.context.getAndClearViewStates(), (['foo[ bar]']));
353
354
await timeout(10000);
355
assert.deepStrictEqual(provider.getAndClearCallHistory(), ([{ position: '(1,4)', triggerKind: 0, text: 'foo' }]));
356
assert.deepStrictEqual(ctx.context.getAndClearViewStates(), ([]));
357
358
await ctx.model.triggerExplicitly(); // reset to provider truth
359
await timeout(10000);
360
assert.deepStrictEqual(ctx.context.getAndClearViewStates(), ([]));
361
}
362
);
363
});
364
365
test('when accepting undo', async function () {
366
// The user types the text as suggested and the provider reports a different suggestion.
367
368
const provider = new MockInlineCompletionsProvider();
369
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
370
{ fakeClock: true, provider },
371
async (ctx) => {
372
await setupScenario(ctx, provider);
373
374
await ctx.model.acceptNextWord();
375
assert.deepStrictEqual(ctx.context.getAndClearViewStates(), (['foo[ bar]']));
376
377
await timeout(10000);
378
assert.deepStrictEqual(ctx.context.getAndClearViewStates(), ([]));
379
assert.deepStrictEqual(provider.getAndClearCallHistory(), ([{ position: '(1,4)', triggerKind: 0, text: 'foo' }]));
380
381
await ctx.editor.getModel().undo();
382
await timeout(10000);
383
assert.deepStrictEqual(ctx.context.getAndClearViewStates(), (['f[oo bar]']));
384
assert.deepStrictEqual(provider.getAndClearCallHistory(), ([{ position: '(1,2)', triggerKind: 0, text: 'f' }]));
385
386
await ctx.editor.getModel().redo();
387
await timeout(10000);
388
assert.deepStrictEqual(ctx.context.getAndClearViewStates(), (['foo[ bar]']));
389
assert.deepStrictEqual(provider.getAndClearCallHistory(), ([{ position: '(1,4)', triggerKind: 0, text: 'foo' }]));
390
}
391
);
392
});
393
394
test('Support backward instability', async function () {
395
// The user deletes text and the suggestion changes
396
const provider = new MockInlineCompletionsProvider();
397
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
398
{ fakeClock: true, provider },
399
async ({ editor, editorViewModel, model, context }) => {
400
context.keyboardType('fooba');
401
402
provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 6) });
403
404
model.triggerExplicitly();
405
await timeout(1000);
406
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
407
{ position: '(1,6)', text: 'fooba', triggerKind: 1, }
408
]);
409
assert.deepStrictEqual(context.getAndClearViewStates(), ['', 'fooba[r]']);
410
411
provider.setReturnValue({ insertText: 'foobaz', range: new Range(1, 1, 1, 5) });
412
context.leftDelete();
413
await timeout(1000);
414
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
415
{ position: '(1,5)', text: 'foob', triggerKind: 0, }
416
]);
417
assert.deepStrictEqual(context.getAndClearViewStates(), [
418
'foob[ar]',
419
'foob[az]'
420
]);
421
}
422
);
423
});
424
425
test('Push item to preserve to front', async function () {
426
const provider = new MockInlineCompletionsProvider(true);
427
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
428
{ fakeClock: true, provider },
429
async ({ editor, editorViewModel, model, context }) => {
430
provider.setReturnValue({ insertText: 'foobar', range: new Range(1, 1, 1, 4) });
431
context.keyboardType('foo');
432
await timeout(1000);
433
434
assert.deepStrictEqual(provider.getAndClearCallHistory(), ([
435
{
436
position: '(1,4)',
437
triggerKind: 0,
438
text: 'foo'
439
}
440
]));
441
assert.deepStrictEqual(context.getAndClearViewStates(),
442
([
443
'',
444
'foo[bar]'
445
])
446
);
447
448
provider.setReturnValues([{ insertText: 'foobar1', range: new Range(1, 1, 1, 4) }, { insertText: 'foobar', range: new Range(1, 1, 1, 4) }]);
449
450
await model.triggerExplicitly();
451
await timeout(1000);
452
453
assert.deepStrictEqual(provider.getAndClearCallHistory(), ([
454
{
455
position: '(1,4)',
456
triggerKind: 1,
457
text: 'foo'
458
}
459
]));
460
assert.deepStrictEqual(context.getAndClearViewStates(),
461
([])
462
);
463
}
464
);
465
});
466
});
467
468
test('No race conditions', async function () {
469
const provider = new MockInlineCompletionsProvider();
470
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
471
{ fakeClock: true, provider, },
472
async ({ editor, editorViewModel, model, context }) => {
473
context.keyboardType('h');
474
provider.setReturnValue({ insertText: 'helloworld', range: new Range(1, 1, 1, 2) }, 1000);
475
476
model.triggerExplicitly();
477
478
await timeout(1030);
479
context.keyboardType('ello');
480
provider.setReturnValue({ insertText: 'helloworld', range: new Range(1, 1, 1, 6) }, 1000);
481
482
// after 20ms: Inline completion provider answers back
483
// after 50ms: Debounce is triggered
484
await timeout(2000);
485
486
assert.deepStrictEqual(context.getAndClearViewStates(), [
487
'',
488
'hello[world]',
489
]);
490
});
491
});
492
493
test('Do not reuse cache from previous session (#132516)', async function () {
494
const provider = new MockInlineCompletionsProvider();
495
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
496
{ fakeClock: true, provider, inlineSuggest: { enabled: true } },
497
async ({ editor, editorViewModel, model, context }) => {
498
context.keyboardType('hello\n');
499
context.cursorLeft();
500
context.keyboardType('x');
501
context.leftDelete();
502
provider.setReturnValue({ insertText: 'helloworld', range: new Range(1, 1, 1, 6) }, 1000);
503
await timeout(2000);
504
505
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
506
{
507
position: '(1,6)',
508
text: 'hello\n',
509
triggerKind: 0,
510
}
511
]);
512
513
provider.setReturnValue({ insertText: 'helloworld', range: new Range(2, 1, 2, 6) }, 1000);
514
515
context.cursorDown();
516
context.keyboardType('hello');
517
await timeout(40);
518
519
assert.deepStrictEqual(provider.getAndClearCallHistory(), []);
520
521
// Update ghost text
522
context.keyboardType('w');
523
context.leftDelete();
524
525
await timeout(2000);
526
527
assert.deepStrictEqual(provider.getAndClearCallHistory(), [
528
{ position: '(2,6)', triggerKind: 0, text: 'hello\nhello' },
529
]);
530
531
assert.deepStrictEqual(context.getAndClearViewStates(), [
532
'',
533
'hello[world]\n',
534
'hello\n',
535
'hello\nhello[world]',
536
]);
537
});
538
});
539
540
test('Additional Text Edits', async function () {
541
const provider = new MockInlineCompletionsProvider();
542
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
543
{ fakeClock: true, provider },
544
async ({ editor, editorViewModel, model, context }) => {
545
context.keyboardType('buzz\nbaz');
546
provider.setReturnValue({
547
insertText: 'bazz',
548
range: new Range(2, 1, 2, 4),
549
additionalTextEdits: [{
550
range: new Range(1, 1, 1, 5),
551
text: 'bla'
552
}],
553
});
554
model.triggerExplicitly();
555
await timeout(1000);
556
557
model.accept(editor);
558
559
assert.deepStrictEqual(provider.getAndClearCallHistory(), ([{ position: '(2,4)', triggerKind: 1, text: 'buzz\nbaz' }]));
560
561
assert.deepStrictEqual(context.getAndClearViewStates(), [
562
'',
563
'buzz\nbaz[z]',
564
'bla\nbazz',
565
]);
566
}
567
);
568
});
569
});
570
571
suite('Multi Cursor Support', () => {
572
ensureNoDisposablesAreLeakedInTestSuite();
573
574
test('Basic', async function () {
575
const provider = new MockInlineCompletionsProvider();
576
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
577
{ fakeClock: true, provider },
578
async ({ editor, editorViewModel, model, context }) => {
579
context.keyboardType('console\nconsole\n');
580
editor.setSelections([
581
new Selection(1, 1000, 1, 1000),
582
new Selection(2, 1000, 2, 1000),
583
]);
584
provider.setReturnValue({
585
insertText: 'console.log("hello");',
586
range: new Range(1, 1, 1, 1000),
587
});
588
model.triggerExplicitly();
589
await timeout(1000);
590
model.accept(editor);
591
assert.deepStrictEqual(
592
editor.getValue(),
593
[
594
`console.log("hello");`,
595
`console.log("hello");`,
596
``
597
].join('\n')
598
);
599
}
600
);
601
});
602
603
test('Multi Part', async function () {
604
const provider = new MockInlineCompletionsProvider();
605
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
606
{ fakeClock: true, provider },
607
async ({ editor, editorViewModel, model, context }) => {
608
context.keyboardType('console.log()\nconsole.log\n');
609
editor.setSelections([
610
new Selection(1, 12, 1, 12),
611
new Selection(2, 1000, 2, 1000),
612
]);
613
provider.setReturnValue({
614
insertText: 'console.log("hello");',
615
range: new Range(1, 1, 1, 1000),
616
});
617
model.triggerExplicitly();
618
await timeout(1000);
619
model.accept(editor);
620
assert.deepStrictEqual(
621
editor.getValue(),
622
[
623
`console.log("hello");`,
624
`console.log`,
625
``
626
].join('\n')
627
);
628
}
629
);
630
});
631
632
test('Multi Part and Different Cursor Columns', async function () {
633
const provider = new MockInlineCompletionsProvider();
634
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
635
{ fakeClock: true, provider },
636
async ({ editor, editorViewModel, model, context }) => {
637
context.keyboardType('console.log()\nconsole.warn\n');
638
editor.setSelections([
639
new Selection(1, 12, 1, 12),
640
new Selection(2, 14, 2, 14),
641
]);
642
provider.setReturnValue({
643
insertText: 'console.log("hello");',
644
range: new Range(1, 1, 1, 1000),
645
});
646
model.triggerExplicitly();
647
await timeout(1000);
648
model.accept(editor);
649
assert.deepStrictEqual(
650
editor.getValue(),
651
[
652
`console.log("hello");`,
653
`console.warn`,
654
``
655
].join('\n')
656
);
657
}
658
);
659
});
660
661
async function acceptNextWord(model: InlineCompletionsModel, editor: ITestCodeEditor, timesToAccept: number = 1): Promise<void> {
662
for (let i = 0; i < timesToAccept; i++) {
663
model.triggerExplicitly();
664
await timeout(1000);
665
await model.acceptNextWord();
666
}
667
}
668
669
test('Basic Partial Completion', async function () {
670
const provider = new MockInlineCompletionsProvider();
671
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
672
{ fakeClock: true, provider },
673
async ({ editor, editorViewModel, model, context }) => {
674
context.keyboardType('let\nlet\n');
675
editor.setSelections([
676
new Selection(1, 1000, 1, 1000),
677
new Selection(2, 1000, 2, 1000),
678
]);
679
680
provider.setReturnValue({
681
insertText: `let a = 'some word'; `,
682
range: new Range(1, 1, 1, 1000),
683
});
684
685
await acceptNextWord(model, editor, 2);
686
687
assert.deepStrictEqual(
688
editor.getValue(),
689
[
690
`let a`,
691
`let a`,
692
``
693
].join('\n')
694
);
695
}
696
);
697
});
698
699
test('Partial Multi-Part Completion', async function () {
700
const provider = new MockInlineCompletionsProvider();
701
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
702
{ fakeClock: true, provider },
703
async ({ editor, editorViewModel, model, context }) => {
704
context.keyboardType('for ()\nfor \n');
705
editor.setSelections([
706
new Selection(1, 5, 1, 5),
707
new Selection(2, 1000, 2, 1000),
708
]);
709
710
provider.setReturnValue({
711
insertText: `for (let i = 0; i < 10; i++) {`,
712
range: new Range(1, 1, 1, 1000),
713
});
714
715
model.triggerExplicitly();
716
await timeout(1000);
717
718
await acceptNextWord(model, editor, 3);
719
720
assert.deepStrictEqual(
721
editor.getValue(),
722
[
723
`for (let i)`,
724
`for `,
725
``
726
].join('\n')
727
);
728
}
729
);
730
});
731
732
test('Partial Mutli-Part and Different Cursor Columns Completion', async function () {
733
const provider = new MockInlineCompletionsProvider();
734
await withAsyncTestCodeEditorAndInlineCompletionsModel('',
735
{ fakeClock: true, provider },
736
async ({ editor, editorViewModel, model, context }) => {
737
context.keyboardType(`console.log()\nconsole.warnnnn\n`);
738
editor.setSelections([
739
new Selection(1, 12, 1, 12),
740
new Selection(2, 16, 2, 16),
741
]);
742
743
provider.setReturnValue({
744
insertText: `console.log("hello" + " " + "world");`,
745
range: new Range(1, 1, 1, 1000),
746
});
747
748
model.triggerExplicitly();
749
await timeout(1000);
750
751
await acceptNextWord(model, editor, 4);
752
753
assert.deepStrictEqual(
754
editor.getValue(),
755
[
756
`console.log("hello" + )`,
757
`console.warnnnn`,
758
``
759
].join('\n')
760
);
761
}
762
);
763
});
764
});
765
766