Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts
4797 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 { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
7
import { EditorOptions, InternalSuggestOptions } from '../../../../common/config/editorOptions.js';
8
import { IPosition } from '../../../../common/core/position.js';
9
import * as languages from '../../../../common/languages.js';
10
import { CompletionModel } from '../../browser/completionModel.js';
11
import { CompletionItem, getSuggestionComparator, SnippetSortOrder } from '../../browser/suggest.js';
12
import { WordDistance } from '../../browser/wordDistance.js';
13
14
export function createSuggestItem(label: string | languages.CompletionItemLabel, overwriteBefore: number, kind = languages.CompletionItemKind.Property, incomplete: boolean = false, position: IPosition = { lineNumber: 1, column: 1 }, sortText?: string, filterText?: string): CompletionItem {
15
const suggestion: languages.CompletionItem = {
16
label,
17
sortText,
18
filterText,
19
range: { startLineNumber: position.lineNumber, startColumn: position.column - overwriteBefore, endLineNumber: position.lineNumber, endColumn: position.column },
20
insertText: typeof label === 'string' ? label : label.label,
21
kind
22
};
23
const container: languages.CompletionList = {
24
incomplete,
25
suggestions: [suggestion]
26
};
27
const provider: languages.CompletionItemProvider = {
28
_debugDisplayName: 'test',
29
provideCompletionItems(): any {
30
return;
31
}
32
};
33
34
return new CompletionItem(position, suggestion, container, provider);
35
}
36
suite('CompletionModel', function () {
37
38
const defaultOptions = <InternalSuggestOptions>{
39
insertMode: 'insert',
40
snippetsPreventQuickSuggestions: true,
41
filterGraceful: true,
42
localityBonus: false,
43
shareSuggestSelections: false,
44
showIcons: true,
45
showMethods: true,
46
showFunctions: true,
47
showConstructors: true,
48
showDeprecated: true,
49
showFields: true,
50
showVariables: true,
51
showClasses: true,
52
showStructs: true,
53
showInterfaces: true,
54
showModules: true,
55
showProperties: true,
56
showEvents: true,
57
showOperators: true,
58
showUnits: true,
59
showValues: true,
60
showConstants: true,
61
showEnums: true,
62
showEnumMembers: true,
63
showKeywords: true,
64
showWords: true,
65
showColors: true,
66
showFiles: true,
67
showReferences: true,
68
showFolders: true,
69
showTypeParameters: true,
70
showSnippets: true,
71
};
72
73
let model: CompletionModel;
74
75
setup(function () {
76
77
model = new CompletionModel([
78
createSuggestItem('foo', 3),
79
createSuggestItem('Foo', 3),
80
createSuggestItem('foo', 2),
81
], 1, {
82
leadingLineContent: 'foo',
83
characterCountDelta: 0
84
}, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
85
});
86
87
ensureNoDisposablesAreLeakedInTestSuite();
88
89
test('filtering - cached', function () {
90
91
const itemsNow = model.items;
92
let itemsThen = model.items;
93
assert.ok(itemsNow === itemsThen);
94
95
// still the same context
96
model.lineContext = { leadingLineContent: 'foo', characterCountDelta: 0 };
97
itemsThen = model.items;
98
assert.ok(itemsNow === itemsThen);
99
100
// different context, refilter
101
model.lineContext = { leadingLineContent: 'foo1', characterCountDelta: 1 };
102
itemsThen = model.items;
103
assert.ok(itemsNow !== itemsThen);
104
});
105
106
107
test('complete/incomplete', () => {
108
109
assert.strictEqual(model.getIncompleteProvider().size, 0);
110
111
const incompleteModel = new CompletionModel([
112
createSuggestItem('foo', 3, undefined, true),
113
createSuggestItem('foo', 2),
114
], 1, {
115
leadingLineContent: 'foo',
116
characterCountDelta: 0
117
}, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
118
assert.strictEqual(incompleteModel.getIncompleteProvider().size, 1);
119
});
120
121
test('Fuzzy matching of snippets stopped working with inline snippet suggestions #49895', function () {
122
const completeItem1 = createSuggestItem('foobar1', 1, undefined, false, { lineNumber: 1, column: 2 });
123
const completeItem2 = createSuggestItem('foobar2', 1, undefined, false, { lineNumber: 1, column: 2 });
124
const completeItem3 = createSuggestItem('foobar3', 1, undefined, false, { lineNumber: 1, column: 2 });
125
const completeItem4 = createSuggestItem('foobar4', 1, undefined, false, { lineNumber: 1, column: 2 });
126
const completeItem5 = createSuggestItem('foobar5', 1, undefined, false, { lineNumber: 1, column: 2 });
127
const incompleteItem1 = createSuggestItem('foofoo1', 1, undefined, true, { lineNumber: 1, column: 2 });
128
129
const model = new CompletionModel(
130
[
131
completeItem1,
132
completeItem2,
133
completeItem3,
134
completeItem4,
135
completeItem5,
136
incompleteItem1,
137
], 2, { leadingLineContent: 'f', characterCountDelta: 0 }, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined
138
);
139
assert.strictEqual(model.getIncompleteProvider().size, 1);
140
assert.strictEqual(model.items.length, 6);
141
});
142
143
test('proper current word when length=0, #16380', function () {
144
145
model = new CompletionModel([
146
createSuggestItem(' </div', 4),
147
createSuggestItem('a', 0),
148
createSuggestItem('p', 0),
149
createSuggestItem(' </tag', 4),
150
createSuggestItem(' XYZ', 4),
151
], 1, {
152
leadingLineContent: ' <',
153
characterCountDelta: 0
154
}, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
155
156
assert.strictEqual(model.items.length, 4);
157
158
const [a, b, c, d] = model.items;
159
assert.strictEqual(a.completion.label, ' </div');
160
assert.strictEqual(b.completion.label, ' </tag');
161
assert.strictEqual(c.completion.label, 'a');
162
assert.strictEqual(d.completion.label, 'p');
163
});
164
165
test('keep snippet sorting with prefix: top, #25495', function () {
166
167
model = new CompletionModel([
168
createSuggestItem('Snippet1', 1, languages.CompletionItemKind.Snippet),
169
createSuggestItem('tnippet2', 1, languages.CompletionItemKind.Snippet),
170
createSuggestItem('semver', 1, languages.CompletionItemKind.Property),
171
], 1, {
172
leadingLineContent: 's',
173
characterCountDelta: 0
174
}, WordDistance.None, defaultOptions, 'top', undefined);
175
176
assert.strictEqual(model.items.length, 2);
177
const [a, b] = model.items;
178
assert.strictEqual(a.completion.label, 'Snippet1');
179
assert.strictEqual(b.completion.label, 'semver');
180
assert.ok(a.score < b.score); // snippet really promoted
181
182
});
183
184
test('keep snippet sorting with prefix: bottom, #25495', function () {
185
186
model = new CompletionModel([
187
createSuggestItem('snippet1', 1, languages.CompletionItemKind.Snippet),
188
createSuggestItem('tnippet2', 1, languages.CompletionItemKind.Snippet),
189
createSuggestItem('Semver', 1, languages.CompletionItemKind.Property),
190
], 1, {
191
leadingLineContent: 's',
192
characterCountDelta: 0
193
}, WordDistance.None, defaultOptions, 'bottom', undefined);
194
195
assert.strictEqual(model.items.length, 2);
196
const [a, b] = model.items;
197
assert.strictEqual(a.completion.label, 'Semver');
198
assert.strictEqual(b.completion.label, 'snippet1');
199
assert.ok(a.score < b.score); // snippet really demoted
200
});
201
202
test('keep snippet sorting with prefix: inline, #25495', function () {
203
204
model = new CompletionModel([
205
createSuggestItem('snippet1', 1, languages.CompletionItemKind.Snippet),
206
createSuggestItem('tnippet2', 1, languages.CompletionItemKind.Snippet),
207
createSuggestItem('Semver', 1),
208
], 1, {
209
leadingLineContent: 's',
210
characterCountDelta: 0
211
}, WordDistance.None, defaultOptions, 'inline', undefined);
212
213
assert.strictEqual(model.items.length, 2);
214
const [a, b] = model.items;
215
assert.strictEqual(a.completion.label, 'snippet1');
216
assert.strictEqual(b.completion.label, 'Semver');
217
assert.ok(a.score > b.score); // snippet really demoted
218
});
219
220
test('filterText seems ignored in autocompletion, #26874', function () {
221
222
const item1 = createSuggestItem('Map - java.util', 1, undefined, undefined, undefined, undefined, 'Map');
223
const item2 = createSuggestItem('Map - java.util', 1);
224
225
model = new CompletionModel([item1, item2], 1, {
226
leadingLineContent: 'M',
227
characterCountDelta: 0
228
}, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
229
230
assert.strictEqual(model.items.length, 2);
231
232
model.lineContext = {
233
leadingLineContent: 'Map ',
234
characterCountDelta: 3
235
};
236
assert.strictEqual(model.items.length, 1);
237
});
238
239
test('Vscode 1.12 no longer obeys \'sortText\' in completion items (from language server), #26096', function () {
240
241
const item1 = createSuggestItem('<- groups', 2, languages.CompletionItemKind.Property, false, { lineNumber: 1, column: 3 }, '00002', ' groups');
242
const item2 = createSuggestItem('source', 0, languages.CompletionItemKind.Property, false, { lineNumber: 1, column: 3 }, '00001', 'source');
243
const items = [item1, item2].sort(getSuggestionComparator(SnippetSortOrder.Inline));
244
245
model = new CompletionModel(items, 3, {
246
leadingLineContent: ' ',
247
characterCountDelta: 0
248
}, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
249
250
assert.strictEqual(model.items.length, 2);
251
252
const [first, second] = model.items;
253
assert.strictEqual(first.completion.label, 'source');
254
assert.strictEqual(second.completion.label, '<- groups');
255
});
256
257
test('Completion item sorting broken when using label details #153026', function () {
258
const itemZZZ = createSuggestItem({ label: 'ZZZ' }, 0, languages.CompletionItemKind.Operator, false);
259
const itemAAA = createSuggestItem({ label: 'AAA' }, 0, languages.CompletionItemKind.Operator, false);
260
const itemIII = createSuggestItem('III', 0, languages.CompletionItemKind.Operator, false);
261
262
const cmp = getSuggestionComparator(SnippetSortOrder.Inline);
263
const actual = [itemZZZ, itemAAA, itemIII].sort(cmp);
264
265
assert.deepStrictEqual(actual, [itemAAA, itemIII, itemZZZ]);
266
});
267
268
test('Score only filtered items when typing more, score all when typing less', function () {
269
model = new CompletionModel([
270
createSuggestItem('console', 0),
271
createSuggestItem('co_new', 0),
272
createSuggestItem('bar', 0),
273
createSuggestItem('car', 0),
274
createSuggestItem('foo', 0),
275
], 1, {
276
leadingLineContent: '',
277
characterCountDelta: 0
278
}, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
279
280
assert.strictEqual(model.items.length, 5);
281
282
// narrow down once
283
model.lineContext = { leadingLineContent: 'c', characterCountDelta: 1 };
284
assert.strictEqual(model.items.length, 3);
285
286
// query gets longer, narrow down the narrow-down'ed-set from before
287
model.lineContext = { leadingLineContent: 'cn', characterCountDelta: 2 };
288
assert.strictEqual(model.items.length, 2);
289
290
// query gets shorter, refilter everything
291
model.lineContext = { leadingLineContent: '', characterCountDelta: 0 };
292
assert.strictEqual(model.items.length, 5);
293
});
294
295
test('Have more relaxed suggest matching algorithm #15419', function () {
296
model = new CompletionModel([
297
createSuggestItem('result', 0),
298
createSuggestItem('replyToUser', 0),
299
createSuggestItem('randomLolut', 0),
300
createSuggestItem('car', 0),
301
createSuggestItem('foo', 0),
302
], 1, {
303
leadingLineContent: '',
304
characterCountDelta: 0
305
}, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
306
307
// query gets longer, narrow down the narrow-down'ed-set from before
308
model.lineContext = { leadingLineContent: 'rlut', characterCountDelta: 4 };
309
assert.strictEqual(model.items.length, 3);
310
311
const [first, second, third] = model.items;
312
assert.strictEqual(first.completion.label, 'result'); // best with `rult`
313
assert.strictEqual(second.completion.label, 'replyToUser'); // best with `rltu`
314
assert.strictEqual(third.completion.label, 'randomLolut'); // best with `rlut`
315
});
316
317
test('Emmet suggestion not appearing at the top of the list in jsx files, #39518', function () {
318
model = new CompletionModel([
319
createSuggestItem('from', 0),
320
createSuggestItem('form', 0),
321
createSuggestItem('form:get', 0),
322
createSuggestItem('testForeignMeasure', 0),
323
createSuggestItem('fooRoom', 0),
324
], 1, {
325
leadingLineContent: '',
326
characterCountDelta: 0
327
}, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
328
329
model.lineContext = { leadingLineContent: 'form', characterCountDelta: 4 };
330
assert.strictEqual(model.items.length, 5);
331
const [first, second, third] = model.items;
332
assert.strictEqual(first.completion.label, 'form'); // best with `form`
333
assert.strictEqual(second.completion.label, 'form:get'); // best with `form`
334
assert.strictEqual(third.completion.label, 'from'); // best with `from`
335
});
336
});
337
338