mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Bring existing items by provider to the suggest-call, refactor trigger options into one argument-object, simpily completionModel
This commit is contained in:
parent
3b4ff94607
commit
d08acd8f1a
|
@ -46,7 +46,8 @@ export class CompletionModel {
|
|||
private _lineContext: LineContext;
|
||||
private _refilterKind: Refilter;
|
||||
private _filteredItems?: StrictCompletionItem[];
|
||||
private _providerInfo?: Map<CompletionItemProvider, boolean>;
|
||||
|
||||
private _itemsByProvider?: Map<CompletionItemProvider, CompletionItem[]>;
|
||||
private _stats?: ICompletionStats;
|
||||
|
||||
constructor(
|
||||
|
@ -92,40 +93,22 @@ export class CompletionModel {
|
|||
return this._filteredItems!;
|
||||
}
|
||||
|
||||
get allProvider(): IterableIterator<CompletionItemProvider> {
|
||||
getItemsByProvider(): ReadonlyMap<CompletionItemProvider, CompletionItem[]> {
|
||||
this._ensureCachedState();
|
||||
return this._providerInfo!.keys();
|
||||
return this._itemsByProvider!;
|
||||
}
|
||||
|
||||
get incomplete(): Set<CompletionItemProvider> {
|
||||
getIncompleteProvider(): Set<CompletionItemProvider> {
|
||||
this._ensureCachedState();
|
||||
const result = new Set<CompletionItemProvider>();
|
||||
for (const [provider, incomplete] of this._providerInfo!) {
|
||||
if (incomplete) {
|
||||
for (const [provider, items] of this.getItemsByProvider()) {
|
||||
if (items.length > 0 && items[0].container.incomplete) {
|
||||
result.add(provider);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
adopt(except: Set<CompletionItemProvider>): CompletionItem[] {
|
||||
const res: CompletionItem[] = [];
|
||||
for (let i = 0; i < this._items.length;) {
|
||||
if (!except.has(this._items[i].provider)) {
|
||||
res.push(this._items[i]);
|
||||
|
||||
// unordered removed
|
||||
this._items[i] = this._items[this._items.length - 1];
|
||||
this._items.pop();
|
||||
} else {
|
||||
// continue with next item
|
||||
i++;
|
||||
}
|
||||
}
|
||||
this._refilterKind = Refilter.All;
|
||||
return res;
|
||||
}
|
||||
|
||||
get stats(): ICompletionStats {
|
||||
this._ensureCachedState();
|
||||
return this._stats!;
|
||||
|
@ -139,7 +122,7 @@ export class CompletionModel {
|
|||
|
||||
private _createCachedState(): void {
|
||||
|
||||
this._providerInfo = new Map();
|
||||
this._itemsByProvider = new Map();
|
||||
|
||||
const labelLengths: number[] = [];
|
||||
|
||||
|
@ -164,8 +147,13 @@ export class CompletionModel {
|
|||
continue; // SKIP invalid items
|
||||
}
|
||||
|
||||
// collect all support, know if their result is incomplete
|
||||
this._providerInfo.set(item.provider, Boolean(item.container.incomplete));
|
||||
// keep all items by their provider
|
||||
const arr = this._itemsByProvider.get(item.provider);
|
||||
if (arr) {
|
||||
arr.push(item);
|
||||
} else {
|
||||
this._itemsByProvider.set(item.provider, [item]);
|
||||
}
|
||||
|
||||
// 'word' is that remainder of the current line that we
|
||||
// filter and score against. In theory each suggestion uses a
|
||||
|
|
|
@ -167,6 +167,7 @@ export class CompletionOptions {
|
|||
readonly snippetSortOrder = SnippetSortOrder.Bottom,
|
||||
readonly kindFilter = new Set<languages.CompletionItemKind>(),
|
||||
readonly providerFilter = new Set<languages.CompletionItemProvider>(),
|
||||
readonly providerItemsToReuse: ReadonlyMap<languages.CompletionItemProvider, CompletionItem[]> = new Map<languages.CompletionItemProvider, CompletionItem[]>(),
|
||||
readonly showDeprecated = true
|
||||
) { }
|
||||
}
|
||||
|
@ -281,6 +282,14 @@ export async function provideSuggestionItems(
|
|||
// for each support in the group ask for suggestions
|
||||
let didAddResult = false;
|
||||
await Promise.all(providerGroup.map(async provider => {
|
||||
// we have items from a previous session that we can reuse
|
||||
if (options.providerItemsToReuse.has(provider)) {
|
||||
const items = options.providerItemsToReuse.get(provider)!;
|
||||
items.forEach(item => result.push(item));
|
||||
didAddResult = didAddResult || items.length > 0;
|
||||
return;
|
||||
}
|
||||
// check if this provider is filtered out
|
||||
if (options.providerFilter.size > 0 && !options.providerFilter.has(provider)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -405,7 +405,7 @@ export class SuggestController implements IEditorContribution {
|
|||
if (item.completion.command) {
|
||||
if (item.completion.command.id === TriggerSuggestAction.id) {
|
||||
// retigger
|
||||
this.model.trigger({ auto: true, shy: false }, true);
|
||||
this.model.trigger({ auto: true, retrigger: true });
|
||||
} else {
|
||||
// exec command, done
|
||||
tasks.push(this._commandService.executeCommand(item.completion.command.id, ...(item.completion.command.arguments ? [...item.completion.command.arguments] : [])).catch(onUnexpectedError));
|
||||
|
@ -500,7 +500,10 @@ export class SuggestController implements IEditorContribution {
|
|||
|
||||
triggerSuggest(onlyFrom?: Set<CompletionItemProvider>, auto?: boolean, noFilter?: boolean): void {
|
||||
if (this.editor.hasModel()) {
|
||||
this.model.trigger({ auto: auto ?? false, shy: false }, false, onlyFrom, undefined, noFilter);
|
||||
this.model.trigger({
|
||||
auto: auto ?? false,
|
||||
completionOptions: { providerFilter: onlyFrom, kindFilter: noFilter ? new Set() : undefined }
|
||||
});
|
||||
this.editor.revealPosition(this.editor.getPosition(), ScrollType.Smooth);
|
||||
this.editor.focus();
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ class InlineCompletionResults extends RefCountedDisposable implements InlineComp
|
|||
&& this.line === line
|
||||
&& this.word.word.length > 0
|
||||
&& this.word.startColumn === word.startColumn && this.word.endColumn < word.endColumn // same word
|
||||
&& this.completionModel.incomplete.size === 0; // no incomplete results
|
||||
&& this.completionModel.getIncompleteProvider().size === 0; // no incomplete results
|
||||
}
|
||||
|
||||
get items(): SuggestInlineCompletion[] {
|
||||
|
|
|
@ -25,7 +25,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { CompletionModel } from './completionModel';
|
||||
import { CompletionDurations, CompletionItem, CompletionOptions, getSnippetSuggestSupport, getSuggestionComparator, provideSuggestionItems, QuickSuggestionsOptions, SnippetSortOrder } from './suggest';
|
||||
import { CompletionDurations, CompletionItem, CompletionOptions, getSnippetSuggestSupport, provideSuggestionItems, QuickSuggestionsOptions, SnippetSortOrder } from './suggest';
|
||||
import { IWordAtPosition } from 'vs/editor/common/core/wordHelper';
|
||||
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures';
|
||||
import { FuzzyScoreOptions } from 'vs/base/common/filters';
|
||||
|
@ -48,11 +48,14 @@ export interface ISuggestEvent {
|
|||
readonly shy: boolean;
|
||||
}
|
||||
|
||||
export interface SuggestTriggerContext {
|
||||
export interface SuggestTriggerOptions {
|
||||
readonly auto: boolean;
|
||||
readonly shy: boolean;
|
||||
readonly shy?: boolean;
|
||||
readonly retrigger?: boolean;
|
||||
readonly triggerKind?: CompletionTriggerKind;
|
||||
readonly triggerCharacter?: string;
|
||||
readonly clipboardText?: string;
|
||||
completionOptions?: Partial<CompletionOptions>;
|
||||
}
|
||||
|
||||
export class LineContext {
|
||||
|
@ -85,13 +88,13 @@ export class LineContext {
|
|||
readonly auto: boolean;
|
||||
readonly shy: boolean;
|
||||
|
||||
constructor(model: ITextModel, position: Position, auto: boolean, shy: boolean) {
|
||||
constructor(model: ITextModel, position: Position, auto: boolean, shy: boolean | undefined) {
|
||||
this.leadingLineContent = model.getLineContent(position.lineNumber).substr(0, position.column - 1);
|
||||
this.leadingWord = model.getWordUntilPosition(position);
|
||||
this.lineNumber = position.lineNumber;
|
||||
this.column = position.column;
|
||||
this.auto = auto;
|
||||
this.shy = shy;
|
||||
this.shy = shy ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,12 +279,25 @@ export class SuggestModel implements IDisposable {
|
|||
|
||||
const supports = supportsByTriggerCharacter.get(lastChar);
|
||||
if (supports) {
|
||||
|
||||
// keep existing items that where not computed by the
|
||||
// supports/providers that want to trigger now
|
||||
const existing = this._completionModel
|
||||
? { items: this._completionModel.adopt(supports), clipboardText: this._completionModel.clipboardText }
|
||||
: undefined;
|
||||
this.trigger({ auto: true, shy: false, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, existing);
|
||||
const providerItemsToReuse = new Map<CompletionItemProvider, CompletionItem[]>();
|
||||
if (this._completionModel) {
|
||||
for (const [provider, items] of this._completionModel.getItemsByProvider()) {
|
||||
if (!supports.has(provider)) {
|
||||
providerItemsToReuse.set(provider, items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.trigger({
|
||||
auto: true,
|
||||
triggerCharacter: lastChar,
|
||||
retrigger: Boolean(this._completionModel),
|
||||
clipboardText: this._completionModel?.clipboardText,
|
||||
completionOptions: { providerFilter: supports, providerItemsToReuse }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -316,7 +332,7 @@ export class SuggestModel implements IDisposable {
|
|||
if (!this._editor.hasModel() || !this._languageFeaturesService.completionProvider.has(this._editor.getModel())) {
|
||||
this.cancel();
|
||||
} else {
|
||||
this.trigger({ auto: this._state === State.Auto, shy: false }, true);
|
||||
this.trigger({ auto: this._state === State.Auto, retrigger: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -415,7 +431,7 @@ export class SuggestModel implements IDisposable {
|
|||
}
|
||||
|
||||
// we made it till here -> trigger now
|
||||
this.trigger({ auto: true, shy: false });
|
||||
this.trigger({ auto: true });
|
||||
|
||||
}, this._editor.getOption(EditorOption.quickSuggestionsDelay));
|
||||
}
|
||||
|
@ -429,29 +445,29 @@ export class SuggestModel implements IDisposable {
|
|||
this._onNewContext(ctx);
|
||||
}
|
||||
|
||||
trigger(context: SuggestTriggerContext, retrigger: boolean = false, onlyFrom?: Set<CompletionItemProvider>, existing?: { items: CompletionItem[]; clipboardText: string | undefined }, noFilter?: boolean): void {
|
||||
trigger(options: SuggestTriggerOptions): void {
|
||||
if (!this._editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
const auto = context.auto;
|
||||
const ctx = new LineContext(model, this._editor.getPosition(), auto, context.shy);
|
||||
const auto = options.auto;
|
||||
const ctx = new LineContext(model, this._editor.getPosition(), auto, options.shy);
|
||||
|
||||
// Cancel previous requests, change state & update UI
|
||||
this.cancel(retrigger);
|
||||
this.cancel(options.retrigger);
|
||||
this._state = auto ? State.Auto : State.Manual;
|
||||
this._onDidTrigger.fire({ auto, shy: context.shy, position: this._editor.getPosition() });
|
||||
this._onDidTrigger.fire({ auto, shy: options.shy ?? false, position: this._editor.getPosition() });
|
||||
|
||||
// Capture context when request was sent
|
||||
this._context = ctx;
|
||||
|
||||
// Build context for request
|
||||
let suggestCtx: CompletionContext = { triggerKind: context.triggerKind ?? CompletionTriggerKind.Invoke };
|
||||
if (context.triggerCharacter) {
|
||||
let suggestCtx: CompletionContext = { triggerKind: options.triggerKind ?? CompletionTriggerKind.Invoke };
|
||||
if (options.triggerCharacter) {
|
||||
suggestCtx = {
|
||||
triggerKind: CompletionTriggerKind.TriggerCharacter,
|
||||
triggerCharacter: context.triggerCharacter
|
||||
triggerCharacter: options.triggerCharacter
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -474,7 +490,7 @@ export class SuggestModel implements IDisposable {
|
|||
}
|
||||
|
||||
const { itemKind: itemKindFilter, showDeprecated } = SuggestModel._createSuggestFilter(this._editor);
|
||||
const completionOptions = new CompletionOptions(snippetSortOrder, !noFilter ? itemKindFilter : new Set(), onlyFrom, showDeprecated);
|
||||
const completionOptions = new CompletionOptions(snippetSortOrder, options.completionOptions?.kindFilter ?? itemKindFilter, options.completionOptions?.providerFilter, options.completionOptions?.providerItemsToReuse, showDeprecated);
|
||||
const wordDistance = WordDistance.create(this._editorWorkerService, this._editor);
|
||||
|
||||
const completions = provideSuggestionItems(
|
||||
|
@ -494,7 +510,7 @@ export class SuggestModel implements IDisposable {
|
|||
return;
|
||||
}
|
||||
|
||||
let clipboardText = existing?.clipboardText;
|
||||
let clipboardText = options?.clipboardText;
|
||||
if (!clipboardText && completions.needsClipboard) {
|
||||
clipboardText = await this._clipboardService.readText();
|
||||
}
|
||||
|
@ -504,19 +520,19 @@ export class SuggestModel implements IDisposable {
|
|||
}
|
||||
|
||||
const model = this._editor.getModel();
|
||||
let items = completions.items;
|
||||
// const items = completions.items;
|
||||
|
||||
if (existing) {
|
||||
const cmpFn = getSuggestionComparator(snippetSortOrder);
|
||||
items = items.concat(existing.items).sort(cmpFn);
|
||||
}
|
||||
// if (existing) {
|
||||
// const cmpFn = getSuggestionComparator(snippetSortOrder);
|
||||
// items = items.concat(existing.items).sort(cmpFn);
|
||||
// }
|
||||
|
||||
const ctx = new LineContext(model, this._editor.getPosition(), auto, context.shy);
|
||||
const ctx = new LineContext(model, this._editor.getPosition(), auto, options.shy);
|
||||
const fuzzySearchOptions = {
|
||||
...FuzzyScoreOptions.default,
|
||||
firstMatchCanBeWeak: !this._editor.getOption(EditorOption.suggest).matchOnWordStartOnly
|
||||
};
|
||||
this._completionModel = new CompletionModel(items, this._context!.column, {
|
||||
this._completionModel = new CompletionModel(completions.items, this._context!.column, {
|
||||
leadingLineContent: ctx.leadingLineContent,
|
||||
characterCountDelta: ctx.column - this._context!.column
|
||||
},
|
||||
|
@ -625,7 +641,7 @@ export class SuggestModel implements IDisposable {
|
|||
if (ctx.column < this._context.column) {
|
||||
// typed -> moved cursor LEFT -> retrigger if still on a word
|
||||
if (ctx.leadingWord.word) {
|
||||
this.trigger({ auto: this._context.auto, shy: false }, true);
|
||||
this.trigger({ auto: this._context.auto, retrigger: true });
|
||||
} else {
|
||||
this.cancel();
|
||||
}
|
||||
|
@ -638,24 +654,37 @@ export class SuggestModel implements IDisposable {
|
|||
}
|
||||
|
||||
if (ctx.leadingWord.word.length !== 0 && ctx.leadingWord.startColumn > this._context.leadingWord.startColumn) {
|
||||
// started a new word while IntelliSense shows -> retrigger
|
||||
|
||||
// Select those providers have not contributed to this completion model and re-trigger completions for
|
||||
// them. Also adopt the existing items and merge them into the new completion model
|
||||
const inactiveProvider = new Set(this._languageFeaturesService.completionProvider.all(this._editor.getModel()!));
|
||||
for (const provider of this._completionModel.allProvider) {
|
||||
inactiveProvider.delete(provider);
|
||||
}
|
||||
const items = this._completionModel.adopt(new Set());
|
||||
this.trigger({ auto: this._context.auto, shy: false }, true, inactiveProvider, { items, clipboardText: this._completionModel.clipboardText });
|
||||
// started a new word while IntelliSense shows -> retrigger but reuse all items that we currently have
|
||||
const map = this._completionModel.getItemsByProvider();
|
||||
this.trigger({
|
||||
auto: this._context.auto,
|
||||
retrigger: true,
|
||||
clipboardText: this._completionModel.clipboardText,
|
||||
completionOptions: { providerItemsToReuse: map }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.column > this._context.column && this._completionModel.incomplete.size > 0 && ctx.leadingWord.word.length !== 0) {
|
||||
if (ctx.column > this._context.column && this._completionModel.getIncompleteProvider().size > 0 && ctx.leadingWord.word.length !== 0) {
|
||||
// typed -> moved cursor RIGHT & incomple model & still on a word -> retrigger
|
||||
const { incomplete } = this._completionModel;
|
||||
const items = this._completionModel.adopt(incomplete);
|
||||
this.trigger({ auto: this._state === State.Auto, shy: false, triggerKind: CompletionTriggerKind.TriggerForIncompleteCompletions }, true, incomplete, { items, clipboardText: this._completionModel.clipboardText });
|
||||
|
||||
const providerItemsToReuse = new Map<CompletionItemProvider, CompletionItem[]>();
|
||||
const providerFilter = new Set<CompletionItemProvider>();
|
||||
for (const [provider, items] of this._completionModel.getItemsByProvider()) {
|
||||
if (items.length > 0 && items[0].container.incomplete) {
|
||||
providerFilter.add(provider);
|
||||
} else {
|
||||
providerItemsToReuse.set(provider, items);
|
||||
}
|
||||
}
|
||||
|
||||
this.trigger({
|
||||
auto: this._state === State.Auto,
|
||||
triggerKind: CompletionTriggerKind.TriggerForIncompleteCompletions,
|
||||
retrigger: true,
|
||||
clipboardText: this._completionModel.clipboardText,
|
||||
completionOptions: { providerFilter, providerItemsToReuse }
|
||||
});
|
||||
|
||||
} else {
|
||||
// typed -> moved cursor RIGHT -> update UI
|
||||
|
@ -678,7 +707,7 @@ export class SuggestModel implements IDisposable {
|
|||
|
||||
if (shouldAutoTrigger && this._context.leadingWord.endColumn < ctx.leadingWord.startColumn) {
|
||||
// retrigger when heading into a new word
|
||||
this.trigger({ auto: this._context.auto, shy: false }, true);
|
||||
this.trigger({ auto: this._context.auto, retrigger: true });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ suite('CompletionModel', function () {
|
|||
|
||||
test('complete/incomplete', () => {
|
||||
|
||||
assert.strictEqual(model.incomplete.size, 0);
|
||||
assert.strictEqual(model.getIncompleteProvider().size, 0);
|
||||
|
||||
const incompleteModel = new CompletionModel([
|
||||
createSuggestItem('foo', 3, undefined, true),
|
||||
|
@ -111,25 +111,7 @@ suite('CompletionModel', function () {
|
|||
leadingLineContent: 'foo',
|
||||
characterCountDelta: 0
|
||||
}, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
|
||||
assert.strictEqual(incompleteModel.incomplete.size, 1);
|
||||
});
|
||||
|
||||
test('replaceIncomplete', () => {
|
||||
|
||||
const completeItem = createSuggestItem('foobar', 1, undefined, false, { lineNumber: 1, column: 2 });
|
||||
const incompleteItem = createSuggestItem('foofoo', 1, undefined, true, { lineNumber: 1, column: 2 });
|
||||
|
||||
const model = new CompletionModel([completeItem, incompleteItem], 2, { leadingLineContent: 'f', characterCountDelta: 0 }, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined);
|
||||
assert.strictEqual(model.incomplete.size, 1);
|
||||
assert.strictEqual(model.items.length, 2);
|
||||
|
||||
const { incomplete } = model;
|
||||
const complete = model.adopt(incomplete);
|
||||
|
||||
assert.strictEqual(incomplete.size, 1);
|
||||
assert.ok(incomplete.has(incompleteItem.provider));
|
||||
assert.strictEqual(complete.length, 1);
|
||||
assert.ok(complete[0] === completeItem);
|
||||
assert.strictEqual(incompleteModel.getIncompleteProvider().size, 1);
|
||||
});
|
||||
|
||||
test('Fuzzy matching of snippets stopped working with inline snippet suggestions #49895', function () {
|
||||
|
@ -150,15 +132,8 @@ suite('CompletionModel', function () {
|
|||
incompleteItem1,
|
||||
], 2, { leadingLineContent: 'f', characterCountDelta: 0 }, WordDistance.None, EditorOptions.suggest.defaultValue, EditorOptions.snippetSuggestions.defaultValue, undefined
|
||||
);
|
||||
assert.strictEqual(model.incomplete.size, 1);
|
||||
assert.strictEqual(model.getIncompleteProvider().size, 1);
|
||||
assert.strictEqual(model.items.length, 6);
|
||||
|
||||
const { incomplete } = model;
|
||||
const complete = model.adopt(incomplete);
|
||||
|
||||
assert.strictEqual(incomplete.size, 1);
|
||||
assert.ok(incomplete.has(incompleteItem1.provider));
|
||||
assert.strictEqual(complete.length, 5);
|
||||
});
|
||||
|
||||
test('proper current word when length=0, #16380', function () {
|
||||
|
|
|
@ -253,7 +253,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
return Promise.all([
|
||||
|
||||
assertEvent(model.onDidTrigger, function () {
|
||||
model.trigger({ auto: true, shy: false });
|
||||
model.trigger({ auto: true });
|
||||
}, function (event) {
|
||||
assert.strictEqual(event.auto, true);
|
||||
|
||||
|
@ -265,13 +265,13 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
}),
|
||||
|
||||
assertEvent(model.onDidTrigger, function () {
|
||||
model.trigger({ auto: true, shy: false });
|
||||
model.trigger({ auto: true });
|
||||
}, function (event) {
|
||||
assert.strictEqual(event.auto, true);
|
||||
}),
|
||||
|
||||
assertEvent(model.onDidTrigger, function () {
|
||||
model.trigger({ auto: false, shy: false });
|
||||
model.trigger({ auto: false });
|
||||
}, function (event) {
|
||||
assert.strictEqual(event.auto, false);
|
||||
})
|
||||
|
@ -287,12 +287,12 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
return withOracle(model => {
|
||||
return Promise.all([
|
||||
assertEvent(model.onDidCancel, function () {
|
||||
model.trigger({ auto: true, shy: false });
|
||||
model.trigger({ auto: true });
|
||||
}, function (event) {
|
||||
assert.strictEqual(event.retrigger, false);
|
||||
}),
|
||||
assertEvent(model.onDidSuggest, function () {
|
||||
model.trigger({ auto: false, shy: false });
|
||||
model.trigger({ auto: false });
|
||||
}, function (event) {
|
||||
assert.strictEqual(event.auto, false);
|
||||
assert.strictEqual(event.isFrozen, false);
|
||||
|
@ -343,7 +343,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
// make sure completionModel starts here!
|
||||
model.trigger({ auto: true, shy: false });
|
||||
model.trigger({ auto: true });
|
||||
}, event => {
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
|
@ -456,7 +456,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
editor.setPosition({ lineNumber: 1, column: 3 });
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
model.trigger({ auto: false, shy: false });
|
||||
model.trigger({ auto: false });
|
||||
}, event => {
|
||||
assert.strictEqual(event.auto, false);
|
||||
assert.strictEqual(event.isFrozen, false);
|
||||
|
@ -481,7 +481,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
editor.setPosition({ lineNumber: 1, column: 3 });
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
model.trigger({ auto: false, shy: false });
|
||||
model.trigger({ auto: false });
|
||||
}, event => {
|
||||
assert.strictEqual(event.auto, false);
|
||||
assert.strictEqual(event.isFrozen, false);
|
||||
|
@ -518,10 +518,10 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
editor.setPosition({ lineNumber: 1, column: 4 });
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
model.trigger({ auto: false, shy: false });
|
||||
model.trigger({ auto: false });
|
||||
}, event => {
|
||||
assert.strictEqual(event.auto, false);
|
||||
assert.strictEqual(event.completionModel.incomplete.size, 1);
|
||||
assert.strictEqual(event.completionModel.getIncompleteProvider().size, 1);
|
||||
assert.strictEqual(event.completionModel.items.length, 1);
|
||||
|
||||
return assertEvent(model.onDidCancel, () => {
|
||||
|
@ -555,10 +555,10 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
editor.setPosition({ lineNumber: 1, column: 4 });
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
model.trigger({ auto: false, shy: false });
|
||||
model.trigger({ auto: false });
|
||||
}, event => {
|
||||
assert.strictEqual(event.auto, false);
|
||||
assert.strictEqual(event.completionModel.incomplete.size, 1);
|
||||
assert.strictEqual(event.completionModel.getIncompleteProvider().size, 1);
|
||||
assert.strictEqual(event.completionModel.items.length, 1);
|
||||
|
||||
return assertEvent(model.onDidSuggest, () => {
|
||||
|
@ -568,7 +568,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
editor.trigger('keyboard', Handler.Type, { text: ';' });
|
||||
}, event => {
|
||||
assert.strictEqual(event.auto, false);
|
||||
assert.strictEqual(event.completionModel.incomplete.size, 1);
|
||||
assert.strictEqual(event.completionModel.getIncompleteProvider().size, 1);
|
||||
assert.strictEqual(event.completionModel.items.length, 1);
|
||||
|
||||
});
|
||||
|
@ -714,7 +714,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
|
||||
await assertEvent(sugget.onDidSuggest, () => {
|
||||
editor.setPosition({ lineNumber: 1, column: 3 });
|
||||
sugget.trigger({ auto: false, shy: false });
|
||||
sugget.trigger({ auto: false });
|
||||
}, event => {
|
||||
|
||||
assert.strictEqual(event.completionModel.items.length, 1);
|
||||
|
@ -941,4 +941,59 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Unexpected suggest scoring #167242', async function () {
|
||||
disposables.add(registry.register('*', {
|
||||
// word-based
|
||||
provideCompletionItems(doc, pos) {
|
||||
const word = doc.getWordUntilPosition(pos);
|
||||
return {
|
||||
suggestions: [{
|
||||
kind: CompletionItemKind.Text,
|
||||
label: 'pull',
|
||||
insertText: 'pull',
|
||||
range: new Range(pos.lineNumber, word.startColumn, pos.lineNumber, word.endColumn)
|
||||
}],
|
||||
};
|
||||
}
|
||||
}));
|
||||
disposables.add(registry.register({ scheme: 'test' }, {
|
||||
// JSON-based
|
||||
provideCompletionItems(doc, pos) {
|
||||
return {
|
||||
suggestions: [{
|
||||
kind: CompletionItemKind.Class,
|
||||
label: 'git.pull',
|
||||
insertText: 'git.pull',
|
||||
range: new Range(pos.lineNumber, 1, pos.lineNumber, pos.column)
|
||||
}],
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
return withOracle(async function (model, editor) {
|
||||
|
||||
await assertEvent(model.onDidSuggest, () => {
|
||||
editor.setValue('gi');
|
||||
editor.setSelection(new Selection(1, 3, 1, 3));
|
||||
editor.trigger('keyboard', Handler.Type, { text: 't' });
|
||||
|
||||
}, event => {
|
||||
assert.strictEqual(event.auto, true);
|
||||
assert.strictEqual(event.completionModel.items.length, 1);
|
||||
assert.strictEqual(event.completionModel.items[0].textLabel, 'git.pull');
|
||||
});
|
||||
|
||||
editor.trigger('keyboard', Handler.Type, { text: '.' });
|
||||
|
||||
await assertEvent(model.onDidSuggest, () => {
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'p' });
|
||||
|
||||
}, event => {
|
||||
assert.strictEqual(event.auto, true);
|
||||
assert.strictEqual(event.completionModel.items.length, 1);
|
||||
assert.strictEqual(event.completionModel.items[0].textLabel, 'git.pull');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue