completionModel.test.ts 11.2 KB
Newer Older
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

import * as assert from 'assert';
8
import { TPromise } from 'vs/base/common/winjs.base';
J
Johannes Rieken 已提交
9 10 11 12
import { IPosition } from 'vs/editor/common/core/position';
import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestionType } from 'vs/editor/common/modes';
import { CompletionModel } from 'vs/editor/contrib/suggest/completionModel';
import { ISuggestionItem, getSuggestionComparator } from 'vs/editor/contrib/suggest/suggest';
J
Johannes Rieken 已提交
13
import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance';
14

15
export function createSuggestItem(label: string, overwriteBefore: number, type: SuggestionType = 'property', incomplete: boolean = false, position: IPosition = { lineNumber: 1, column: 1 }): ISuggestionItem {
16

17
	return new class implements ISuggestionItem {
18

19
		position = position;
20

21 22 23 24 25 26
		suggestion: ISuggestion = {
			label,
			overwriteBefore,
			insertText: label,
			type
		};
27

28 29 30 31
		container: ISuggestResult = {
			incomplete,
			suggestions: [this.suggestion]
		};
32

33 34 35
		support: ISuggestSupport = {
			provideCompletionItems(): any {
				return;
36 37
			}
		};
38 39 40 41 42 43 44 45

		resolve(): TPromise<void> {
			return null;
		}
	};
}
suite('CompletionModel', function () {

46 47 48 49 50 51 52 53 54

	let model: CompletionModel;

	setup(function () {

		model = new CompletionModel([
			createSuggestItem('foo', 3),
			createSuggestItem('Foo', 3),
			createSuggestItem('foo', 2),
55
		], 1, {
J
Johannes Rieken 已提交
56 57
				leadingLineContent: 'foo',
				characterCountDelta: 0
J
Johannes Rieken 已提交
58
			}, WordDistance.None);
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
	});

	test('filtering - cached', function () {

		const itemsNow = model.items;
		let itemsThen = model.items;
		assert.ok(itemsNow === itemsThen);

		// still the same context
		model.lineContext = { leadingLineContent: 'foo', characterCountDelta: 0 };
		itemsThen = model.items;
		assert.ok(itemsNow === itemsThen);

		// different context, refilter
		model.lineContext = { leadingLineContent: 'foo1', characterCountDelta: 1 };
		itemsThen = model.items;
		assert.ok(itemsNow !== itemsThen);
	});

78

J
Johannes Rieken 已提交
79 80
	test('complete/incomplete', function () {

J
Johannes Rieken 已提交
81
		assert.equal(model.incomplete.size, 0);
J
Johannes Rieken 已提交
82 83

		let incompleteModel = new CompletionModel([
J
Johannes Rieken 已提交
84
			createSuggestItem('foo', 3, undefined, true),
J
Johannes Rieken 已提交
85
			createSuggestItem('foo', 2),
86
		], 1, {
J
Johannes Rieken 已提交
87 88
				leadingLineContent: 'foo',
				characterCountDelta: 0
J
Johannes Rieken 已提交
89
			}, WordDistance.None);
J
Johannes Rieken 已提交
90
		assert.equal(incompleteModel.incomplete.size, 1);
J
Johannes Rieken 已提交
91
	});
92 93 94

	test('replaceIncomplete', function () {

J
Johannes Rieken 已提交
95 96
		const completeItem = createSuggestItem('foobar', 1, undefined, false, { lineNumber: 1, column: 2 });
		const incompleteItem = createSuggestItem('foofoo', 1, undefined, true, { lineNumber: 1, column: 2 });
97

J
Johannes Rieken 已提交
98
		const model = new CompletionModel([completeItem, incompleteItem], 2, { leadingLineContent: 'f', characterCountDelta: 0 }, WordDistance.None);
J
Johannes Rieken 已提交
99
		assert.equal(model.incomplete.size, 1);
100 101
		assert.equal(model.items.length, 2);

J
Johannes Rieken 已提交
102 103
		const { incomplete } = model;
		const complete = model.adopt(incomplete);
J
Johannes Rieken 已提交
104

J
Johannes Rieken 已提交
105 106
		assert.equal(incomplete.size, 1);
		assert.ok(incomplete.has(incompleteItem.support));
J
Johannes Rieken 已提交
107 108
		assert.equal(complete.length, 1);
		assert.ok(complete[0] === completeItem);
109
	});
110

J
Johannes Rieken 已提交
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
	test('Fuzzy matching of snippets stopped working with inline snippet suggestions #49895', function () {
		const completeItem1 = createSuggestItem('foobar1', 1, undefined, false, { lineNumber: 1, column: 2 });
		const completeItem2 = createSuggestItem('foobar2', 1, undefined, false, { lineNumber: 1, column: 2 });
		const completeItem3 = createSuggestItem('foobar3', 1, undefined, false, { lineNumber: 1, column: 2 });
		const completeItem4 = createSuggestItem('foobar4', 1, undefined, false, { lineNumber: 1, column: 2 });
		const completeItem5 = createSuggestItem('foobar5', 1, undefined, false, { lineNumber: 1, column: 2 });
		const incompleteItem1 = createSuggestItem('foofoo1', 1, undefined, true, { lineNumber: 1, column: 2 });

		const model = new CompletionModel(
			[
				completeItem1,
				completeItem2,
				completeItem3,
				completeItem4,
				completeItem5,
				incompleteItem1,
J
Johannes Rieken 已提交
127
			], 2, { leadingLineContent: 'f', characterCountDelta: 0 }, WordDistance.None
J
Johannes Rieken 已提交
128 129 130 131 132 133 134 135 136 137 138 139
		);
		assert.equal(model.incomplete.size, 1);
		assert.equal(model.items.length, 6);

		const { incomplete } = model;
		const complete = model.adopt(incomplete);

		assert.equal(incomplete.size, 1);
		assert.ok(incomplete.has(incompleteItem1.support));
		assert.equal(complete.length, 5);
	});

J
Johannes Rieken 已提交
140 141 142 143 144 145 146 147 148 149 150
	test('proper current word when length=0, #16380', function () {

		model = new CompletionModel([
			createSuggestItem('    </div', 4),
			createSuggestItem('a', 0),
			createSuggestItem('p', 0),
			createSuggestItem('    </tag', 4),
			createSuggestItem('    XYZ', 4),
		], 1, {
				leadingLineContent: '   <',
				characterCountDelta: 0
J
Johannes Rieken 已提交
151
			}, WordDistance.None);
J
Johannes Rieken 已提交
152 153 154 155 156

		assert.equal(model.items.length, 4);

		const [a, b, c, d] = model.items;
		assert.equal(a.suggestion.label, '    </div');
157 158 159
		assert.equal(b.suggestion.label, '    </tag');
		assert.equal(c.suggestion.label, 'a');
		assert.equal(d.suggestion.label, 'p');
J
Johannes Rieken 已提交
160 161
	});

J
Johannes Rieken 已提交
162 163 164 165 166 167 168 169 170
	test('keep snippet sorting with prefix: top, #25495', function () {

		model = new CompletionModel([
			createSuggestItem('Snippet1', 1, 'snippet'),
			createSuggestItem('tnippet2', 1, 'snippet'),
			createSuggestItem('semver', 1, 'property'),
		], 1, {
				leadingLineContent: 's',
				characterCountDelta: 0
J
Johannes Rieken 已提交
171
			}, WordDistance.None, { snippets: 'top', snippetsPreventQuickSuggestions: true, filterGraceful: true, localityBonus: false });
J
Johannes Rieken 已提交
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189

		assert.equal(model.items.length, 2);
		const [a, b] = model.items;
		assert.equal(a.suggestion.label, 'Snippet1');
		assert.equal(b.suggestion.label, 'semver');
		assert.ok(a.score < b.score); // snippet really promoted

	});

	test('keep snippet sorting with prefix: bottom, #25495', function () {

		model = new CompletionModel([
			createSuggestItem('snippet1', 1, 'snippet'),
			createSuggestItem('tnippet2', 1, 'snippet'),
			createSuggestItem('Semver', 1, 'property'),
		], 1, {
				leadingLineContent: 's',
				characterCountDelta: 0
J
Johannes Rieken 已提交
190
			}, WordDistance.None, { snippets: 'bottom', snippetsPreventQuickSuggestions: true, filterGraceful: true, localityBonus: false });
J
Johannes Rieken 已提交
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

		assert.equal(model.items.length, 2);
		const [a, b] = model.items;
		assert.equal(a.suggestion.label, 'Semver');
		assert.equal(b.suggestion.label, 'snippet1');
		assert.ok(a.score < b.score); // snippet really demoted
	});

	test('keep snippet sorting with prefix: inline, #25495', function () {

		model = new CompletionModel([
			createSuggestItem('snippet1', 1, 'snippet'),
			createSuggestItem('tnippet2', 1, 'snippet'),
			createSuggestItem('Semver', 1, 'property'),
		], 1, {
				leadingLineContent: 's',
				characterCountDelta: 0
J
Johannes Rieken 已提交
208
			}, WordDistance.None, { snippets: 'inline', snippetsPreventQuickSuggestions: true, filterGraceful: true, localityBonus: false });
J
Johannes Rieken 已提交
209 210 211 212 213 214 215

		assert.equal(model.items.length, 2);
		const [a, b] = model.items;
		assert.equal(a.suggestion.label, 'snippet1');
		assert.equal(b.suggestion.label, 'Semver');
		assert.ok(a.score > b.score); // snippet really demoted
	});
216 217 218 219 220 221 222 223 224 225

	test('filterText seems ignored in autocompletion, #26874', function () {

		const item1 = createSuggestItem('Map - java.util', 1, 'property');
		item1.suggestion.filterText = 'Map';
		const item2 = createSuggestItem('Map - java.util', 1, 'property');

		model = new CompletionModel([item1, item2], 1, {
			leadingLineContent: 'M',
			characterCountDelta: 0
J
Johannes Rieken 已提交
226
		}, WordDistance.None);
227 228 229 230 231 232 233 234 235

		assert.equal(model.items.length, 2);

		model.lineContext = {
			leadingLineContent: 'Map ',
			characterCountDelta: 3
		};
		assert.equal(model.items.length, 1);
	});
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251

	test('Vscode 1.12 no longer obeys \'sortText\' in completion items (from language server), #26096', function () {

		const item1 = createSuggestItem('<- groups', 2, 'property', false, { lineNumber: 1, column: 3 });
		item1.suggestion.filterText = '  groups';
		item1.suggestion.sortText = '00002';

		const item2 = createSuggestItem('source', 0, 'property', false, { lineNumber: 1, column: 3 });
		item2.suggestion.filterText = 'source';
		item2.suggestion.sortText = '00001';

		const items = [item1, item2].sort(getSuggestionComparator('inline'));

		model = new CompletionModel(items, 3, {
			leadingLineContent: '  ',
			characterCountDelta: 0
J
Johannes Rieken 已提交
252
		}, WordDistance.None);
253 254 255 256 257 258 259 260

		assert.equal(model.items.length, 2);

		const [first, second] = model.items;
		assert.equal(first.suggestion.label, 'source');
		assert.equal(second.suggestion.label, '<- groups');
	});

261 262 263 264 265 266 267 268 269 270
	test('Score only filtered items when typing more, score all when typing less', function () {
		model = new CompletionModel([
			createSuggestItem('console', 0, 'property'),
			createSuggestItem('co_new', 0, 'property'),
			createSuggestItem('bar', 0, 'property'),
			createSuggestItem('car', 0, 'property'),
			createSuggestItem('foo', 0, 'property'),
		], 1, {
				leadingLineContent: '',
				characterCountDelta: 0
J
Johannes Rieken 已提交
271
			}, WordDistance.None);
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

		assert.equal(model.items.length, 5);

		// narrow down once
		model.lineContext = { leadingLineContent: 'c', characterCountDelta: 1 };
		assert.equal(model.items.length, 3);

		// query gets longer, narrow down the narrow-down'ed-set from before
		model.lineContext = { leadingLineContent: 'cn', characterCountDelta: 2 };
		assert.equal(model.items.length, 2);

		// query gets shorter, refilter everything
		model.lineContext = { leadingLineContent: '', characterCountDelta: 0 };
		assert.equal(model.items.length, 5);
	});
287 288 289 290 291 292 293 294 295 296 297

	test('Have more relaxed suggest matching algorithm #15419', function () {
		model = new CompletionModel([
			createSuggestItem('result', 0, 'property'),
			createSuggestItem('replyToUser', 0, 'property'),
			createSuggestItem('randomLolut', 0, 'property'),
			createSuggestItem('car', 0, 'property'),
			createSuggestItem('foo', 0, 'property'),
		], 1, {
				leadingLineContent: '',
				characterCountDelta: 0
J
Johannes Rieken 已提交
298
			}, WordDistance.None);
299 300 301 302 303 304 305 306 307 308

		// query gets longer, narrow down the narrow-down'ed-set from before
		model.lineContext = { leadingLineContent: 'rlut', characterCountDelta: 4 };
		assert.equal(model.items.length, 3);

		const [first, second, third] = model.items;
		assert.equal(first.suggestion.label, 'result'); // best with `rult`
		assert.equal(second.suggestion.label, 'replyToUser');  // best with `rltu`
		assert.equal(third.suggestion.label, 'randomLolut');  // best with `rlut`
	});
J
Johannes Rieken 已提交
309 310 311 312 313 314 315 316 317 318 319

	test('Emmet suggestion not appearing at the top of the list in jsx files, #39518', function () {
		model = new CompletionModel([
			createSuggestItem('from', 0, 'property'),
			createSuggestItem('form', 0, 'property'),
			createSuggestItem('form:get', 0, 'property'),
			createSuggestItem('testForeignMeasure', 0, 'property'),
			createSuggestItem('fooRoom', 0, 'property'),
		], 1, {
				leadingLineContent: '',
				characterCountDelta: 0
J
Johannes Rieken 已提交
320
			}, WordDistance.None);
J
Johannes Rieken 已提交
321 322 323 324 325 326 327 328

		model.lineContext = { leadingLineContent: 'form', characterCountDelta: 4 };
		assert.equal(model.items.length, 5);
		const [first, second, third] = model.items;
		assert.equal(first.suggestion.label, 'form'); // best with `form`
		assert.equal(second.suggestion.label, 'form:get');  // best with `form`
		assert.equal(third.suggestion.label, 'from');  // best with `from`
	});
329
});