mirror of
https://github.com/Microsoft/vscode
synced 2024-10-01 08:50:48 +00:00
Joh/rainy-mollusk (#200976)
* snippet completions should also check with the completion model this will allow to return "more" from the snippet completion item provider and rely on the completions model to do some filtering * when debugging tests allow to use console.log * fix https://github.com/microsoft/vscode/issues/191070
This commit is contained in:
parent
74bf30a8ec
commit
a5698e8857
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { MarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { compare, compareSubstring, firstNonWhitespaceIndex } from 'vs/base/common/strings';
|
||||
import { compare, compareSubstring } from 'vs/base/common/strings';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
|
@ -17,9 +17,9 @@ import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/sn
|
|||
import { isPatternInWord } from 'vs/base/common/filters';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
|
||||
import { getWordAtText } from 'vs/editor/common/core/wordHelper';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
|
||||
|
||||
|
||||
const markSnippetAsUsed = '_snippet.markAsUsed';
|
||||
|
@ -70,6 +70,12 @@ export class SnippetCompletion implements CompletionItem {
|
|||
}
|
||||
}
|
||||
|
||||
interface ISnippetPosition {
|
||||
startColumn: number;
|
||||
prefixLow: string;
|
||||
isWord: boolean;
|
||||
}
|
||||
|
||||
export class SnippetCompletionProvider implements CompletionItemProvider {
|
||||
|
||||
readonly _debugDisplayName = 'snippetCompletions';
|
||||
|
@ -85,97 +91,100 @@ export class SnippetCompletionProvider implements CompletionItemProvider {
|
|||
async provideCompletionItems(model: ITextModel, position: Position, context: CompletionContext): Promise<CompletionList> {
|
||||
|
||||
const sw = new StopWatch();
|
||||
const lineContentLow = model.getLineContent(position.lineNumber).toLowerCase();
|
||||
const wordUntil = model.getWordUntilPosition(position).word.toLowerCase();
|
||||
|
||||
// compute all snippet anchors: word starts and every non word character
|
||||
const line = position.lineNumber;
|
||||
const word = model.getWordAtPosition(position) ?? { startColumn: position.column, endColumn: position.column, word: '' };
|
||||
|
||||
const lineContentLow = model.getLineContent(position.lineNumber).toLowerCase();
|
||||
const lineContentWithWordLow = lineContentLow.substring(0, word.startColumn + word.word.length - 1);
|
||||
const anchors = this._computeSnippetPositions(model, line, word, lineContentWithWordLow);
|
||||
|
||||
// loop over possible snippets and match them against the anchors
|
||||
const columnOffset = position.column - 1;
|
||||
const triggerCharacterLow = context.triggerCharacter?.toLowerCase() ?? '';
|
||||
const languageId = this._getLanguageIdAtPosition(model, position);
|
||||
const languageConfig = this._languageConfigurationService.getLanguageConfiguration(languageId);
|
||||
const snippets = new Set(await this._snippets.getSnippets(languageId));
|
||||
|
||||
const suggestions: SnippetCompletion[] = [];
|
||||
const columnOffset = position.column - 1;
|
||||
|
||||
const triggerCharacterLow = context.triggerCharacter?.toLowerCase() ?? '';
|
||||
|
||||
|
||||
snippet: for (const snippet of snippets) {
|
||||
for (const snippet of snippets) {
|
||||
|
||||
if (context.triggerKind === CompletionTriggerKind.TriggerCharacter && !snippet.prefixLow.startsWith(triggerCharacterLow)) {
|
||||
// strict -> when having trigger characters they must prefix-match
|
||||
continue snippet;
|
||||
continue;
|
||||
}
|
||||
|
||||
const word = getWordAtText(1, languageConfig.getWordDefinition(), snippet.prefixLow, 0);
|
||||
let candidate: ISnippetPosition | undefined;
|
||||
for (const anchor of anchors) {
|
||||
|
||||
if (wordUntil && word && !isPatternInWord(wordUntil, 0, wordUntil.length, snippet.prefixLow, 0, snippet.prefixLow.length)) {
|
||||
// when at a word the snippet prefix must match
|
||||
continue snippet;
|
||||
if (anchor.prefixLow.match(/^\s/) && !snippet.prefixLow.match(/^\s/)) {
|
||||
// only allow whitespace anchor when snippet prefix starts with whitespace too
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isPatternInWord(anchor.prefixLow, 0, anchor.prefixLow.length, snippet.prefixLow, 0, snippet.prefixLow.length)) {
|
||||
candidate = anchor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// don't eat into leading whitespace unless the snippet prefix starts with whitespace
|
||||
const minPos = firstNonWhitespaceIndex(snippet.prefixLow) === 0
|
||||
? Math.max(0, model.getLineFirstNonWhitespaceColumn(position.lineNumber) - 1)
|
||||
: 0;
|
||||
|
||||
column: for (let pos = Math.max(minPos, columnOffset - snippet.prefixLow.length); pos < lineContentLow.length; pos++) {
|
||||
|
||||
if (!isPatternInWord(lineContentLow, pos, columnOffset, snippet.prefixLow, 0, snippet.prefixLow.length)) {
|
||||
continue column;
|
||||
}
|
||||
|
||||
const prefixRestLen = snippet.prefixLow.length - (columnOffset - pos);
|
||||
const endsWithPrefixRest = compareSubstring(lineContentLow, snippet.prefixLow, columnOffset, columnOffset + prefixRestLen, columnOffset - pos);
|
||||
const startPosition = position.with(undefined, pos + 1);
|
||||
|
||||
if (wordUntil && position.equals(startPosition)) {
|
||||
// at word-end but no overlap
|
||||
continue snippet;
|
||||
}
|
||||
|
||||
let endColumn = endsWithPrefixRest === 0 ? position.column + prefixRestLen : position.column;
|
||||
|
||||
// First check if there is anything to the right of the cursor
|
||||
if (columnOffset < lineContentLow.length) {
|
||||
const autoClosingPairs = languageConfig.getAutoClosingPairs();
|
||||
const standardAutoClosingPairConditionals = autoClosingPairs.autoClosingPairsCloseSingleChar.get(lineContentLow[columnOffset]);
|
||||
// If the character to the right of the cursor is a closing character of an autoclosing pair
|
||||
if (standardAutoClosingPairConditionals?.some(p =>
|
||||
// and the start position is the opening character of an autoclosing pair
|
||||
p.open === lineContentLow[startPosition.column - 1] &&
|
||||
// and the snippet prefix contains the opening and closing pair at its edges
|
||||
snippet.prefix.startsWith(p.open) &&
|
||||
snippet.prefix[snippet.prefix.length - 1] === p.close)
|
||||
) {
|
||||
// Eat the character that was likely inserted because of auto-closing pairs
|
||||
endColumn++;
|
||||
}
|
||||
}
|
||||
|
||||
const replace = Range.fromPositions(startPosition, { lineNumber: position.lineNumber, column: endColumn });
|
||||
const insert = replace.setEndPosition(position.lineNumber, position.column);
|
||||
|
||||
suggestions.push(new SnippetCompletion(snippet, { replace, insert }));
|
||||
snippets.delete(snippet);
|
||||
break;
|
||||
if (!candidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pos = candidate.startColumn - 1;
|
||||
|
||||
const prefixRestLen = snippet.prefixLow.length - (columnOffset - pos);
|
||||
const endsWithPrefixRest = compareSubstring(lineContentLow, snippet.prefixLow, columnOffset, columnOffset + prefixRestLen, columnOffset - pos);
|
||||
const startPosition = position.with(undefined, pos + 1);
|
||||
|
||||
let endColumn = endsWithPrefixRest === 0 ? position.column + prefixRestLen : position.column;
|
||||
|
||||
// First check if there is anything to the right of the cursor
|
||||
if (columnOffset < lineContentLow.length) {
|
||||
const autoClosingPairs = languageConfig.getAutoClosingPairs();
|
||||
const standardAutoClosingPairConditionals = autoClosingPairs.autoClosingPairsCloseSingleChar.get(lineContentLow[columnOffset]);
|
||||
// If the character to the right of the cursor is a closing character of an autoclosing pair
|
||||
if (standardAutoClosingPairConditionals?.some(p =>
|
||||
// and the start position is the opening character of an autoclosing pair
|
||||
p.open === lineContentLow[startPosition.column - 1] &&
|
||||
// and the snippet prefix contains the opening and closing pair at its edges
|
||||
snippet.prefix.startsWith(p.open) &&
|
||||
snippet.prefix[snippet.prefix.length - 1] === p.close)
|
||||
) {
|
||||
// Eat the character that was likely inserted because of auto-closing pairs
|
||||
endColumn++;
|
||||
}
|
||||
}
|
||||
|
||||
const replace = Range.fromPositions({ lineNumber: line, column: candidate.startColumn }, { lineNumber: line, column: endColumn });
|
||||
const insert = replace.setEndPosition(line, position.column);
|
||||
|
||||
suggestions.push(new SnippetCompletion(snippet, { replace, insert }));
|
||||
snippets.delete(snippet);
|
||||
}
|
||||
|
||||
|
||||
// add remaing snippets when the current prefix ends in whitespace or when line is empty
|
||||
// and when not having a trigger character
|
||||
if (!triggerCharacterLow) {
|
||||
const endsInWhitespace = /\s/.test(lineContentLow[position.column - 2]);
|
||||
if (endsInWhitespace || !lineContentLow /*empty line*/) {
|
||||
for (const snippet of snippets) {
|
||||
const insert = Range.fromPositions(position);
|
||||
const replace = lineContentLow.indexOf(snippet.prefixLow, columnOffset) === columnOffset ? insert.setEndPosition(position.lineNumber, position.column + snippet.prefixLow.length) : insert;
|
||||
suggestions.push(new SnippetCompletion(snippet, { replace, insert }));
|
||||
}
|
||||
if (!triggerCharacterLow && (/\s/.test(lineContentLow[position.column - 2]) /*end in whitespace */ || !lineContentLow /*empty line*/)) {
|
||||
for (const snippet of snippets) {
|
||||
const insert = Range.fromPositions(position);
|
||||
const replace = lineContentLow.indexOf(snippet.prefixLow, columnOffset) === columnOffset ? insert.setEndPosition(position.lineNumber, position.column + snippet.prefixLow.length) : insert;
|
||||
suggestions.push(new SnippetCompletion(snippet, { replace, insert }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// dismbiguate suggestions with same labels
|
||||
this._disambiguateSnippets(suggestions);
|
||||
|
||||
return {
|
||||
suggestions,
|
||||
duration: sw.elapsed()
|
||||
};
|
||||
}
|
||||
|
||||
private _disambiguateSnippets(suggestions: SnippetCompletion[]) {
|
||||
suggestions.sort(SnippetCompletion.compareByLabel);
|
||||
for (let i = 0; i < suggestions.length; i++) {
|
||||
const item = suggestions[i];
|
||||
|
@ -188,17 +197,45 @@ export class SnippetCompletionProvider implements CompletionItemProvider {
|
|||
i = to;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
suggestions,
|
||||
duration: sw.elapsed()
|
||||
};
|
||||
}
|
||||
|
||||
resolveCompletionItem(item: CompletionItem): CompletionItem {
|
||||
return (item instanceof SnippetCompletion) ? item.resolve() : item;
|
||||
}
|
||||
|
||||
private _computeSnippetPositions(model: ITextModel, line: number, word: IWordAtPosition, lineContentWithWordLow: string): ISnippetPosition[] {
|
||||
const result: ISnippetPosition[] = [];
|
||||
|
||||
for (let column = 1; column < word.startColumn; column++) {
|
||||
const wordInfo = model.getWordAtPosition(new Position(line, column));
|
||||
result.push({
|
||||
startColumn: column,
|
||||
prefixLow: lineContentWithWordLow.substring(column - 1),
|
||||
isWord: Boolean(wordInfo)
|
||||
});
|
||||
if (wordInfo) {
|
||||
column = wordInfo.endColumn;
|
||||
|
||||
// the character right after a word is an anchor, always
|
||||
result.push({
|
||||
startColumn: wordInfo.endColumn,
|
||||
prefixLow: lineContentWithWordLow.substring(wordInfo.endColumn - 1),
|
||||
isWord: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (word.word.length > 0 || result.length === 0) {
|
||||
result.push({
|
||||
startColumn: word.startColumn,
|
||||
prefixLow: lineContentWithWordLow.substring(word.startColumn - 1),
|
||||
isWord: true
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _getLanguageIdAtPosition(model: ITextModel, position: Position): string {
|
||||
// validate the `languageId` to ensure this is a user
|
||||
// facing language with a name and the chance to have
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { SnippetCompletion, SnippetCompletionProvider } from 'vs/workbench/contrib/snippets/browser/snippetCompletionProvider';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { createModelServices, instantiateTextModel } from 'vs/editor/test/common/testTextModel';
|
||||
import { ISnippetsService } from 'vs/workbench/contrib/snippets/browser/snippets';
|
||||
import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile';
|
||||
|
@ -17,6 +17,11 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/
|
|||
import { ILanguageService } from 'vs/editor/common/languages/language';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { CompletionModel } from 'vs/editor/contrib/suggest/browser/completionModel';
|
||||
import { CompletionItem } from 'vs/editor/contrib/suggest/browser/suggest';
|
||||
import { WordDistance } from 'vs/editor/contrib/suggest/browser/wordDistance';
|
||||
import { EditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
|
||||
class SimpleSnippetService implements ISnippetsService {
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
@ -42,7 +47,7 @@ class SimpleSnippetService implements ISnippetsService {
|
|||
}
|
||||
|
||||
suite('SnippetsService', function () {
|
||||
const context: CompletionContext = { triggerKind: CompletionTriggerKind.Invoke };
|
||||
const defaultCompletionContext: CompletionContext = { triggerKind: CompletionTriggerKind.Invoke };
|
||||
|
||||
let disposables: DisposableStore;
|
||||
let instantiationService: TestInstantiationService;
|
||||
|
@ -86,15 +91,33 @@ suite('SnippetsService', function () {
|
|||
|
||||
ensureNoDisposablesAreLeakedInTestSuite();
|
||||
|
||||
test('snippet completions - simple', function () {
|
||||
async function asCompletionModel(model: ITextModel, position: IPosition, provider: SnippetCompletionProvider, context: CompletionContext = defaultCompletionContext) {
|
||||
|
||||
const list = await provider.provideCompletionItems(model, Position.lift(position), context);
|
||||
|
||||
const result = new CompletionModel(list.suggestions.map(s => {
|
||||
return new CompletionItem(position, s, list, provider);
|
||||
}),
|
||||
position.column,
|
||||
{ characterCountDelta: 0, leadingLineContent: model.getLineContent(position.lineNumber).substring(0, position.column - 1) },
|
||||
WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
test('snippet completions - simple', async function () {
|
||||
|
||||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, '', 'fooLang'));
|
||||
|
||||
return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => {
|
||||
await provider.provideCompletionItems(model, new Position(1, 1), defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
});
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 1), provider);
|
||||
assert.strictEqual(completions.items.length, 2);
|
||||
});
|
||||
|
||||
test('snippet completions - simple 2', async function () {
|
||||
|
@ -102,23 +125,29 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, 'hello ', 'fooLang'));
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 6) /* hello| */, context)!.then(result => {
|
||||
await provider.provideCompletionItems(model, new Position(1, 6) /* hello| */, defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 0);
|
||||
});
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 7) /* hello |*/, context)!.then(result => {
|
||||
await provider.provideCompletionItems(model, new Position(1, 7) /* hello |*/, defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
});
|
||||
|
||||
const completions1 = await asCompletionModel(model, new Position(1, 6)/* hello| */, provider);
|
||||
assert.strictEqual(completions1.items.length, 0);
|
||||
|
||||
const completions2 = await asCompletionModel(model, new Position(1, 7)/* hello |*/, provider);
|
||||
assert.strictEqual(completions2.items.length, 2);
|
||||
});
|
||||
|
||||
test('snippet completions - with prefix', function () {
|
||||
test('snippet completions - with prefix', async function () {
|
||||
|
||||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, 'bar', 'fooLang'));
|
||||
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), context)!.then(result => {
|
||||
await provider.provideCompletionItems(model, new Position(1, 4), defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
|
@ -128,6 +157,15 @@ suite('SnippetsService', function () {
|
|||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 1);
|
||||
assert.strictEqual(result.suggestions[0].insertText, 'barCodeSnippet');
|
||||
});
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 4), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.deepStrictEqual(completions.items[0].completion.label, {
|
||||
label: 'bar',
|
||||
description: 'barTest'
|
||||
});
|
||||
assert.strictEqual((completions.items[0].completion.range as any).insert.startColumn, 1);
|
||||
assert.strictEqual(completions.items[0].completion.insertText, 'barCodeSnippet');
|
||||
});
|
||||
|
||||
test('snippet completions - with different prefixes', async function () {
|
||||
|
@ -156,63 +194,118 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, 'bar-bar', 'fooLang'));
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 3), context)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
{
|
||||
await provider.provideCompletionItems(model, new Position(1, 3), defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
label: 'bar',
|
||||
description: 'barTest'
|
||||
});
|
||||
assert.strictEqual(result.suggestions[0].insertText, 's1');
|
||||
assert.strictEqual((result.suggestions[0].range as CompletionItemRanges).insert.startColumn, 1);
|
||||
assert.deepStrictEqual(result.suggestions[1].label, {
|
||||
label: 'bar-bar',
|
||||
description: 'name'
|
||||
});
|
||||
assert.strictEqual(result.suggestions[1].insertText, 's2');
|
||||
assert.strictEqual((result.suggestions[1].range as CompletionItemRanges).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 3), provider);
|
||||
assert.strictEqual(completions.items.length, 2);
|
||||
assert.deepStrictEqual(completions.items[0].completion.label, {
|
||||
label: 'bar',
|
||||
description: 'barTest'
|
||||
});
|
||||
assert.strictEqual(result.suggestions[0].insertText, 's1');
|
||||
assert.strictEqual((result.suggestions[0].range as CompletionItemRanges).insert.startColumn, 1);
|
||||
assert.deepStrictEqual(result.suggestions[1].label, {
|
||||
assert.strictEqual(completions.items[0].completion.insertText, 's1');
|
||||
assert.strictEqual((completions.items[0].completion.range as CompletionItemRanges).insert.startColumn, 1);
|
||||
assert.deepStrictEqual(completions.items[1].completion.label, {
|
||||
label: 'bar-bar',
|
||||
description: 'name'
|
||||
});
|
||||
assert.strictEqual(result.suggestions[1].insertText, 's2');
|
||||
assert.strictEqual((result.suggestions[1].range as CompletionItemRanges).insert.startColumn, 1);
|
||||
});
|
||||
assert.strictEqual(completions.items[1].completion.insertText, 's2');
|
||||
assert.strictEqual((completions.items[1].completion.range as CompletionItemRanges).insert.startColumn, 1);
|
||||
}
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 5), context)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
{
|
||||
await provider.provideCompletionItems(model, new Position(1, 5), defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
|
||||
const [first, second] = result.suggestions;
|
||||
const [first, second] = result.suggestions;
|
||||
|
||||
assert.deepStrictEqual(first.label, {
|
||||
label: 'bar',
|
||||
description: 'barTest'
|
||||
});
|
||||
assert.strictEqual(first.insertText, 's1');
|
||||
assert.strictEqual((first.range as CompletionItemRanges).insert.startColumn, 5);
|
||||
|
||||
assert.deepStrictEqual(second.label, {
|
||||
label: 'bar-bar',
|
||||
description: 'name'
|
||||
});
|
||||
assert.strictEqual(second.insertText, 's2');
|
||||
assert.strictEqual((second.range as CompletionItemRanges).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 5), provider);
|
||||
assert.strictEqual(completions.items.length, 2);
|
||||
|
||||
const [first, second] = completions.items.map(i => i.completion);
|
||||
|
||||
assert.deepStrictEqual(first.label, {
|
||||
label: 'bar',
|
||||
description: 'barTest'
|
||||
label: 'bar-bar',
|
||||
description: 'name'
|
||||
});
|
||||
assert.strictEqual(first.insertText, 's1');
|
||||
assert.strictEqual((first.range as CompletionItemRanges).insert.startColumn, 5);
|
||||
assert.strictEqual(first.insertText, 's2');
|
||||
assert.strictEqual((first.range as CompletionItemRanges).insert.startColumn, 1);
|
||||
|
||||
assert.deepStrictEqual(second.label, {
|
||||
label: 'bar-bar',
|
||||
description: 'name'
|
||||
});
|
||||
assert.strictEqual(second.insertText, 's2');
|
||||
assert.strictEqual((second.range as CompletionItemRanges).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(1, 6), context)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
label: 'bar',
|
||||
description: 'barTest'
|
||||
});
|
||||
assert.strictEqual(result.suggestions[0].insertText, 's1');
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 5);
|
||||
assert.deepStrictEqual(result.suggestions[1].label, {
|
||||
assert.strictEqual(second.insertText, 's1');
|
||||
assert.strictEqual((second.range as CompletionItemRanges).insert.startColumn, 5);
|
||||
}
|
||||
|
||||
{
|
||||
await provider.provideCompletionItems(model, new Position(1, 6), defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.incomplete, undefined);
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
label: 'bar',
|
||||
description: 'barTest'
|
||||
});
|
||||
assert.strictEqual(result.suggestions[0].insertText, 's1');
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 5);
|
||||
assert.deepStrictEqual(result.suggestions[1].label, {
|
||||
label: 'bar-bar',
|
||||
description: 'name'
|
||||
});
|
||||
assert.strictEqual(result.suggestions[1].insertText, 's2');
|
||||
assert.strictEqual((result.suggestions[1].range as any).insert.startColumn, 1);
|
||||
});
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 6), provider);
|
||||
assert.strictEqual(completions.items.length, 2);
|
||||
assert.deepStrictEqual(completions.items[0].completion.label, {
|
||||
label: 'bar-bar',
|
||||
description: 'name'
|
||||
});
|
||||
assert.strictEqual(result.suggestions[1].insertText, 's2');
|
||||
assert.strictEqual((result.suggestions[1].range as any).insert.startColumn, 1);
|
||||
});
|
||||
assert.strictEqual(completions.items[0].completion.insertText, 's2');
|
||||
assert.strictEqual((completions.items[0].completion.range as any).insert.startColumn, 1);
|
||||
assert.deepStrictEqual(completions.items[1].completion.label, {
|
||||
label: 'bar',
|
||||
description: 'barTest'
|
||||
});
|
||||
assert.strictEqual(completions.items[1].completion.insertText, 's1');
|
||||
assert.strictEqual((completions.items[1].completion.range as any).insert.startColumn, 5);
|
||||
}
|
||||
});
|
||||
|
||||
test('Cannot use "<?php" as user snippet prefix anymore, #26275', function () {
|
||||
test('Cannot use "<?php" as user snippet prefix anymore, #26275', async function () {
|
||||
snippetService = new SimpleSnippetService([new Snippet(
|
||||
false,
|
||||
['fooLang'],
|
||||
|
@ -228,27 +321,35 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
let model = instantiateTextModel(instantiationService, '\t<?php', 'fooLang');
|
||||
return provider.provideCompletionItems(model, new Position(1, 7), context)!.then(result => {
|
||||
await provider.provideCompletionItems(model, new Position(1, 7), defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
model.dispose();
|
||||
|
||||
model = instantiateTextModel(instantiationService, '\t<?', 'fooLang');
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), context)!;
|
||||
}).then(result => {
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
model.dispose();
|
||||
|
||||
model = instantiateTextModel(instantiationService, 'a<?', 'fooLang');
|
||||
return provider.provideCompletionItems(model, new Position(1, 4), context)!;
|
||||
}).then(result => {
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
model.dispose();
|
||||
});
|
||||
const completions1 = await asCompletionModel(model, new Position(1, 7), provider);
|
||||
assert.strictEqual(completions1.items.length, 1);
|
||||
|
||||
model.dispose();
|
||||
model = instantiateTextModel(instantiationService, '\t<?', 'fooLang');
|
||||
await provider.provideCompletionItems(model, new Position(1, 4), defaultCompletionContext).then(result => {
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
});
|
||||
const completions2 = await asCompletionModel(model, new Position(1, 4), provider);
|
||||
assert.strictEqual(completions2.items.length, 1);
|
||||
assert.strictEqual((completions2.items[0].completion.range as any).insert.startColumn, 2);
|
||||
|
||||
model.dispose();
|
||||
model = instantiateTextModel(instantiationService, 'a<?', 'fooLang');
|
||||
await provider.provideCompletionItems(model, new Position(1, 4), defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual((result.suggestions[0].range as any).insert.startColumn, 2);
|
||||
});
|
||||
const completions3 = await asCompletionModel(model, new Position(1, 4), provider);
|
||||
assert.strictEqual(completions3.items.length, 1);
|
||||
assert.strictEqual((completions3.items[0].completion.range as any).insert.startColumn, 2);
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('No user snippets in suggestions, when inside the code, #30508', function () {
|
||||
test('No user snippets in suggestions, when inside the code, #30508', async function () {
|
||||
|
||||
snippetService = new SimpleSnippetService([new Snippet(
|
||||
false,
|
||||
|
@ -265,15 +366,22 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, '<head>\n\t\n>/head>', 'fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => {
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
return provider.provideCompletionItems(model, new Position(2, 2), context)!;
|
||||
}).then(result => {
|
||||
await provider.provideCompletionItems(model, new Position(1, 1), defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
});
|
||||
const completions = await asCompletionModel(model, new Position(1, 1), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
|
||||
|
||||
await provider.provideCompletionItems(model, new Position(2, 2), defaultCompletionContext).then(result => {
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
});
|
||||
const completions2 = await asCompletionModel(model, new Position(2, 2), provider);
|
||||
assert.strictEqual(completions2.items.length, 1);
|
||||
|
||||
});
|
||||
|
||||
test('SnippetSuggest - ensure extension snippets come last ', function () {
|
||||
test('SnippetSuggest - ensure extension snippets come last ', async function () {
|
||||
snippetService = new SimpleSnippetService([new Snippet(
|
||||
false,
|
||||
['fooLang'],
|
||||
|
@ -299,7 +407,7 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, '', 'fooLang'));
|
||||
return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => {
|
||||
await provider.provideCompletionItems(model, new Position(1, 1), defaultCompletionContext)!.then(result => {
|
||||
assert.strictEqual(result.suggestions.length, 2);
|
||||
const [first, second] = result.suggestions;
|
||||
assert.deepStrictEqual(first.label, {
|
||||
|
@ -311,6 +419,18 @@ suite('SnippetsService', function () {
|
|||
description: 'second'
|
||||
});
|
||||
});
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 1), provider);
|
||||
assert.strictEqual(completions.items.length, 2);
|
||||
const [first, second] = completions.items;
|
||||
assert.deepStrictEqual(first.completion.label, {
|
||||
label: 'first',
|
||||
description: 'first'
|
||||
});
|
||||
assert.deepStrictEqual(second.completion.label, {
|
||||
label: 'second',
|
||||
description: 'second'
|
||||
});
|
||||
});
|
||||
|
||||
test('Dash in snippets prefix broken #53945', async function () {
|
||||
|
@ -329,14 +449,20 @@ suite('SnippetsService', function () {
|
|||
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, 'p-', 'fooLang'));
|
||||
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 2), context)!;
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 2), defaultCompletionContext)!;
|
||||
let completions = await asCompletionModel(model, new Position(1, 2), provider);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), defaultCompletionContext)!;
|
||||
completions = await asCompletionModel(model, new Position(1, 3), provider);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), defaultCompletionContext)!;
|
||||
completions = await asCompletionModel(model, new Position(1, 3), provider);
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
});
|
||||
|
||||
test('No snippets suggestion on long lines beyond character 100 #58807', async function () {
|
||||
|
@ -355,9 +481,11 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, 'Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b', 'fooLang'));
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 158), context)!;
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 158), defaultCompletionContext)!;
|
||||
const completions = await asCompletionModel(model, new Position(1, 158), provider);
|
||||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
});
|
||||
|
||||
test('Type colon will trigger snippet #60746', async function () {
|
||||
|
@ -376,9 +504,11 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, ':', 'fooLang'));
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 2), context)!;
|
||||
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 2), defaultCompletionContext)!;
|
||||
assert.strictEqual(result.suggestions.length, 0);
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 2), provider);
|
||||
assert.strictEqual(completions.items.length, 0);
|
||||
});
|
||||
|
||||
test('substring of prefix can\'t trigger snippet #60737', async function () {
|
||||
|
@ -397,13 +527,16 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, 'template', 'fooLang'));
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 9), context)!;
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 9), defaultCompletionContext);
|
||||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.deepStrictEqual(result.suggestions[0].label, {
|
||||
label: 'mytemplate',
|
||||
description: 'mytemplate'
|
||||
});
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 9), provider);
|
||||
assert.strictEqual(completions.items.length, 0);
|
||||
});
|
||||
|
||||
test('No snippets suggestion beyond character 100 if not at end of line #60247', async function () {
|
||||
|
@ -422,9 +555,12 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, 'Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b text_after_b', 'fooLang'));
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 158), context)!;
|
||||
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 158), defaultCompletionContext)!;
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 158), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
});
|
||||
|
||||
test('issue #61296: VS code freezes when editing CSS file with emoji', async function () {
|
||||
|
@ -448,9 +584,12 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, languageConfigurationService);
|
||||
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, '.🐷-a-b', 'fooLang'));
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 8), context)!;
|
||||
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 8), defaultCompletionContext)!;
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 8), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
});
|
||||
|
||||
test('No snippets shown when triggering completions at whitespace on line that already has text #62335', async function () {
|
||||
|
@ -469,9 +608,12 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
const model = disposables.add(instantiateTextModel(instantiationService, 'a ', 'fooLang'));
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 3), defaultCompletionContext)!;
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 3), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
});
|
||||
|
||||
test('Snippet prefix with special chars and numbers does not work #62906', async function () {
|
||||
|
@ -500,19 +642,27 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
let model = instantiateTextModel(instantiationService, ' <', 'fooLang');
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 3), defaultCompletionContext)!;
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
let [first] = result.suggestions;
|
||||
assert.strictEqual((first.range as any).insert.startColumn, 2);
|
||||
model.dispose();
|
||||
|
||||
let completions = await asCompletionModel(model, new Position(1, 3), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].editStart.column, 2);
|
||||
|
||||
model.dispose();
|
||||
model = instantiateTextModel(instantiationService, '1', 'fooLang');
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 2), context)!;
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 2), defaultCompletionContext)!;
|
||||
completions = await asCompletionModel(model, new Position(1, 2), provider);
|
||||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
[first] = result.suggestions;
|
||||
assert.strictEqual((first.range as any).insert.startColumn, 1);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].editStart.column, 1);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
|
@ -532,30 +682,46 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
let model = instantiateTextModel(instantiationService, 'not wordFoo bar', 'fooLang');
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
|
||||
let result = await provider.provideCompletionItems(model, new Position(1, 3), defaultCompletionContext)!;
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
let [first] = result.suggestions;
|
||||
assert.strictEqual((first.range as any).insert.endColumn, 3);
|
||||
assert.strictEqual((first.range as any).replace.endColumn, 9);
|
||||
model.dispose();
|
||||
|
||||
let completions = await asCompletionModel(model, new Position(1, 3), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].editInsertEnd.column, 3);
|
||||
assert.strictEqual(completions.items[0].editReplaceEnd.column, 9);
|
||||
|
||||
model.dispose();
|
||||
model = instantiateTextModel(instantiationService, 'not woFoo bar', 'fooLang');
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), context)!;
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 3), defaultCompletionContext)!;
|
||||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
[first] = result.suggestions;
|
||||
assert.strictEqual((first.range as any).insert.endColumn, 3);
|
||||
assert.strictEqual((first.range as any).replace.endColumn, 3);
|
||||
model.dispose();
|
||||
|
||||
completions = await asCompletionModel(model, new Position(1, 3), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].editInsertEnd.column, 3);
|
||||
assert.strictEqual(completions.items[0].editReplaceEnd.column, 3);
|
||||
|
||||
model.dispose();
|
||||
model = instantiateTextModel(instantiationService, 'not word', 'fooLang');
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 1), context)!;
|
||||
result = await provider.provideCompletionItems(model, new Position(1, 1), defaultCompletionContext)!;
|
||||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
[first] = result.suggestions;
|
||||
assert.strictEqual((first.range as any).insert.endColumn, 1);
|
||||
assert.strictEqual((first.range as any).replace.endColumn, 9);
|
||||
|
||||
completions = await asCompletionModel(model, new Position(1, 1), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].editInsertEnd.column, 1);
|
||||
assert.strictEqual(completions.items[0].editReplaceEnd.column, 9);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
|
@ -576,12 +742,18 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
const model = instantiateTextModel(instantiationService, 'filler e KEEP ng filler', 'fooLang');
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 9), context)!;
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 9), defaultCompletionContext)!;
|
||||
const completions = await asCompletionModel(model, new Position(1, 9), provider);
|
||||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
const [first] = result.suggestions;
|
||||
assert.strictEqual((first.range as any).insert.endColumn, 9);
|
||||
assert.strictEqual((first.range as any).replace.endColumn, 9);
|
||||
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].editInsertEnd.column, 9);
|
||||
assert.strictEqual(completions.items[0].editReplaceEnd.column, 9);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
|
@ -610,13 +782,19 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, languageConfigurationService);
|
||||
|
||||
const model = instantiateTextModel(instantiationService, '[psc]', 'fooLang');
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 5), context)!;
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 5), defaultCompletionContext)!;
|
||||
const completions = await asCompletionModel(model, new Position(1, 5), provider);
|
||||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
const [first] = result.suggestions;
|
||||
assert.strictEqual((first.range as any).insert.endColumn, 5);
|
||||
// This is 6 because it should eat the `]` at the end of the text even if cursor is before it
|
||||
assert.strictEqual((first.range as any).replace.endColumn, 6);
|
||||
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].editInsertEnd.column, 5);
|
||||
assert.strictEqual(completions.items[0].editReplaceEnd.column, 6);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
|
@ -637,13 +815,18 @@ suite('SnippetsService', function () {
|
|||
const provider = new SnippetCompletionProvider(languageService, snippetService, disposables.add(new TestLanguageConfigurationService()));
|
||||
|
||||
const model = instantiateTextModel(instantiationService, ' ci', 'fooLang');
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 4), context)!;
|
||||
const result = await provider.provideCompletionItems(model, new Position(1, 4), defaultCompletionContext)!;
|
||||
const completions = await asCompletionModel(model, new Position(1, 4), provider);
|
||||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
const [first] = result.suggestions;
|
||||
assert.strictEqual((<CompletionItemLabel>first.label).label, ' cite');
|
||||
assert.strictEqual((<CompletionItemRanges>first.range).insert.startColumn, 1);
|
||||
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].textLabel, ' cite');
|
||||
assert.strictEqual(completions.items[0].editStart.column, 1);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
|
@ -688,6 +871,10 @@ suite('SnippetsService', function () {
|
|||
)!;
|
||||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 2), provider, { triggerKind: CompletionTriggerKind.TriggerCharacter, triggerCharacter: '\'' });
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
|
@ -709,6 +896,11 @@ suite('SnippetsService', function () {
|
|||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual((<SnippetCompletion>result.suggestions[0]).label.label, 'hell_or_tell');
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 8), provider, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].textLabel, 'hell_or_tell');
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
|
@ -730,6 +922,12 @@ suite('SnippetsService', function () {
|
|||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual((<SnippetCompletion>result.suggestions[0]).label.label, '^y');
|
||||
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 5), provider, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].textLabel, '^y');
|
||||
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
|
@ -750,6 +948,10 @@ suite('SnippetsService', function () {
|
|||
|
||||
assert.strictEqual(result.suggestions.length, 1);
|
||||
assert.strictEqual((<SnippetCompletion>result.suggestions[0]).label.label, 'foobarrrrrr');
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 7), provider, { triggerKind: CompletionTriggerKind.Invoke });
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].textLabel, 'foobarrrrrr');
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
|
@ -818,9 +1020,13 @@ suite('SnippetsService', function () {
|
|||
assert.strictEqual(result.suggestions.length, 1);
|
||||
const first = result.suggestions[0];
|
||||
assert.strictEqual((<CompletionItemRanges>first.range).insert.startColumn, 5);
|
||||
|
||||
const completions = await asCompletionModel(model, new Position(1, 8), provider);
|
||||
assert.strictEqual(completions.items.length, 1);
|
||||
assert.strictEqual(completions.items[0].editStart.column, 5);
|
||||
});
|
||||
|
||||
test.skip('Autocomplete suggests based on the last letter of a word and it depends on the typing speed #191070', async function () {
|
||||
test('Autocomplete suggests based on the last letter of a word and it depends on the typing speed #191070', async function () {
|
||||
snippetService = new SimpleSnippetService([
|
||||
new Snippet(false, ['fooLang'], '/whiletrue', '/whiletrue', '', 'one', '', SnippetSource.User, generateUuid()),
|
||||
new Snippet(false, ['fooLang'], '/sc not expanding', '/sc not expanding', '', 'two', '', SnippetSource.User, generateUuid()),
|
||||
|
@ -836,8 +1042,8 @@ suite('SnippetsService', function () {
|
|||
new Position(1, 2),
|
||||
{ triggerKind: CompletionTriggerKind.Invoke }
|
||||
);
|
||||
assert.strictEqual(result1.suggestions.length, 1);
|
||||
assert.strictEqual(result1.suggestions[0].insertText, 'one');
|
||||
assert.strictEqual(result1.suggestions.length, 1);
|
||||
}
|
||||
|
||||
{ // PREFIX: where
|
||||
|
@ -847,8 +1053,8 @@ suite('SnippetsService', function () {
|
|||
new Position(1, 6),
|
||||
{ triggerKind: CompletionTriggerKind.Invoke }
|
||||
);
|
||||
assert.strictEqual(result2.suggestions[0].insertText, 'one'); // /whiletrue matches where (WHilEtRuE)
|
||||
assert.strictEqual(result2.suggestions.length, 1);
|
||||
assert.strictEqual(result2.suggestions[1].insertText, 'one'); // /whiletrue matches where (WHilEtRuE)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -278,7 +278,7 @@ function loadTests(opts) {
|
|||
teardown(() => {
|
||||
|
||||
// should not have unexpected output
|
||||
if (_testsWithUnexpectedOutput) {
|
||||
if (_testsWithUnexpectedOutput && !opts.dev) {
|
||||
assert.ok(false, 'Error: Unexpected console output in test run. Please ensure no console.[log|error|info|warn] usage in tests or runtime errors.');
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue