mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 13:46:13 +00:00
fix #60247
This commit is contained in:
parent
099c23febc
commit
53321de6d3
|
@ -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<CompletionList> {
|
||||
provideCompletionItems(model: ITextModel, pos: Position): Promise<CompletionList> {
|
||||
|
||||
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();
|
||||
let suggestions: SnippetSuggestion[] = [];
|
||||
let atWord = Boolean(model.getWordAtPosition(pos));
|
||||
let lineLow = model.getLineContent(pos.lineNumber).substring(0, pos.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;
|
||||
for (const snippet of snippets) {
|
||||
|
||||
if (word.endColumn - 1 < linePrefixLow.length && !/\s/.test(linePrefixLow[word.endColumn - 1])) {
|
||||
lineOffsets.push(word.endColumn - 1);
|
||||
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;
|
||||
}
|
||||
|
||||
} 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;
|
||||
prefixPos -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (lineOffsets.length === 0) {
|
||||
// no interesting spans found -> pick all snippets
|
||||
suggestions = snippets.map(snippet => new SnippetSuggestion(snippet, Range.fromPositions(position)));
|
||||
if (linePos !== linePosStart) {
|
||||
// some overlap
|
||||
suggestions.push(new SnippetSuggestion(snippet, Range.fromPositions(pos.delta(0, linePos - linePosStart), pos)));
|
||||
|
||||
} else {
|
||||
let consumed = new Set<Snippet>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
} 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
|
||||
|
|
|
@ -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<?php', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 7), suggestContext).then(result => {
|
||||
return provider.provideCompletionItems(model, new Position(1, 7)).then(result => {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
model.dispose();
|
||||
|
||||
model = TextModel.createFromString('\t<?', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), suggestContext);
|
||||
return provider.provideCompletionItems(model, new Position(1, 4));
|
||||
}).then(result => {
|
||||
assert.equal(result.suggestions.length, 1);
|
||||
assert.equal(result.suggestions[0].range.startColumn, 2);
|
||||
model.dispose();
|
||||
|
||||
model = TextModel.createFromString('a<?', undefined, modeService.getLanguageIdentifier('fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), suggestContext);
|
||||
return provider.provideCompletionItems(model, new Position(1, 4));
|
||||
}).then(result => {
|
||||
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('<head>\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);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue