diff --git a/src/vs/editor/contrib/suggest/common/completionModel.ts b/src/vs/editor/contrib/suggest/common/completionModel.ts index 42e79d06a9cc7f53e03e30e20974a7739623dce5..2c0447734468c0d501129828925c3d14bfef2bce 100644 --- a/src/vs/editor/contrib/suggest/common/completionModel.ts +++ b/src/vs/editor/contrib/suggest/common/completionModel.ts @@ -6,7 +6,6 @@ 'use strict'; import { isFalsyOrEmpty } from 'vs/base/common/arrays'; -import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { IMatch, fuzzyContiguousFilter } from 'vs/base/common/filters'; import { ISuggestSupport } from 'vs/editor/common/modes'; import { ISuggestionItem } from './suggest'; @@ -155,7 +154,7 @@ export class CompletionModel { this._filteredItems.push(item); // compute score against word - const score = CompletionModel._scoreByHighlight(item, word); + const score = CompletionModel._scoreByHighlight(item, word, word.toLowerCase()); if (score > topScore) { topScore = score; this._topScoreIdx = this._filteredItems.length - 1; @@ -176,30 +175,19 @@ export class CompletionModel { } } - private static _scoreByHighlight(item: ICompletionItem, currentWord: string): number { + private static _scoreByHighlight(item: ICompletionItem, currentWord: string, currentWordLowerCase: string): number { const {highlights, suggestion} = item; let score = 0; if (!isFalsyOrEmpty(highlights)) { for (const {start, end} of highlights) { - // scoring logic: - // First find the highlighted portion from the label in the current - // word and second score a case-senstive match with 2pts and everything - // else with 1pt. - - // this is a case-insensitive String#indexOf + // find the highlight in the current word and + // score it based on case-match and start index const part = suggestion.label.substring(start, end); - const regexp = new RegExp(escapeRegExpCharacters(part), 'i'); - const idx = currentWord.search(regexp); - - // case senstive match score 2, the rest 1 - if (idx >= 0) { - for (let i = 0; i < part.length; i++) { - if (part[i] === currentWord[idx + i]) { - score += 2; - } else { - score += 1; - } - } + if (currentWord.indexOf(part) >= 0) { + score += (2 * part.length) / (start + 1); + + } else if (currentWordLowerCase.indexOf(part) >= 0) { + score += part.length / (start + 1); } } } diff --git a/src/vs/editor/contrib/suggest/test/common/completionModel.test.ts b/src/vs/editor/contrib/suggest/test/common/completionModel.test.ts index 22f249441eb7a2d74aae376d24a87df064ed0f8a..67a7948e4fd9a35413fc1889b00c6c37b0fb1f97 100644 --- a/src/vs/editor/contrib/suggest/test/common/completionModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/common/completionModel.test.ts @@ -74,51 +74,6 @@ suite('CompletionModel', function () { assert.ok(itemsNow !== itemsThen); }); - test('top score', function () { - - assert.equal(model.topScoreIdx, 0); - - model.lineContext = { leadingLineContent: 'Foo', characterCountDelta: 0 }; - assert.equal(model.topScoreIdx, 1); - }); - - test('top score, issue #14446', function () { - - const model = new CompletionModel([ - createSuggestItem('workbench.editor.defaultSideBySideLayout', 15), - createSuggestItem('workbench.sideBar.location', 15), - ], 15, { - leadingLineContent: 'workbench.sideb', - characterCountDelta: 0 - } - ) - - assert.equal(model.topScoreIdx, 1); - assert.equal(model.items[model.topScoreIdx].suggestion.label, 'workbench.sideBar.location') - }); - - test('top score, issue #11423', function () { - - const model = new CompletionModel([ - createSuggestItem('diffEditor.renderSideBySide', 8), - createSuggestItem('editor.overviewRulerlanes', 8), - createSuggestItem('editor.renderControlCharacter', 8), - createSuggestItem('editor.renderWhitespace', 8), - ], 15, { - leadingLineContent: 'editor.r', - characterCountDelta: 0 - } - ) - - assert.equal(model.topScoreIdx, 2); - assert.equal(model.items[model.topScoreIdx].suggestion.label, 'editor.renderControlCharacter') - - model.lineContext = { leadingLineContent: 'editor.R', characterCountDelta: 0 }; - assert.equal(model.topScoreIdx, 1); - - model.lineContext = { leadingLineContent: 'Editor.r', characterCountDelta: 0 }; - assert.equal(model.topScoreIdx, 0); - }); test('complete/incomplete', function () { @@ -152,4 +107,37 @@ suite('CompletionModel', function () { assert.equal(model.incomplete.length, 0); assert.equal(model.items.length, 3); }); + + function assertTopScore(lineContent: string, expected: number, ...suggestionLabels: string[]): void { + + const model = new CompletionModel( + suggestionLabels.map(label => createSuggestItem(label, lineContent.length)), + lineContent.length, + { + characterCountDelta: 0, + leadingLineContent: lineContent + } + ); + + assert.equal(model.topScoreIdx, expected, `${lineContent}, ACTUAL: ${model.items[model.topScoreIdx].suggestion.label} <> EXPECTED: ${model.items[expected].suggestion.label}`); + + } + + test('top score', function () { + + assertTopScore('Foo', 1, 'foo', 'Foo', 'foo'); + + // issue #14583 + assertTopScore('log', 3, 'HTMLOptGroupElement', 'ScrollLogicalPosition', 'SVGFEMorphologyElement', 'log'); + assertTopScore('e', 2, 'AbstractWorker', 'ActiveXObject', 'else'); + + // issue #14446 + // assertTopScore('workbench.sideb', 1, 'workbench.editor.defaultSideBySideLayout', 'workbench.sideBar.location'); + + // issue #11423 + assertTopScore('editor.r', 2, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace'); + assertTopScore('editor.R', 1, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace'); + // assertTopScore('Editor.r', 0, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace'); + + }); });