From 53321de6d3567a1a696216a47fa38e8f550f8e32 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 9 Oct 2018 12:09:21 +0200 Subject: [PATCH] fix #60247 --- .../electron-browser/snippetsService.ts | 76 ++++++------------- .../electron-browser/snippetsService.test.ts | 51 ++++++++----- 2 files changed, 58 insertions(+), 69 deletions(-) diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index a63e501486e..afb20204aa9 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -13,7 +13,7 @@ import { compare, endsWith, isFalsyOrWhitespace } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; -import { CompletionItem, CompletionList, CompletionItemProvider, LanguageId, CompletionContext, CompletionItemKind } from 'vs/editor/common/modes'; +import { CompletionItem, CompletionList, CompletionItemProvider, LanguageId, CompletionItemKind } from 'vs/editor/common/modes'; import { IModeService } from 'vs/editor/common/services/modeService'; import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { setSnippetSuggestSupport } from 'vs/editor/contrib/suggest/suggest'; @@ -365,53 +365,35 @@ export class SnippetSuggestProvider implements CompletionItemProvider { // } - provideCompletionItems(model: ITextModel, position: Position, context: CompletionContext): Promise { + provideCompletionItems(model: ITextModel, pos: Position): Promise { - const languageId = this._getLanguageIdAtPosition(model, position); + const languageId = this._getLanguageIdAtPosition(model, pos); return this._snippets.getSnippets(languageId).then(snippets => { - let suggestions: SnippetSuggestion[]; - let shift = Math.max(0, position.column - 100); - let pos = { lineNumber: position.lineNumber, column: Math.max(1, position.column - 100) }; - let lineOffsets: number[] = []; - let linePrefixLow = model.getLineContent(position.lineNumber).substr(Math.max(0, position.column - 100), position.column - 1).toLowerCase(); - - while (pos.column < position.column) { - let word = model.getWordAtPosition(pos); - if (word) { - // at a word - lineOffsets.push(word.startColumn - 1); - pos.column = word.endColumn + 1; - - if (word.endColumn - 1 < linePrefixLow.length && !/\s/.test(linePrefixLow[word.endColumn - 1])) { - lineOffsets.push(word.endColumn - 1); - } + let suggestions: SnippetSuggestion[] = []; + let atWord = Boolean(model.getWordAtPosition(pos)); + let lineLow = model.getLineContent(pos.lineNumber).substring(0, pos.column - 1).toLowerCase(); - } else if (!/\s/.test(linePrefixLow[pos.column - 1])) { - // at a none-whitespace character - lineOffsets.push(pos.column - 1); - pos.column += 1; - } else { - // always advance! - pos.column += 1; - } - } + for (const snippet of snippets) { - if (lineOffsets.length === 0) { - // no interesting spans found -> pick all snippets - suggestions = snippets.map(snippet => new SnippetSuggestion(snippet, Range.fromPositions(position))); - - } else { - let consumed = new Set(); - suggestions = []; - for (let start of lineOffsets) { - start -= shift; - for (const snippet of snippets) { - if (!consumed.has(snippet) && matches(linePrefixLow, start, snippet.prefixLow, 0)) { - suggestions.push(new SnippetSuggestion(snippet, Range.fromPositions(position.delta(0, -(linePrefixLow.length - start)), position))); - consumed.add(snippet); - } + let prefixLow = snippet.prefix; + let prefixPos = prefixLow.length - 1; + let linePos = lineLow.length - 1; + let linePosStart = linePos; + while (linePos >= 0 && prefixPos >= 0) { + if (lineLow[linePos] === prefixLow[prefixPos]) { + linePos -= 1; } + prefixPos -= 1; + } + + if (linePos !== linePosStart) { + // some overlap + suggestions.push(new SnippetSuggestion(snippet, Range.fromPositions(pos.delta(0, linePos - linePosStart), pos))); + + } else if (!atWord) { + // no overlap but not at a word + suggestions.push(new SnippetSuggestion(snippet, Range.fromPositions(pos))); } } @@ -451,16 +433,6 @@ export class SnippetSuggestProvider implements CompletionItemProvider { } } -function matches(pattern: string, patternStart: number, word: string, wordStart: number): boolean { - while (patternStart < pattern.length && wordStart < word.length) { - if (pattern[patternStart] === word[wordStart]) { - patternStart += 1; - } - wordStart += 1; - } - return patternStart === pattern.length; -} - export function getNonWhitespacePrefix(model: ISimpleModel, position: Position): string { /** * Do not analyze more characters diff --git a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts index 37e132671ea..2de580f6a71 100644 --- a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts +++ b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts @@ -11,7 +11,6 @@ import { ModeServiceImpl } from 'vs/editor/common/services/modeServiceImpl'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ISnippetsService } from 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; import { Snippet, SnippetSource } from 'vs/workbench/parts/snippets/electron-browser/snippetsFile'; -import { CompletionContext, CompletionTriggerKind } from 'vs/editor/common/modes'; class SimpleSnippetService implements ISnippetsService { _serviceBrand: any; @@ -39,7 +38,6 @@ suite('SnippetsService', function () { let modeService: ModeServiceImpl; let snippetService: ISnippetsService; - let suggestContext: CompletionContext = { triggerKind: CompletionTriggerKind.Invoke }; setup(function () { modeService = new ModeServiceImpl(); @@ -68,7 +66,7 @@ suite('SnippetsService', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); const model = TextModel.createFromString('', undefined, modeService.getLanguageIdentifier('fooLang')); - return provider.provideCompletionItems(model, new Position(1, 1), suggestContext).then(result => { + return provider.provideCompletionItems(model, new Position(1, 1)).then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 2); }); @@ -79,7 +77,7 @@ suite('SnippetsService', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); const model = TextModel.createFromString('bar', undefined, modeService.getLanguageIdentifier('fooLang')); - return provider.provideCompletionItems(model, new Position(1, 4), suggestContext).then(result => { + return provider.provideCompletionItems(model, new Position(1, 4)).then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 1); assert.equal(result.suggestions[0].label, 'bar'); @@ -111,7 +109,7 @@ suite('SnippetsService', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); const model = TextModel.createFromString('bar-bar', undefined, modeService.getLanguageIdentifier('fooLang')); - await provider.provideCompletionItems(model, new Position(1, 3), suggestContext).then(result => { + await provider.provideCompletionItems(model, new Position(1, 3)).then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 2); assert.equal(result.suggestions[0].label, 'bar'); @@ -122,7 +120,7 @@ suite('SnippetsService', function () { assert.equal(result.suggestions[1].range.startColumn, 1); }); - await provider.provideCompletionItems(model, new Position(1, 5), suggestContext).then(result => { + await provider.provideCompletionItems(model, new Position(1, 5)).then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 1); assert.equal(result.suggestions[0].label, 'bar-bar'); @@ -130,7 +128,7 @@ suite('SnippetsService', function () { assert.equal(result.suggestions[0].range.startColumn, 1); }); - await provider.provideCompletionItems(model, new Position(1, 6), suggestContext).then(result => { + await provider.provideCompletionItems(model, new Position(1, 6)).then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 2); assert.equal(result.suggestions[0].label, 'bar'); @@ -156,19 +154,19 @@ suite('SnippetsService', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); let model = TextModel.createFromString('\t { + return provider.provideCompletionItems(model, new Position(1, 7)).then(result => { assert.equal(result.suggestions.length, 1); model.dispose(); model = TextModel.createFromString('\t { assert.equal(result.suggestions.length, 1); assert.equal(result.suggestions[0].range.startColumn, 2); model.dispose(); model = TextModel.createFromString('a { assert.equal(result.suggestions.length, 1); assert.equal(result.suggestions[0].range.startColumn, 2); @@ -191,9 +189,9 @@ suite('SnippetsService', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); let model = TextModel.createFromString('\n\t\n>/head>', undefined, modeService.getLanguageIdentifier('fooLang')); - return provider.provideCompletionItems(model, new Position(1, 1), suggestContext).then(result => { + return provider.provideCompletionItems(model, new Position(1, 1)).then(result => { assert.equal(result.suggestions.length, 1); - return provider.provideCompletionItems(model, new Position(2, 2), suggestContext); + return provider.provideCompletionItems(model, new Position(2, 2)); }).then(result => { assert.equal(result.suggestions.length, 1); }); @@ -221,7 +219,7 @@ suite('SnippetsService', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); let model = TextModel.createFromString('', undefined, modeService.getLanguageIdentifier('fooLang')); - return provider.provideCompletionItems(model, new Position(1, 1), suggestContext).then(result => { + return provider.provideCompletionItems(model, new Position(1, 1)).then(result => { assert.equal(result.suggestions.length, 2); let [first, second] = result.suggestions; assert.equal(first.label, 'first'); @@ -243,13 +241,13 @@ suite('SnippetsService', function () { let model = TextModel.createFromString('p-', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 2), suggestContext); + let result = await provider.provideCompletionItems(model, new Position(1, 2)); assert.equal(result.suggestions.length, 1); - result = await provider.provideCompletionItems(model, new Position(1, 3), suggestContext); + result = await provider.provideCompletionItems(model, new Position(1, 3)); assert.equal(result.suggestions.length, 1); - result = await provider.provideCompletionItems(model, new Position(1, 3), { triggerCharacter: '-', triggerKind: CompletionTriggerKind.TriggerCharacter }); + result = await provider.provideCompletionItems(model, new Position(1, 3)); assert.equal(result.suggestions.length, 1); }); @@ -267,7 +265,26 @@ suite('SnippetsService', function () { const provider = new SnippetSuggestProvider(modeService, snippetService); let model = TextModel.createFromString('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 158), suggestContext); + let result = await provider.provideCompletionItems(model, new Position(1, 158)); + + assert.equal(result.suggestions.length, 1); + }); + + test('No snippets suggestion beyond character 100 if not at end of line #60247', async function () { + snippetService = new SimpleSnippetService([new Snippet( + ['fooLang'], + 'bug', + 'bug', + '', + 'second', + '', + SnippetSource.User + )]); + + const provider = new SnippetSuggestProvider(modeService, snippetService); + + let model = TextModel.createFromString('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b text_after_b', undefined, modeService.getLanguageIdentifier('fooLang')); + let result = await provider.provideCompletionItems(model, new Position(1, 158)); assert.equal(result.suggestions.length, 1); }); -- GitLab