diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 3f601407837..e15c4a99b51 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -450,13 +450,6 @@ export interface CompletionItem { * A command that should be run upon acceptance of this item. */ command?: Command; - - /**@internal*/ - _labelLow?: string; - /**@internal*/ - _sortTextLow?: string; - /**@internal*/ - _filterTextLow?: string; } export interface CompletionList { diff --git a/src/vs/editor/contrib/suggest/completionModel.ts b/src/vs/editor/contrib/suggest/completionModel.ts index 3dfe9456fc2..a268da75b14 100644 --- a/src/vs/editor/contrib/suggest/completionModel.ts +++ b/src/vs/editor/contrib/suggest/completionModel.ts @@ -6,7 +6,7 @@ import { fuzzyScore, fuzzyScoreGracefulAggressive, anyScore, FuzzyScorer } from 'vs/base/common/filters'; import { isDisposable } from 'vs/base/common/lifecycle'; import { CompletionList, CompletionItemProvider, CompletionItemKind } from 'vs/editor/common/modes'; -import { SuggestionItem, ensureLowerCaseVariants } from './suggest'; +import { SuggestionItem } from './suggest'; import { InternalSuggestOptions, EDITOR_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; import { CharCode } from 'vs/base/common/charCode'; @@ -165,21 +165,17 @@ export class CompletionModel { for (let i = 0; i < source.length; i++) { const item = source[i]; - const { suggestion, container } = item; - - // make sure _labelLow, _filterTextLow, _sortTextLow exist - ensureLowerCaseVariants(suggestion); // collect those supports that signaled having // an incomplete result - if (container.incomplete) { + if (item.container.incomplete) { this._isIncomplete.add(item.provider); } // 'word' is that remainder of the current line that we // filter and score against. In theory each suggestion uses a // different word, but in practice not - that's why we cache - const overwriteBefore = item.position.column - suggestion.range.startColumn; + const overwriteBefore = item.position.column - item.suggestion.range.startColumn; const wordLen = overwriteBefore + characterCountDelta - (item.position.column - this._column); if (word.length !== wordLen) { word = wordLen === 0 ? '' : leadingLineContent.slice(-wordLen); @@ -218,21 +214,21 @@ export class CompletionModel { item.score = -100; item.matches = []; - } else if (typeof suggestion.filterText === 'string') { + } else if (typeof item.suggestion.filterText === 'string') { // when there is a `filterText` it must match the `word`. // if it matches we check with the label to compute highlights // and if that doesn't yield a result we have no highlights, // despite having the match - let match = scoreFn(word, wordLow, wordPos, suggestion.filterText, suggestion._filterTextLow, 0, false); + let match = scoreFn(word, wordLow, wordPos, item.suggestion.filterText, item.filterTextLow, 0, false); if (!match) { continue; } item.score = match[0]; - item.matches = (fuzzyScore(word, wordLow, 0, suggestion.label, suggestion._labelLow, 0, true) || anyScore(word, suggestion.label))[1]; + item.matches = (fuzzyScore(word, wordLow, 0, item.suggestion.label, item.labelLow, 0, true) || anyScore(word, item.suggestion.label))[1]; } else { // by default match `word` against the `label` - let match = scoreFn(word, wordLow, wordPos, suggestion.label, suggestion._labelLow, 0, false); + let match = scoreFn(word, wordLow, wordPos, item.suggestion.label, item.labelLow, 0, false); if (match) { item.score = match[0]; item.matches = match[1]; @@ -243,12 +239,12 @@ export class CompletionModel { } item.idx = i; - item.distance = this._wordDistance.distance(item.position, suggestion); + item.distance = this._wordDistance.distance(item.position, item.suggestion); target.push(item); // update stats this._stats.suggestionCount++; - switch (suggestion.kind) { + switch (item.suggestion.kind) { case CompletionItemKind.Snippet: this._stats.snippetCount++; break; case CompletionItemKind.Text: this._stats.textCount++; break; } diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index 6709c6f7219..4aa650e4cdd 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -30,6 +30,11 @@ export class SuggestionItem { readonly resolve: (token: CancellationToken) => Thenable; + // perf + readonly labelLow: string; + readonly sortTextLow?: string; + readonly filterTextLow?: string; + constructor( readonly position: IPosition, readonly suggestion: CompletionItem, @@ -37,6 +42,11 @@ export class SuggestionItem { readonly provider: CompletionItemProvider, model: ITextModel ) { + // ensure lower-variants (perf) + this.labelLow = suggestion.label.toLowerCase(); + this.sortTextLow = suggestion.sortText && suggestion.sortText.toLowerCase(); + this.filterTextLow = suggestion.filterText && suggestion.filterText.toLowerCase(); + // create the suggestion resolver const { resolveCompletionItem } = provider; if (typeof resolveCompletionItem !== 'function') { @@ -135,9 +145,6 @@ export function provideSuggestionItems( suggestion.range = defaultRange; } - // fill in lower-case text - ensureLowerCaseVariants(suggestion); - allSuggestions.push(new SuggestionItem(position, suggestion, container, provider, model)); } } @@ -171,18 +178,6 @@ export function provideSuggestionItems( return result; } -export function ensureLowerCaseVariants(suggestion: CompletionItem) { - if (!suggestion._labelLow) { - suggestion._labelLow = suggestion.label.toLowerCase(); - } - if (suggestion.sortText && !suggestion._sortTextLow) { - suggestion._sortTextLow = suggestion.sortText.toLowerCase(); - } - if (suggestion.filterText && !suggestion._filterTextLow) { - suggestion._filterTextLow = suggestion.filterText.toLowerCase(); - } -} - function createSuggesionFilter(snippetConfig: SnippetConfig): (candidate: CompletionItem) => boolean { if (snippetConfig === 'none') { return suggestion => suggestion.kind !== CompletionItemKind.Snippet; @@ -192,10 +187,10 @@ function createSuggesionFilter(snippetConfig: SnippetConfig): (candidate: Comple } function defaultComparator(a: SuggestionItem, b: SuggestionItem): number { // check with 'sortText' - if (a.suggestion._sortTextLow && b.suggestion._sortTextLow) { - if (a.suggestion._sortTextLow < b.suggestion._sortTextLow) { + if (a.sortTextLow && b.sortTextLow) { + if (a.sortTextLow < b.sortTextLow) { return -1; - } else if (a.suggestion._sortTextLow > b.suggestion._sortTextLow) { + } else if (a.sortTextLow > b.sortTextLow) { return 1; } } diff --git a/src/vs/editor/contrib/suggest/test/completionModel.test.ts b/src/vs/editor/contrib/suggest/test/completionModel.test.ts index 3db6c71b734..3e857ec1662 100644 --- a/src/vs/editor/contrib/suggest/test/completionModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/completionModel.test.ts @@ -6,39 +6,29 @@ import * as assert from 'assert'; import { IPosition } from 'vs/editor/common/core/position'; import { CompletionList, CompletionItemProvider, CompletionItem, CompletionItemKind } from 'vs/editor/common/modes'; import { CompletionModel } from 'vs/editor/contrib/suggest/completionModel'; -import { SuggestionItem, getSuggestionComparator, ensureLowerCaseVariants } from 'vs/editor/contrib/suggest/suggest'; +import { SuggestionItem, getSuggestionComparator } from 'vs/editor/contrib/suggest/suggest'; import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; -export function createSuggestItem(label: string, overwriteBefore: number, kind = CompletionItemKind.Property, incomplete: boolean = false, position: IPosition = { lineNumber: 1, column: 1 }): SuggestionItem { - - return new class implements SuggestionItem { - - _brand: 'ISuggestionItem'; - - position = position; - - suggestion: CompletionItem = { - label, - range: { startLineNumber: position.lineNumber, startColumn: position.column - overwriteBefore, endLineNumber: position.lineNumber, endColumn: position.column }, - insertText: label, - kind - }; - - container: CompletionList = { - incomplete, - suggestions: [this.suggestion] - }; - - provider: CompletionItemProvider = { - provideCompletionItems(): any { - return; - } - }; - - resolve(): Promise { - return null; +export function createSuggestItem(label: string, overwriteBefore: number, kind = CompletionItemKind.Property, incomplete: boolean = false, position: IPosition = { lineNumber: 1, column: 1 }, sortText?: string, filterText?: string): SuggestionItem { + const suggestion: CompletionItem = { + label, + sortText, + filterText, + range: { startLineNumber: position.lineNumber, startColumn: position.column - overwriteBefore, endLineNumber: position.lineNumber, endColumn: position.column }, + insertText: label, + kind + }; + const container: CompletionList = { + incomplete, + suggestions: [suggestion] + }; + const provider: CompletionItemProvider = { + provideCompletionItems(): any { + return; } }; + + return new SuggestionItem(position, suggestion, container, provider, undefined); } suite('CompletionModel', function () { @@ -215,8 +205,7 @@ suite('CompletionModel', function () { test('filterText seems ignored in autocompletion, #26874', function () { - const item1 = createSuggestItem('Map - java.util', 1); - item1.suggestion.filterText = 'Map'; + const item1 = createSuggestItem('Map - java.util', 1, undefined, undefined, undefined, undefined, 'Map'); const item2 = createSuggestItem('Map - java.util', 1); model = new CompletionModel([item1, item2], 1, { @@ -235,17 +224,8 @@ suite('CompletionModel', function () { test('Vscode 1.12 no longer obeys \'sortText\' in completion items (from language server), #26096', function () { - const item1 = createSuggestItem('<- groups', 2, CompletionItemKind.Property, false, { lineNumber: 1, column: 3 }); - item1.suggestion.filterText = ' groups'; - item1.suggestion.sortText = '00002'; - - const item2 = createSuggestItem('source', 0, CompletionItemKind.Property, false, { lineNumber: 1, column: 3 }); - item2.suggestion.filterText = 'source'; - item2.suggestion.sortText = '00001'; - - ensureLowerCaseVariants(item1.suggestion); - ensureLowerCaseVariants(item2.suggestion); - + const item1 = createSuggestItem('<- groups', 2, CompletionItemKind.Property, false, { lineNumber: 1, column: 3 }, '00002', ' groups'); + const item2 = createSuggestItem('source', 0, CompletionItemKind.Property, false, { lineNumber: 1, column: 3 }, '00001', 'source'); const items = [item1, item2].sort(getSuggestionComparator('inline')); model = new CompletionModel(items, 3, { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 8f9f92b6d31..a2172065376 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -688,11 +688,7 @@ class SuggestAdapter { insertTextRules: item.keepWhitespace ? modes.CompletionItemInsertTextRule.KeepWhitespace : 0, additionalTextEdits: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from), command: this._commands.toInternal(item.command), - commitCharacters: item.commitCharacters, - // help with perf - _labelLow: item.label.toLowerCase(), - _filterTextLow: item.filterText && item.filterText.toLowerCase(), - _sortTextLow: item.sortText && item.sortText.toLowerCase() + commitCharacters: item.commitCharacters }; // 'insertText'-logic