Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts
5263 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 { Event } from '../../../../../base/common/event.js';
7
import { Disposable, DisposableStore, toDisposable } from '../../../../../base/common/lifecycle.js';
8
import { URI } from '../../../../../base/common/uri.js';
9
import { mock } from '../../../../../base/test/common/mock.js';
10
import { CoreEditingCommands } from '../../../../browser/coreCommands.js';
11
import { EditOperation } from '../../../../common/core/editOperation.js';
12
import { Position } from '../../../../common/core/position.js';
13
import { Range } from '../../../../common/core/range.js';
14
import { Selection } from '../../../../common/core/selection.js';
15
import { Handler } from '../../../../common/editorCommon.js';
16
import { ITextModel } from '../../../../common/model.js';
17
import { TextModel } from '../../../../common/model/textModel.js';
18
import { CompletionItemKind, CompletionItemProvider, CompletionList, CompletionTriggerKind, EncodedTokenizationResult, InlineCompletionsProvider, IState, TokenizationRegistry } from '../../../../common/languages.js';
19
import { MetadataConsts } from '../../../../common/encodedTokenAttributes.js';
20
import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js';
21
import { NullState } from '../../../../common/languages/nullTokenize.js';
22
import { ILanguageService } from '../../../../common/languages/language.js';
23
import { SnippetController2 } from '../../../snippet/browser/snippetController2.js';
24
import { SuggestController } from '../../browser/suggestController.js';
25
import { ISuggestMemoryService } from '../../browser/suggestMemory.js';
26
import { LineContext, SuggestModel } from '../../browser/suggestModel.js';
27
import { ISelectedSuggestion } from '../../browser/suggestWidget.js';
28
import { createTestCodeEditor, ITestCodeEditor } from '../../../../test/browser/testCodeEditor.js';
29
import { createModelServices, createTextModel, instantiateTextModel } from '../../../../test/common/testTextModel.js';
30
import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js';
31
import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';
32
import { MockKeybindingService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js';
33
import { ILabelService } from '../../../../../platform/label/common/label.js';
34
import { InMemoryStorageService, IStorageService } from '../../../../../platform/storage/common/storage.js';
35
import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';
36
import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js';
37
import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';
38
import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js';
39
import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js';
40
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
41
import { getSnippetSuggestSupport, setSnippetSuggestSupport } from '../../browser/suggest.js';
42
import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js';
43
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
44
import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js';
45
46
47
function createMockEditor(model: TextModel, languageFeaturesService: ILanguageFeaturesService): ITestCodeEditor {
48
49
const storeService = new InMemoryStorageService();
50
const editor = createTestCodeEditor(model, {
51
serviceCollection: new ServiceCollection(
52
[ILanguageFeaturesService, languageFeaturesService],
53
[ITelemetryService, NullTelemetryService],
54
[IStorageService, storeService],
55
[IKeybindingService, new MockKeybindingService()],
56
[ISuggestMemoryService, new class implements ISuggestMemoryService {
57
declare readonly _serviceBrand: undefined;
58
memorize(): void {
59
}
60
select(): number {
61
return -1;
62
}
63
}],
64
[ILabelService, new class extends mock<ILabelService>() { }],
65
[IWorkspaceContextService, new class extends mock<IWorkspaceContextService>() { }],
66
[IEnvironmentService, new class extends mock<IEnvironmentService>() {
67
override isBuilt: boolean = true;
68
override isExtensionDevelopment: boolean = false;
69
}],
70
),
71
});
72
const ctrl = editor.registerAndInstantiateContribution(SnippetController2.ID, SnippetController2);
73
editor.hasWidgetFocus = () => true;
74
75
editor.registerDisposable(ctrl);
76
editor.registerDisposable(storeService);
77
return editor;
78
}
79
80
suite('SuggestModel - Context', function () {
81
const OUTER_LANGUAGE_ID = 'outerMode';
82
const INNER_LANGUAGE_ID = 'innerMode';
83
84
class OuterMode extends Disposable {
85
public readonly languageId = OUTER_LANGUAGE_ID;
86
constructor(
87
@ILanguageService languageService: ILanguageService,
88
@ILanguageConfigurationService languageConfigurationService: ILanguageConfigurationService,
89
) {
90
super();
91
this._register(languageService.registerLanguage({ id: this.languageId }));
92
this._register(languageConfigurationService.register(this.languageId, {}));
93
94
this._register(TokenizationRegistry.register(this.languageId, {
95
getInitialState: (): IState => NullState,
96
tokenize: undefined!,
97
tokenizeEncoded: (line: string, hasEOL: boolean, state: IState): EncodedTokenizationResult => {
98
const tokensArr: number[] = [];
99
let prevLanguageId: string | undefined = undefined;
100
for (let i = 0; i < line.length; i++) {
101
const languageId = (line.charAt(i) === 'x' ? INNER_LANGUAGE_ID : OUTER_LANGUAGE_ID);
102
const encodedLanguageId = languageService.languageIdCodec.encodeLanguageId(languageId);
103
if (prevLanguageId !== languageId) {
104
tokensArr.push(i);
105
tokensArr.push((encodedLanguageId << MetadataConsts.LANGUAGEID_OFFSET));
106
}
107
prevLanguageId = languageId;
108
}
109
110
const tokens = new Uint32Array(tokensArr.length);
111
for (let i = 0; i < tokens.length; i++) {
112
tokens[i] = tokensArr[i];
113
}
114
return new EncodedTokenizationResult(tokens, [], state);
115
}
116
}));
117
}
118
}
119
120
class InnerMode extends Disposable {
121
public readonly languageId = INNER_LANGUAGE_ID;
122
constructor(
123
@ILanguageService languageService: ILanguageService,
124
@ILanguageConfigurationService languageConfigurationService: ILanguageConfigurationService
125
) {
126
super();
127
this._register(languageService.registerLanguage({ id: this.languageId }));
128
this._register(languageConfigurationService.register(this.languageId, {}));
129
}
130
}
131
132
const assertAutoTrigger = (model: TextModel, offset: number, expected: boolean, message?: string): void => {
133
const pos = model.getPositionAt(offset);
134
const editor = createMockEditor(model, new LanguageFeaturesService());
135
editor.setPosition(pos);
136
assert.strictEqual(LineContext.shouldAutoTrigger(editor), expected, message);
137
editor.dispose();
138
};
139
140
let disposables: DisposableStore;
141
142
setup(() => {
143
disposables = new DisposableStore();
144
});
145
146
teardown(function () {
147
disposables.dispose();
148
});
149
150
ensureNoDisposablesAreLeakedInTestSuite();
151
152
test('Context - shouldAutoTrigger', function () {
153
const model = createTextModel('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?');
154
disposables.add(model);
155
156
assertAutoTrigger(model, 3, true, 'end of word, Das|');
157
assertAutoTrigger(model, 4, false, 'no word Das |');
158
assertAutoTrigger(model, 1, true, 'typing a single character before a word: D|as');
159
assertAutoTrigger(model, 55, false, 'number, 1861|');
160
model.dispose();
161
});
162
163
test('shouldAutoTrigger at embedded language boundaries', () => {
164
const disposables = new DisposableStore();
165
const instantiationService = createModelServices(disposables);
166
const outerMode = disposables.add(instantiationService.createInstance(OuterMode));
167
disposables.add(instantiationService.createInstance(InnerMode));
168
169
const model = disposables.add(instantiateTextModel(instantiationService, 'a<xx>a<x>', outerMode.languageId));
170
171
assertAutoTrigger(model, 1, true, 'a|<x — should trigger at end of word');
172
assertAutoTrigger(model, 2, false, 'a<|x — should NOT trigger at start of word');
173
assertAutoTrigger(model, 3, true, 'a<x|x — should trigger after typing a single character before a word');
174
assertAutoTrigger(model, 4, true, 'a<xx|> — should trigger at boundary between languages');
175
assertAutoTrigger(model, 5, false, 'a<xx>|a — should NOT trigger at start of word');
176
assertAutoTrigger(model, 6, true, 'a<xx>a|< — should trigger at end of word');
177
assertAutoTrigger(model, 8, true, 'a<xx>a<x|> — should trigger at end of word at boundary');
178
179
disposables.dispose();
180
});
181
});
182
183
suite('SuggestModel - TriggerAndCancelOracle', function () {
184
185
186
function getDefaultSuggestRange(model: ITextModel, position: Position) {
187
const wordUntil = model.getWordUntilPosition(position);
188
return new Range(position.lineNumber, wordUntil.startColumn, position.lineNumber, wordUntil.endColumn);
189
}
190
191
const alwaysEmptySupport: CompletionItemProvider = {
192
_debugDisplayName: 'test',
193
provideCompletionItems(doc, pos): CompletionList {
194
return {
195
incomplete: false,
196
suggestions: []
197
};
198
}
199
};
200
201
const alwaysSomethingSupport: CompletionItemProvider = {
202
_debugDisplayName: 'test',
203
provideCompletionItems(doc, pos): CompletionList {
204
return {
205
incomplete: false,
206
suggestions: [{
207
label: doc.getWordUntilPosition(pos).word,
208
kind: CompletionItemKind.Property,
209
insertText: 'foofoo',
210
range: getDefaultSuggestRange(doc, pos)
211
}]
212
};
213
}
214
};
215
216
let disposables: DisposableStore;
217
let model: TextModel;
218
const languageFeaturesService = new LanguageFeaturesService();
219
const registry = languageFeaturesService.completionProvider;
220
221
setup(function () {
222
disposables = new DisposableStore();
223
model = createTextModel('abc def', undefined, undefined, URI.parse('test:somefile.ttt'));
224
disposables.add(model);
225
});
226
227
teardown(() => {
228
disposables.dispose();
229
});
230
231
ensureNoDisposablesAreLeakedInTestSuite();
232
233
function withOracle(callback: (model: SuggestModel, editor: ITestCodeEditor) => any): Promise<any> {
234
235
return new Promise((resolve, reject) => {
236
const editor = createMockEditor(model, languageFeaturesService);
237
const oracle = editor.invokeWithinContext(accessor => accessor.get(IInstantiationService).createInstance(SuggestModel, editor));
238
disposables.add(oracle);
239
disposables.add(editor);
240
241
try {
242
resolve(callback(oracle, editor));
243
} catch (err) {
244
reject(err);
245
}
246
});
247
}
248
249
function assertEvent<E>(event: Event<E>, action: () => any, assert: (e: E) => any) {
250
return new Promise((resolve, reject) => {
251
const sub = event(e => {
252
sub.dispose();
253
try {
254
resolve(assert(e));
255
} catch (err) {
256
reject(err);
257
}
258
});
259
try {
260
action();
261
} catch (err) {
262
sub.dispose();
263
reject(err);
264
}
265
});
266
}
267
268
test('events - cancel/trigger', function () {
269
return withOracle(model => {
270
271
return Promise.all([
272
273
assertEvent(model.onDidTrigger, function () {
274
model.trigger({ auto: true });
275
}, function (event) {
276
assert.strictEqual(event.auto, true);
277
278
return assertEvent(model.onDidCancel, function () {
279
model.cancel();
280
}, function (event) {
281
assert.strictEqual(event.retrigger, false);
282
});
283
}),
284
285
assertEvent(model.onDidTrigger, function () {
286
model.trigger({ auto: true });
287
}, function (event) {
288
assert.strictEqual(event.auto, true);
289
}),
290
291
assertEvent(model.onDidTrigger, function () {
292
model.trigger({ auto: false });
293
}, function (event) {
294
assert.strictEqual(event.auto, false);
295
})
296
]);
297
});
298
});
299
300
301
test('events - suggest/empty', function () {
302
303
disposables.add(registry.register({ scheme: 'test' }, alwaysEmptySupport));
304
305
return withOracle(model => {
306
return Promise.all([
307
assertEvent(model.onDidCancel, function () {
308
model.trigger({ auto: true });
309
}, function (event) {
310
assert.strictEqual(event.retrigger, false);
311
}),
312
assertEvent(model.onDidSuggest, function () {
313
model.trigger({ auto: false });
314
}, function (event) {
315
assert.strictEqual(event.triggerOptions.auto, false);
316
assert.strictEqual(event.isFrozen, false);
317
assert.strictEqual(event.completionModel.items.length, 0);
318
})
319
]);
320
});
321
});
322
323
test('trigger - on type', function () {
324
325
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
326
327
return withOracle((model, editor) => {
328
return assertEvent(model.onDidSuggest, () => {
329
editor.setPosition({ lineNumber: 1, column: 4 });
330
editor.trigger('keyboard', Handler.Type, { text: 'd' });
331
332
}, event => {
333
assert.strictEqual(event.triggerOptions.auto, true);
334
assert.strictEqual(event.completionModel.items.length, 1);
335
const [first] = event.completionModel.items;
336
337
assert.strictEqual(first.provider, alwaysSomethingSupport);
338
});
339
});
340
});
341
342
test('#17400: Keep filtering suggestModel.ts after space', function () {
343
344
disposables.add(registry.register({ scheme: 'test' }, {
345
_debugDisplayName: 'test',
346
provideCompletionItems(doc, pos): CompletionList {
347
return {
348
incomplete: false,
349
suggestions: [{
350
label: 'My Table',
351
kind: CompletionItemKind.Property,
352
insertText: 'My Table',
353
range: getDefaultSuggestRange(doc, pos)
354
}]
355
};
356
}
357
}));
358
359
model.setValue('');
360
361
return withOracle((model, editor) => {
362
363
return assertEvent(model.onDidSuggest, () => {
364
// make sure completionModel starts here!
365
model.trigger({ auto: true });
366
}, event => {
367
368
return assertEvent(model.onDidSuggest, () => {
369
editor.setPosition({ lineNumber: 1, column: 1 });
370
editor.trigger('keyboard', Handler.Type, { text: 'My' });
371
372
}, event => {
373
assert.strictEqual(event.triggerOptions.auto, true);
374
assert.strictEqual(event.completionModel.items.length, 1);
375
const [first] = event.completionModel.items;
376
assert.strictEqual(first.completion.label, 'My Table');
377
378
return assertEvent(model.onDidSuggest, () => {
379
editor.setPosition({ lineNumber: 1, column: 3 });
380
editor.trigger('keyboard', Handler.Type, { text: ' ' });
381
382
}, event => {
383
assert.strictEqual(event.triggerOptions.auto, true);
384
assert.strictEqual(event.completionModel.items.length, 1);
385
const [first] = event.completionModel.items;
386
assert.strictEqual(first.completion.label, 'My Table');
387
});
388
});
389
});
390
});
391
});
392
393
test('#21484: Trigger character always force a new completion session', function () {
394
395
disposables.add(registry.register({ scheme: 'test' }, {
396
_debugDisplayName: 'test',
397
provideCompletionItems(doc, pos): CompletionList {
398
return {
399
incomplete: false,
400
suggestions: [{
401
label: 'foo.bar',
402
kind: CompletionItemKind.Property,
403
insertText: 'foo.bar',
404
range: Range.fromPositions(pos.with(undefined, 1), pos)
405
}]
406
};
407
}
408
}));
409
410
disposables.add(registry.register({ scheme: 'test' }, {
411
_debugDisplayName: 'test',
412
triggerCharacters: ['.'],
413
provideCompletionItems(doc, pos): CompletionList {
414
return {
415
incomplete: false,
416
suggestions: [{
417
label: 'boom',
418
kind: CompletionItemKind.Property,
419
insertText: 'boom',
420
range: Range.fromPositions(
421
pos.delta(0, doc.getLineContent(pos.lineNumber)[pos.column - 2] === '.' ? 0 : -1),
422
pos
423
)
424
}]
425
};
426
}
427
}));
428
429
model.setValue('');
430
431
return withOracle(async (model, editor) => {
432
433
await assertEvent(model.onDidSuggest, () => {
434
editor.setPosition({ lineNumber: 1, column: 1 });
435
editor.trigger('keyboard', Handler.Type, { text: 'foo' });
436
437
}, event => {
438
assert.strictEqual(event.triggerOptions.auto, true);
439
assert.strictEqual(event.completionModel.items.length, 1);
440
const [first] = event.completionModel.items;
441
assert.strictEqual(first.completion.label, 'foo.bar');
442
443
});
444
445
await assertEvent(model.onDidSuggest, () => {
446
editor.trigger('keyboard', Handler.Type, { text: '.' });
447
448
}, event => {
449
// SYNC
450
assert.strictEqual(event.triggerOptions.auto, true);
451
assert.strictEqual(event.completionModel.items.length, 1);
452
const [first] = event.completionModel.items;
453
assert.strictEqual(first.completion.label, 'foo.bar');
454
});
455
456
await assertEvent(model.onDidSuggest, () => {
457
// nothing -> triggered by the trigger character typing (see above)
458
459
}, event => {
460
// ASYNC
461
assert.strictEqual(event.triggerOptions.auto, true);
462
assert.strictEqual(event.completionModel.items.length, 2);
463
const [first, second] = event.completionModel.items;
464
assert.strictEqual(first.completion.label, 'foo.bar');
465
assert.strictEqual(second.completion.label, 'boom');
466
});
467
});
468
});
469
470
test('Intellisense Completion doesn\'t respect space after equal sign (.html file), #29353 [1/2]', function () {
471
472
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
473
474
return withOracle((model, editor) => {
475
476
editor.getModel()!.setValue('fo');
477
editor.setPosition({ lineNumber: 1, column: 3 });
478
479
return assertEvent(model.onDidSuggest, () => {
480
model.trigger({ auto: false });
481
}, event => {
482
assert.strictEqual(event.triggerOptions.auto, false);
483
assert.strictEqual(event.isFrozen, false);
484
assert.strictEqual(event.completionModel.items.length, 1);
485
486
return assertEvent(model.onDidCancel, () => {
487
editor.trigger('keyboard', Handler.Type, { text: '+' });
488
}, event => {
489
assert.strictEqual(event.retrigger, false);
490
});
491
});
492
});
493
});
494
495
test('Intellisense Completion doesn\'t respect space after equal sign (.html file), #29353 [2/2]', function () {
496
497
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
498
499
return withOracle((model, editor) => {
500
501
editor.getModel()!.setValue('fo');
502
editor.setPosition({ lineNumber: 1, column: 3 });
503
504
return assertEvent(model.onDidSuggest, () => {
505
model.trigger({ auto: false });
506
}, event => {
507
assert.strictEqual(event.triggerOptions.auto, false);
508
assert.strictEqual(event.isFrozen, false);
509
assert.strictEqual(event.completionModel.items.length, 1);
510
511
return assertEvent(model.onDidCancel, () => {
512
editor.trigger('keyboard', Handler.Type, { text: ' ' });
513
}, event => {
514
assert.strictEqual(event.retrigger, false);
515
});
516
});
517
});
518
});
519
520
test('Incomplete suggestion results cause re-triggering when typing w/o further context, #28400 (1/2)', function () {
521
522
disposables.add(registry.register({ scheme: 'test' }, {
523
_debugDisplayName: 'test',
524
provideCompletionItems(doc, pos): CompletionList {
525
return {
526
incomplete: true,
527
suggestions: [{
528
label: 'foo',
529
kind: CompletionItemKind.Property,
530
insertText: 'foo',
531
range: Range.fromPositions(pos.with(undefined, 1), pos)
532
}]
533
};
534
}
535
}));
536
537
return withOracle((model, editor) => {
538
539
editor.getModel()!.setValue('foo');
540
editor.setPosition({ lineNumber: 1, column: 4 });
541
542
return assertEvent(model.onDidSuggest, () => {
543
model.trigger({ auto: false });
544
}, event => {
545
assert.strictEqual(event.triggerOptions.auto, false);
546
assert.strictEqual(event.completionModel.getIncompleteProvider().size, 1);
547
assert.strictEqual(event.completionModel.items.length, 1);
548
549
return assertEvent(model.onDidCancel, () => {
550
editor.trigger('keyboard', Handler.Type, { text: ';' });
551
}, event => {
552
assert.strictEqual(event.retrigger, false);
553
});
554
});
555
});
556
});
557
558
test('Incomplete suggestion results cause re-triggering when typing w/o further context, #28400 (2/2)', function () {
559
560
disposables.add(registry.register({ scheme: 'test' }, {
561
_debugDisplayName: 'test',
562
provideCompletionItems(doc, pos): CompletionList {
563
return {
564
incomplete: true,
565
suggestions: [{
566
label: 'foo;',
567
kind: CompletionItemKind.Property,
568
insertText: 'foo',
569
range: Range.fromPositions(pos.with(undefined, 1), pos)
570
}]
571
};
572
}
573
}));
574
575
return withOracle((model, editor) => {
576
577
editor.getModel()!.setValue('foo');
578
editor.setPosition({ lineNumber: 1, column: 4 });
579
580
return assertEvent(model.onDidSuggest, () => {
581
model.trigger({ auto: false });
582
}, event => {
583
assert.strictEqual(event.triggerOptions.auto, false);
584
assert.strictEqual(event.completionModel.getIncompleteProvider().size, 1);
585
assert.strictEqual(event.completionModel.items.length, 1);
586
587
return assertEvent(model.onDidSuggest, () => {
588
// while we cancel incrementally enriching the set of
589
// completions we still filter against those that we have
590
// until now
591
editor.trigger('keyboard', Handler.Type, { text: ';' });
592
}, event => {
593
assert.strictEqual(event.triggerOptions.auto, false);
594
assert.strictEqual(event.completionModel.getIncompleteProvider().size, 1);
595
assert.strictEqual(event.completionModel.items.length, 1);
596
597
});
598
});
599
});
600
});
601
602
test('Trigger character is provided in suggest context', function () {
603
let triggerCharacter = '';
604
disposables.add(registry.register({ scheme: 'test' }, {
605
_debugDisplayName: 'test',
606
triggerCharacters: ['.'],
607
provideCompletionItems(doc, pos, context): CompletionList {
608
assert.strictEqual(context.triggerKind, CompletionTriggerKind.TriggerCharacter);
609
triggerCharacter = context.triggerCharacter!;
610
return {
611
incomplete: false,
612
suggestions: [
613
{
614
label: 'foo.bar',
615
kind: CompletionItemKind.Property,
616
insertText: 'foo.bar',
617
range: Range.fromPositions(pos.with(undefined, 1), pos)
618
}
619
]
620
};
621
}
622
}));
623
624
model.setValue('');
625
626
return withOracle((model, editor) => {
627
628
return assertEvent(model.onDidSuggest, () => {
629
editor.setPosition({ lineNumber: 1, column: 1 });
630
editor.trigger('keyboard', Handler.Type, { text: 'foo.' });
631
}, event => {
632
assert.strictEqual(triggerCharacter, '.');
633
});
634
});
635
});
636
637
test('Mac press and hold accent character insertion does not update suggestions, #35269', function () {
638
disposables.add(registry.register({ scheme: 'test' }, {
639
_debugDisplayName: 'test',
640
provideCompletionItems(doc, pos): CompletionList {
641
return {
642
incomplete: true,
643
suggestions: [{
644
label: 'abc',
645
kind: CompletionItemKind.Property,
646
insertText: 'abc',
647
range: Range.fromPositions(pos.with(undefined, 1), pos)
648
}, {
649
label: 'äbc',
650
kind: CompletionItemKind.Property,
651
insertText: 'äbc',
652
range: Range.fromPositions(pos.with(undefined, 1), pos)
653
}]
654
};
655
}
656
}));
657
658
model.setValue('');
659
return withOracle((model, editor) => {
660
661
return assertEvent(model.onDidSuggest, () => {
662
editor.setPosition({ lineNumber: 1, column: 1 });
663
editor.trigger('keyboard', Handler.Type, { text: 'a' });
664
}, event => {
665
assert.strictEqual(event.completionModel.items.length, 1);
666
assert.strictEqual(event.completionModel.items[0].completion.label, 'abc');
667
668
return assertEvent(model.onDidSuggest, () => {
669
editor.executeEdits('test', [EditOperation.replace(new Range(1, 1, 1, 2), 'ä')]);
670
671
}, event => {
672
// suggest model changed to äbc
673
assert.strictEqual(event.completionModel.items.length, 1);
674
assert.strictEqual(event.completionModel.items[0].completion.label, 'äbc');
675
676
});
677
});
678
});
679
});
680
681
test('Backspace should not always cancel code completion, #36491', function () {
682
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
683
684
return withOracle(async (model, editor) => {
685
await assertEvent(model.onDidSuggest, () => {
686
editor.setPosition({ lineNumber: 1, column: 4 });
687
editor.trigger('keyboard', Handler.Type, { text: 'd' });
688
689
}, event => {
690
assert.strictEqual(event.triggerOptions.auto, true);
691
assert.strictEqual(event.completionModel.items.length, 1);
692
const [first] = event.completionModel.items;
693
694
assert.strictEqual(first.provider, alwaysSomethingSupport);
695
});
696
697
await assertEvent(model.onDidSuggest, () => {
698
editor.runCommand(CoreEditingCommands.DeleteLeft, null);
699
700
}, event => {
701
assert.strictEqual(event.triggerOptions.auto, true);
702
assert.strictEqual(event.completionModel.items.length, 1);
703
const [first] = event.completionModel.items;
704
705
assert.strictEqual(first.provider, alwaysSomethingSupport);
706
});
707
});
708
});
709
710
test('Text changes for completion CodeAction are affected by the completion #39893', function () {
711
disposables.add(registry.register({ scheme: 'test' }, {
712
_debugDisplayName: 'test',
713
provideCompletionItems(doc, pos): CompletionList {
714
return {
715
incomplete: true,
716
suggestions: [{
717
label: 'bar',
718
kind: CompletionItemKind.Property,
719
insertText: 'bar',
720
range: Range.fromPositions(pos.delta(0, -2), pos),
721
additionalTextEdits: [{
722
text: ', bar',
723
range: { startLineNumber: 1, endLineNumber: 1, startColumn: 17, endColumn: 17 }
724
}]
725
}]
726
};
727
}
728
}));
729
730
model.setValue('ba; import { foo } from "./b"');
731
732
return withOracle(async (sugget, editor) => {
733
class TestCtrl extends SuggestController {
734
_insertSuggestion_publicForTest(item: ISelectedSuggestion, flags: number = 0) {
735
super._insertSuggestion(item, flags);
736
}
737
}
738
const ctrl = <TestCtrl>editor.registerAndInstantiateContribution(TestCtrl.ID, TestCtrl);
739
editor.registerAndInstantiateContribution(SnippetController2.ID, SnippetController2);
740
741
await assertEvent(sugget.onDidSuggest, () => {
742
editor.setPosition({ lineNumber: 1, column: 3 });
743
sugget.trigger({ auto: false });
744
}, event => {
745
746
assert.strictEqual(event.completionModel.items.length, 1);
747
const [first] = event.completionModel.items;
748
assert.strictEqual(first.completion.label, 'bar');
749
750
ctrl._insertSuggestion_publicForTest({ item: first, index: 0, model: event.completionModel });
751
});
752
753
assert.strictEqual(
754
model.getValue(),
755
'bar; import { foo, bar } from "./b"'
756
);
757
});
758
});
759
760
test('Completion unexpectedly triggers on second keypress of an edit group in a snippet #43523', function () {
761
762
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
763
764
return withOracle((model, editor) => {
765
return assertEvent(model.onDidSuggest, () => {
766
editor.setValue('d');
767
editor.setSelection(new Selection(1, 1, 1, 2));
768
editor.trigger('keyboard', Handler.Type, { text: 'e' });
769
770
}, event => {
771
assert.strictEqual(event.triggerOptions.auto, true);
772
assert.strictEqual(event.completionModel.items.length, 1);
773
const [first] = event.completionModel.items;
774
775
assert.strictEqual(first.provider, alwaysSomethingSupport);
776
});
777
});
778
});
779
780
781
test('Fails to render completion details #47988', function () {
782
783
let disposeA = 0;
784
let disposeB = 0;
785
786
disposables.add(registry.register({ scheme: 'test' }, {
787
_debugDisplayName: 'test',
788
provideCompletionItems(doc, pos) {
789
return {
790
incomplete: true,
791
suggestions: [{
792
kind: CompletionItemKind.Folder,
793
label: 'CompleteNot',
794
insertText: 'Incomplete',
795
sortText: 'a',
796
range: getDefaultSuggestRange(doc, pos)
797
}],
798
dispose() { disposeA += 1; }
799
};
800
}
801
}));
802
disposables.add(registry.register({ scheme: 'test' }, {
803
_debugDisplayName: 'test',
804
provideCompletionItems(doc, pos) {
805
return {
806
incomplete: false,
807
suggestions: [{
808
kind: CompletionItemKind.Folder,
809
label: 'Complete',
810
insertText: 'Complete',
811
sortText: 'z',
812
range: getDefaultSuggestRange(doc, pos)
813
}],
814
dispose() { disposeB += 1; }
815
};
816
},
817
resolveCompletionItem(item) {
818
return item;
819
},
820
}));
821
822
return withOracle(async (model, editor) => {
823
824
await assertEvent(model.onDidSuggest, () => {
825
editor.setValue('');
826
editor.setSelection(new Selection(1, 1, 1, 1));
827
editor.trigger('keyboard', Handler.Type, { text: 'c' });
828
829
}, event => {
830
assert.strictEqual(event.triggerOptions.auto, true);
831
assert.strictEqual(event.completionModel.items.length, 2);
832
assert.strictEqual(disposeA, 0);
833
assert.strictEqual(disposeB, 0);
834
});
835
836
await assertEvent(model.onDidSuggest, () => {
837
editor.trigger('keyboard', Handler.Type, { text: 'o' });
838
}, event => {
839
assert.strictEqual(event.triggerOptions.auto, true);
840
assert.strictEqual(event.completionModel.items.length, 2);
841
842
// clean up
843
model.clear();
844
assert.strictEqual(disposeA, 2); // provide got called two times!
845
assert.strictEqual(disposeB, 1);
846
});
847
848
});
849
});
850
851
852
test('Trigger (full) completions when (incomplete) completions are already active #99504', function () {
853
854
let countA = 0;
855
let countB = 0;
856
857
disposables.add(registry.register({ scheme: 'test' }, {
858
_debugDisplayName: 'test',
859
provideCompletionItems(doc, pos) {
860
countA += 1;
861
return {
862
incomplete: false, // doesn't matter if incomplete or not
863
suggestions: [{
864
kind: CompletionItemKind.Class,
865
label: 'Z aaa',
866
insertText: 'Z aaa',
867
range: new Range(1, 1, pos.lineNumber, pos.column)
868
}],
869
};
870
}
871
}));
872
disposables.add(registry.register({ scheme: 'test' }, {
873
_debugDisplayName: 'test',
874
provideCompletionItems(doc, pos) {
875
countB += 1;
876
if (!doc.getWordUntilPosition(pos).word.startsWith('a')) {
877
return;
878
}
879
return {
880
incomplete: false,
881
suggestions: [{
882
kind: CompletionItemKind.Folder,
883
label: 'aaa',
884
insertText: 'aaa',
885
range: getDefaultSuggestRange(doc, pos)
886
}],
887
};
888
},
889
}));
890
891
return withOracle(async (model, editor) => {
892
893
await assertEvent(model.onDidSuggest, () => {
894
editor.setValue('');
895
editor.setSelection(new Selection(1, 1, 1, 1));
896
editor.trigger('keyboard', Handler.Type, { text: 'Z' });
897
898
}, event => {
899
assert.strictEqual(event.triggerOptions.auto, true);
900
assert.strictEqual(event.completionModel.items.length, 1);
901
assert.strictEqual(event.completionModel.items[0].textLabel, 'Z aaa');
902
});
903
904
await assertEvent(model.onDidSuggest, () => {
905
// started another word: Z a|
906
// item should be: Z aaa, aaa
907
editor.trigger('keyboard', Handler.Type, { text: ' a' });
908
}, event => {
909
assert.strictEqual(event.triggerOptions.auto, true);
910
assert.strictEqual(event.completionModel.items.length, 2);
911
assert.strictEqual(event.completionModel.items[0].textLabel, 'Z aaa');
912
assert.strictEqual(event.completionModel.items[1].textLabel, 'aaa');
913
914
assert.strictEqual(countA, 1); // should we keep the suggestions from the "active" provider?, Yes! See: #106573
915
assert.strictEqual(countB, 2);
916
});
917
});
918
});
919
920
test('registerCompletionItemProvider with letters as trigger characters block other completion items to show up #127815', async function () {
921
922
disposables.add(registry.register({ scheme: 'test' }, {
923
_debugDisplayName: 'test',
924
provideCompletionItems(doc, pos) {
925
return {
926
suggestions: [{
927
kind: CompletionItemKind.Class,
928
label: 'AAAA',
929
insertText: 'WordTriggerA',
930
range: new Range(pos.lineNumber, pos.column, pos.lineNumber, pos.column)
931
}],
932
};
933
}
934
}));
935
disposables.add(registry.register({ scheme: 'test' }, {
936
_debugDisplayName: 'test',
937
triggerCharacters: ['a', '.'],
938
provideCompletionItems(doc, pos) {
939
return {
940
suggestions: [{
941
kind: CompletionItemKind.Class,
942
label: 'AAAA',
943
insertText: 'AutoTriggerA',
944
range: new Range(pos.lineNumber, pos.column, pos.lineNumber, pos.column)
945
}],
946
};
947
},
948
}));
949
950
return withOracle(async (model, editor) => {
951
952
await assertEvent(model.onDidSuggest, () => {
953
editor.setValue('');
954
editor.setSelection(new Selection(1, 1, 1, 1));
955
editor.trigger('keyboard', Handler.Type, { text: '.' });
956
957
}, event => {
958
assert.strictEqual(event.triggerOptions.auto, true);
959
assert.strictEqual(event.completionModel.items.length, 1);
960
});
961
962
963
editor.getModel().setValue('');
964
965
await assertEvent(model.onDidSuggest, () => {
966
editor.setValue('');
967
editor.setSelection(new Selection(1, 1, 1, 1));
968
editor.trigger('keyboard', Handler.Type, { text: 'a' });
969
970
}, event => {
971
assert.strictEqual(event.triggerOptions.auto, true);
972
assert.strictEqual(event.completionModel.items.length, 2);
973
});
974
});
975
});
976
977
test('Unexpected suggest scoring #167242', async function () {
978
disposables.add(registry.register('*', {
979
// word-based
980
_debugDisplayName: 'test',
981
provideCompletionItems(doc, pos) {
982
const word = doc.getWordUntilPosition(pos);
983
return {
984
suggestions: [{
985
kind: CompletionItemKind.Text,
986
label: 'pull',
987
insertText: 'pull',
988
range: new Range(pos.lineNumber, word.startColumn, pos.lineNumber, word.endColumn)
989
}],
990
};
991
}
992
}));
993
disposables.add(registry.register({ scheme: 'test' }, {
994
// JSON-based
995
_debugDisplayName: 'test',
996
provideCompletionItems(doc, pos) {
997
return {
998
suggestions: [{
999
kind: CompletionItemKind.Class,
1000
label: 'git.pull',
1001
insertText: 'git.pull',
1002
range: new Range(pos.lineNumber, 1, pos.lineNumber, pos.column)
1003
}],
1004
};
1005
},
1006
}));
1007
1008
return withOracle(async function (model, editor) {
1009
1010
await assertEvent(model.onDidSuggest, () => {
1011
editor.setValue('gi');
1012
editor.setSelection(new Selection(1, 3, 1, 3));
1013
editor.trigger('keyboard', Handler.Type, { text: 't' });
1014
1015
}, event => {
1016
assert.strictEqual(event.triggerOptions.auto, true);
1017
assert.strictEqual(event.completionModel.items.length, 1);
1018
assert.strictEqual(event.completionModel.items[0].textLabel, 'git.pull');
1019
});
1020
1021
editor.trigger('keyboard', Handler.Type, { text: '.' });
1022
1023
await assertEvent(model.onDidSuggest, () => {
1024
editor.trigger('keyboard', Handler.Type, { text: 'p' });
1025
1026
}, event => {
1027
assert.strictEqual(event.triggerOptions.auto, true);
1028
assert.strictEqual(event.completionModel.items.length, 1);
1029
assert.strictEqual(event.completionModel.items[0].textLabel, 'git.pull');
1030
});
1031
});
1032
});
1033
1034
test('Completion list closes unexpectedly when typing a digit after a word separator #169390', function () {
1035
1036
const requestCounts = [0, 0];
1037
1038
disposables.add(registry.register({ scheme: 'test' }, {
1039
_debugDisplayName: 'test',
1040
1041
provideCompletionItems(doc, pos) {
1042
requestCounts[0] += 1;
1043
return {
1044
suggestions: [{
1045
kind: CompletionItemKind.Text,
1046
label: 'foo-20',
1047
insertText: 'foo-20',
1048
range: new Range(pos.lineNumber, 1, pos.lineNumber, pos.column)
1049
}, {
1050
kind: CompletionItemKind.Text,
1051
label: 'foo-hello',
1052
insertText: 'foo-hello',
1053
range: new Range(pos.lineNumber, 1, pos.lineNumber, pos.column)
1054
}],
1055
};
1056
}
1057
}));
1058
disposables.add(registry.register({ scheme: 'test' }, {
1059
_debugDisplayName: 'test',
1060
triggerCharacters: ['2'],
1061
provideCompletionItems(doc, pos, ctx) {
1062
requestCounts[1] += 1;
1063
if (ctx.triggerKind !== CompletionTriggerKind.TriggerCharacter) {
1064
return;
1065
}
1066
return {
1067
suggestions: [{
1068
kind: CompletionItemKind.Class,
1069
label: 'foo-210',
1070
insertText: 'foo-210',
1071
range: new Range(pos.lineNumber, 1, pos.lineNumber, pos.column)
1072
}],
1073
};
1074
},
1075
}));
1076
1077
return withOracle(async function (model, editor) {
1078
1079
await assertEvent(model.onDidSuggest, () => {
1080
editor.setValue('foo');
1081
editor.setSelection(new Selection(1, 4, 1, 4));
1082
model.trigger({ auto: false });
1083
1084
}, event => {
1085
assert.strictEqual(event.triggerOptions.auto, false);
1086
assert.strictEqual(event.completionModel.items.length, 2);
1087
assert.strictEqual(event.completionModel.items[0].textLabel, 'foo-20');
1088
assert.strictEqual(event.completionModel.items[1].textLabel, 'foo-hello');
1089
});
1090
1091
editor.trigger('keyboard', Handler.Type, { text: '-' });
1092
1093
1094
await assertEvent(model.onDidSuggest, () => {
1095
editor.trigger('keyboard', Handler.Type, { text: '2' });
1096
1097
}, event => {
1098
assert.strictEqual(event.triggerOptions.auto, true);
1099
assert.strictEqual(event.completionModel.items.length, 2);
1100
assert.strictEqual(event.completionModel.items[0].textLabel, 'foo-20');
1101
assert.strictEqual(event.completionModel.items[1].textLabel, 'foo-210');
1102
assert.deepStrictEqual(requestCounts, [1, 2]);
1103
});
1104
});
1105
});
1106
1107
test('Set refilter-flag, keep triggerKind', function () {
1108
1109
disposables.add(registry.register({ scheme: 'test' }, {
1110
_debugDisplayName: 'test',
1111
triggerCharacters: ['.'],
1112
provideCompletionItems(doc, pos, ctx) {
1113
return {
1114
suggestions: [{
1115
label: doc.getWordUntilPosition(pos).word || 'hello',
1116
kind: CompletionItemKind.Property,
1117
insertText: 'foofoo',
1118
range: getDefaultSuggestRange(doc, pos)
1119
}]
1120
};
1121
},
1122
}));
1123
1124
return withOracle(async function (model, editor) {
1125
1126
await assertEvent(model.onDidSuggest, () => {
1127
editor.setValue('foo');
1128
editor.setSelection(new Selection(1, 4, 1, 4));
1129
editor.trigger('keyboard', Handler.Type, { text: 'o' });
1130
1131
1132
}, event => {
1133
assert.strictEqual(event.triggerOptions.auto, true);
1134
assert.strictEqual(event.triggerOptions.triggerCharacter, undefined);
1135
assert.strictEqual(event.triggerOptions.triggerKind, undefined);
1136
assert.strictEqual(event.completionModel.items.length, 1);
1137
});
1138
1139
await assertEvent(model.onDidSuggest, () => {
1140
editor.trigger('keyboard', Handler.Type, { text: '.' });
1141
1142
}, event => {
1143
assert.strictEqual(event.triggerOptions.auto, true);
1144
assert.strictEqual(event.triggerOptions.refilter, undefined);
1145
assert.strictEqual(event.triggerOptions.triggerCharacter, '.');
1146
assert.strictEqual(event.triggerOptions.triggerKind, CompletionTriggerKind.TriggerCharacter);
1147
assert.strictEqual(event.completionModel.items.length, 1);
1148
});
1149
1150
await assertEvent(model.onDidSuggest, () => {
1151
editor.trigger('keyboard', Handler.Type, { text: 'h' });
1152
1153
}, event => {
1154
assert.strictEqual(event.triggerOptions.auto, true);
1155
assert.strictEqual(event.triggerOptions.refilter, true);
1156
assert.strictEqual(event.triggerOptions.triggerCharacter, '.');
1157
assert.strictEqual(event.triggerOptions.triggerKind, CompletionTriggerKind.TriggerCharacter);
1158
assert.strictEqual(event.completionModel.items.length, 1);
1159
});
1160
});
1161
});
1162
1163
test('Snippets gone from IntelliSense #173244', function () {
1164
1165
const snippetProvider: CompletionItemProvider = {
1166
_debugDisplayName: 'test',
1167
provideCompletionItems(doc, pos, ctx) {
1168
return {
1169
suggestions: [{
1170
label: 'log',
1171
kind: CompletionItemKind.Snippet,
1172
insertText: 'log',
1173
range: getDefaultSuggestRange(doc, pos)
1174
}]
1175
};
1176
}
1177
};
1178
const old = setSnippetSuggestSupport(snippetProvider);
1179
1180
disposables.add(toDisposable(() => {
1181
if (getSnippetSuggestSupport() === snippetProvider) {
1182
setSnippetSuggestSupport(old);
1183
}
1184
}));
1185
1186
disposables.add(registry.register({ scheme: 'test' }, {
1187
_debugDisplayName: 'test',
1188
triggerCharacters: ['.'],
1189
provideCompletionItems(doc, pos, ctx) {
1190
return {
1191
suggestions: [{
1192
label: 'locals',
1193
kind: CompletionItemKind.Property,
1194
insertText: 'locals',
1195
range: getDefaultSuggestRange(doc, pos)
1196
}],
1197
incomplete: true
1198
};
1199
},
1200
}));
1201
1202
return withOracle(async function (model, editor) {
1203
1204
await assertEvent(model.onDidSuggest, () => {
1205
editor.setValue('');
1206
editor.setSelection(new Selection(1, 1, 1, 1));
1207
editor.trigger('keyboard', Handler.Type, { text: 'l' });
1208
1209
1210
}, event => {
1211
assert.strictEqual(event.triggerOptions.auto, true);
1212
assert.strictEqual(event.triggerOptions.triggerCharacter, undefined);
1213
assert.strictEqual(event.triggerOptions.triggerKind, undefined);
1214
assert.strictEqual(event.completionModel.items.length, 2);
1215
assert.strictEqual(event.completionModel.items[0].textLabel, 'locals');
1216
assert.strictEqual(event.completionModel.items[1].textLabel, 'log');
1217
});
1218
1219
await assertEvent(model.onDidSuggest, () => {
1220
editor.trigger('keyboard', Handler.Type, { text: 'o' });
1221
1222
}, event => {
1223
assert.strictEqual(event.triggerOptions.triggerKind, CompletionTriggerKind.TriggerForIncompleteCompletions);
1224
assert.strictEqual(event.triggerOptions.auto, true);
1225
assert.strictEqual(event.completionModel.items.length, 2);
1226
assert.strictEqual(event.completionModel.items[0].textLabel, 'locals');
1227
assert.strictEqual(event.completionModel.items[1].textLabel, 'log');
1228
});
1229
1230
});
1231
});
1232
1233
test('offWhenInlineCompletions - suppresses quick suggest when inline provider exists', function () {
1234
1235
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
1236
1237
// Register a dummy inline completions provider
1238
const inlineProvider: InlineCompletionsProvider = {
1239
provideInlineCompletions: () => ({ items: [] }),
1240
disposeInlineCompletions: () => { }
1241
};
1242
disposables.add(languageFeaturesService.inlineCompletionsProvider.register({ scheme: 'test' }, inlineProvider));
1243
1244
return withOracle((suggestOracle, editor) => {
1245
editor.updateOptions({ quickSuggestions: { comments: 'off', strings: 'off', other: 'offWhenInlineCompletions' } });
1246
1247
return new Promise<void>((resolve, reject) => {
1248
const unexpectedSuggestSub = suggestOracle.onDidSuggest(() => {
1249
unexpectedSuggestSub.dispose();
1250
reject(new Error('Quick suggestions should not have been triggered'));
1251
});
1252
1253
editor.setPosition({ lineNumber: 1, column: 4 });
1254
editor.trigger('keyboard', Handler.Type, { text: 'd' });
1255
1256
// Wait for the quick suggest delay to pass without triggering
1257
setTimeout(() => {
1258
unexpectedSuggestSub.dispose();
1259
resolve();
1260
}, 200);
1261
});
1262
});
1263
});
1264
1265
test('offWhenInlineCompletions - allows quick suggest when no inline provider exists', function () {
1266
1267
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
1268
1269
// No inline completions provider registered for 'test' scheme
1270
1271
return withOracle((suggestOracle, editor) => {
1272
editor.updateOptions({ quickSuggestions: { comments: 'off', strings: 'off', other: 'offWhenInlineCompletions' } });
1273
1274
return assertEvent(suggestOracle.onDidSuggest, () => {
1275
editor.setPosition({ lineNumber: 1, column: 4 });
1276
editor.trigger('keyboard', Handler.Type, { text: 'd' });
1277
}, suggestEvent => {
1278
assert.strictEqual(suggestEvent.triggerOptions.auto, true);
1279
assert.strictEqual(suggestEvent.completionModel.items.length, 1);
1280
});
1281
});
1282
});
1283
1284
test('offWhenInlineCompletions - allows quick suggest when inlineSuggest is disabled', function () {
1285
return runWithFakedTimers({ useFakeTimers: true }, () => {
1286
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
1287
1288
// Register a dummy inline completions provider
1289
const inlineProvider: InlineCompletionsProvider = {
1290
provideInlineCompletions: () => ({ items: [] }),
1291
disposeInlineCompletions: () => { }
1292
};
1293
disposables.add(languageFeaturesService.inlineCompletionsProvider.register({ scheme: 'test' }, inlineProvider));
1294
1295
return withOracle((suggestOracle, editor) => {
1296
editor.updateOptions({
1297
quickSuggestions: { comments: 'off', strings: 'off', other: 'offWhenInlineCompletions' },
1298
inlineSuggest: { enabled: false }
1299
});
1300
1301
return assertEvent(suggestOracle.onDidSuggest, () => {
1302
editor.setPosition({ lineNumber: 1, column: 4 });
1303
editor.trigger('keyboard', Handler.Type, { text: 'd' });
1304
}, suggestEvent => {
1305
assert.strictEqual(suggestEvent.triggerOptions.auto, true);
1306
assert.strictEqual(suggestEvent.completionModel.items.length, 1);
1307
});
1308
});
1309
});
1310
});
1311
1312
test('string shorthand - "off" disables quick suggestions for all token types', function () {
1313
return runWithFakedTimers({ useFakeTimers: true }, () => {
1314
1315
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
1316
1317
return withOracle((suggestOracle, editor) => {
1318
// Use string shorthand instead of object form
1319
editor.updateOptions({ quickSuggestions: 'off' });
1320
1321
return new Promise<void>((resolve, reject) => {
1322
const sub = suggestOracle.onDidSuggest(() => {
1323
sub.dispose();
1324
reject(new Error('Quick suggestions should have been suppressed by string shorthand "off"'));
1325
});
1326
1327
editor.setPosition({ lineNumber: 1, column: 4 });
1328
editor.trigger('keyboard', Handler.Type, { text: 'd' });
1329
1330
setTimeout(() => {
1331
sub.dispose();
1332
resolve();
1333
}, 200);
1334
});
1335
});
1336
});
1337
});
1338
1339
test('string shorthand - "offWhenInlineCompletions" suppresses when inline provider exists', function () {
1340
return runWithFakedTimers({ useFakeTimers: true }, () => {
1341
disposables.add(registry.register({ scheme: 'test' }, alwaysSomethingSupport));
1342
1343
const inlineProvider: InlineCompletionsProvider = {
1344
provideInlineCompletions: () => ({ items: [] }),
1345
disposeInlineCompletions: () => { }
1346
};
1347
disposables.add(languageFeaturesService.inlineCompletionsProvider.register({ scheme: 'test' }, inlineProvider));
1348
1349
return withOracle((suggestOracle, editor) => {
1350
// Use string shorthand — applies to all token types
1351
editor.updateOptions({ quickSuggestions: 'offWhenInlineCompletions' });
1352
1353
return new Promise<void>((resolve, reject) => {
1354
const sub = suggestOracle.onDidSuggest(() => {
1355
sub.dispose();
1356
reject(new Error('Quick suggestions should have been suppressed by offWhenInlineCompletions shorthand'));
1357
});
1358
1359
editor.setPosition({ lineNumber: 1, column: 4 });
1360
editor.trigger('keyboard', Handler.Type, { text: 'd' });
1361
1362
setTimeout(() => {
1363
sub.dispose();
1364
resolve();
1365
}, 200);
1366
});
1367
});
1368
});
1369
});
1370
});
1371
1372