Skip to content

Commit

Permalink
fix #47988
Browse files Browse the repository at this point in the history
  • Loading branch information
jrieken committed Apr 30, 2018
1 parent 8188543 commit ab78dc7
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 68 deletions.
34 changes: 18 additions & 16 deletions src/vs/editor/contrib/suggest/completionModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
'use strict';

import { fuzzyScore, fuzzyScoreGracefulAggressive, skipScore } from 'vs/base/common/filters';
import { ISuggestSupport, ISuggestResult } from 'vs/editor/common/modes';
import { ISuggestionItem, SnippetConfig } from './suggest';
import { isDisposable } from 'vs/base/common/lifecycle';
import { ISuggestResult, ISuggestSupport } from 'vs/editor/common/modes';
import { ISuggestionItem, SnippetConfig } from './suggest';

export interface ICompletionItem extends ISuggestionItem {
matches?: number[];
Expand Down Expand Up @@ -53,7 +53,7 @@ export class CompletionModel {
private _lineContext: LineContext;
private _refilterKind: Refilter;
private _filteredItems: ICompletionItem[];
private _isIncomplete: boolean;
private _isIncomplete: Set<ISuggestSupport>;
private _stats: ICompletionStats;

constructor(items: ISuggestionItem[], column: number, lineContext: LineContext, snippetConfig?: SnippetConfig) {
Expand Down Expand Up @@ -99,24 +99,24 @@ export class CompletionModel {
return this._filteredItems;
}

get incomplete(): boolean {
get incomplete(): Set<ISuggestSupport> {
this._ensureCachedState();
return this._isIncomplete;
}

resolveIncompleteInfo(): { incomplete: ISuggestSupport[], complete: ISuggestionItem[] } {
const incomplete: ISuggestSupport[] = [];
const complete: ISuggestionItem[] = [];
adopt(except: Set<ISuggestSupport>): ISuggestionItem[] {
let res = new Array<ISuggestionItem>();
for (let i = 0; i < this._items.length; i++) {
if (!except.has(this._items[i].support)) {
res.push(this._items[i]);

for (const item of this._items) {
if (!item.container.incomplete) {
complete.push(item);
} else if (incomplete.indexOf(item.support) < 0) {
incomplete.push(item.support);
// unordered removed
this._items[i] = this._items[this._items.length - 1];
this._items.pop();
}
}

return { incomplete, complete };
this._refilterKind = Refilter.All;
return res;
}

get stats(): ICompletionStats {
Expand All @@ -132,7 +132,7 @@ export class CompletionModel {

private _createCachedState(): void {

this._isIncomplete = false;
this._isIncomplete = new Set();
this._stats = { suggestionCount: 0, snippetCount: 0, textCount: 0 };

const { leadingLineContent, characterCountDelta } = this._lineContext;
Expand All @@ -153,7 +153,9 @@ export class CompletionModel {

// collect those supports that signaled having
// an incomplete result
this._isIncomplete = this._isIncomplete || container.incomplete;
if (container.incomplete) {
this._isIncomplete.add(item.support);
}

// 'word' is that remainder of the current line that we
// filter and score against. In theory each suggestion uses a
Expand Down
44 changes: 19 additions & 25 deletions src/vs/editor/contrib/suggest/suggestModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@
*--------------------------------------------------------------------------------------------*/
'use strict';

import { onUnexpectedError } from 'vs/base/common/errors';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { TimeoutTimer } from 'vs/base/common/async';
import { Event, Emitter } from 'vs/base/common/event';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/map';
import { TPromise } from 'vs/base/common/winjs.base';
import { ITextModel, IWordAtPosition } from 'vs/editor/common/model';
import { ISuggestSupport, SuggestRegistry, StandardTokenType, SuggestTriggerKind, SuggestContext } from 'vs/editor/common/modes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest';
import { ITextModel, IWordAtPosition } from 'vs/editor/common/model';
import { ISuggestSupport, StandardTokenType, SuggestContext, SuggestRegistry, SuggestTriggerKind } from 'vs/editor/common/modes';
import { CompletionModel } from './completionModel';
import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ISuggestionItem, getSuggestionComparator, provideSuggestionItems } from './suggest';

export interface ICancelEvent {
readonly retrigger: boolean;
Expand Down Expand Up @@ -170,18 +171,17 @@ export class SuggestModel implements IDisposable {
return;
}

const supportsByTriggerCharacter: { [ch: string]: ISuggestSupport[] } = Object.create(null);
const supportsByTriggerCharacter: { [ch: string]: Set<ISuggestSupport> } = Object.create(null);
for (const support of SuggestRegistry.all(this._editor.getModel())) {
if (isFalsyOrEmpty(support.triggerCharacters)) {
continue;
}
for (const ch of support.triggerCharacters) {
const array = supportsByTriggerCharacter[ch];
if (!array) {
supportsByTriggerCharacter[ch] = [support];
} else {
array.push(support);
let set = supportsByTriggerCharacter[ch];
if (!set) {
set = supportsByTriggerCharacter[ch] = new Set();
}
set.add(support);
}
}

Expand All @@ -192,15 +192,8 @@ export class SuggestModel implements IDisposable {
if (supports) {
// keep existing items that where not computed by the
// supports/providers that want to trigger now
const items: ISuggestionItem[] = [];
if (this._completionModel) {
for (const item of this._completionModel.items) {
if (supports.indexOf(item.support) < 0) {
items.push(item);
}
}
}
this.trigger({ auto: true, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, items);
const items: ISuggestionItem[] = this._completionModel ? this._completionModel.adopt(supports) : undefined;
this.trigger({ auto: true, triggerCharacter: lastChar }, Boolean(this._completionModel), values(supports), items);
}
});
}
Expand Down Expand Up @@ -427,10 +420,11 @@ export class SuggestModel implements IDisposable {
return;
}

if (ctx.column > this._context.column && this._completionModel.incomplete && ctx.leadingWord.word.length !== 0) {
if (ctx.column > this._context.column && this._completionModel.incomplete.size > 0 && ctx.leadingWord.word.length !== 0) {
// typed -> moved cursor RIGHT & incomple model & still on a word -> retrigger
const { complete, incomplete } = this._completionModel.resolveIncompleteInfo();
this.trigger({ auto: this._state === State.Auto }, true, incomplete, complete);
const { incomplete } = this._completionModel;
const adopted = this._completionModel.adopt(incomplete);
this.trigger({ auto: this._state === State.Auto }, true, values(incomplete), adopted);

} else {
// typed -> moved cursor RIGHT -> update UI
Expand Down
21 changes: 11 additions & 10 deletions src/vs/editor/contrib/suggest/test/completionModel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
'use strict';

import * as assert from 'assert';
import { ISuggestion, ISuggestResult, ISuggestSupport, SuggestionType } from 'vs/editor/common/modes';
import { ISuggestionItem, getSuggestionComparator } from 'vs/editor/contrib/suggest/suggest';
import { CompletionModel } from 'vs/editor/contrib/suggest/completionModel';
import { IPosition } from 'vs/editor/common/core/position';
import { TPromise } from 'vs/base/common/winjs.base';
import { IPosition } from 'vs/editor/common/core/position';
import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestionType } from 'vs/editor/common/modes';
import { CompletionModel } from 'vs/editor/contrib/suggest/completionModel';
import { ISuggestionItem, getSuggestionComparator } from 'vs/editor/contrib/suggest/suggest';

export function createSuggestItem(label: string, overwriteBefore: number, type: SuggestionType = 'property', incomplete: boolean = false, position: IPosition = { lineNumber: 1, column: 1 }): ISuggestionItem {

Expand Down Expand Up @@ -77,7 +77,7 @@ suite('CompletionModel', function () {

test('complete/incomplete', function () {

assert.equal(model.incomplete, false);
assert.equal(model.incomplete.size, 0);

let incompleteModel = new CompletionModel([
createSuggestItem('foo', 3, undefined, true),
Expand All @@ -86,7 +86,7 @@ suite('CompletionModel', function () {
leadingLineContent: 'foo',
characterCountDelta: 0
});
assert.equal(incompleteModel.incomplete, true);
assert.equal(incompleteModel.incomplete.size, 1);
});

test('replaceIncomplete', function () {
Expand All @@ -95,13 +95,14 @@ suite('CompletionModel', function () {
const incompleteItem = createSuggestItem('foofoo', 1, undefined, true, { lineNumber: 1, column: 2 });

const model = new CompletionModel([completeItem, incompleteItem], 2, { leadingLineContent: 'f', characterCountDelta: 0 });
assert.equal(model.incomplete, true);
assert.equal(model.incomplete.size, 1);
assert.equal(model.items.length, 2);

const { complete, incomplete } = model.resolveIncompleteInfo();
const { incomplete } = model;
const complete = model.adopt(incomplete);

assert.equal(incomplete.length, 1);
assert.ok(incomplete[0] === incompleteItem.support);
assert.equal(incomplete.size, 1);
assert.ok(incomplete.has(incompleteItem.support));
assert.equal(complete.length, 1);
assert.ok(complete[0] === completeItem);
});
Expand Down
26 changes: 9 additions & 17 deletions src/vs/editor/contrib/suggest/test/suggestModel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { IState, ISuggestResult, ISuggestSupport, LanguageIdentifier, MetadataCo
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { NULL_STATE } from 'vs/editor/common/modes/nullMode';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { ICompletionItem } from 'vs/editor/contrib/suggest/completionModel';
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
import { LineContext, SuggestModel } from 'vs/editor/contrib/suggest/suggestModel';
import { ISelectedSuggestion } from 'vs/editor/contrib/suggest/suggestWidget';
Expand Down Expand Up @@ -464,7 +463,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
model.trigger({ auto: false });
}, event => {
assert.equal(event.auto, false);
assert.equal(event.completionModel.incomplete, true);
assert.equal(event.completionModel.incomplete.size, 1);
assert.equal(event.completionModel.items.length, 1);

return assertEvent(model.onDidCancel, () => {
Expand Down Expand Up @@ -501,7 +500,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
model.trigger({ auto: false });
}, event => {
assert.equal(event.auto, false);
assert.equal(event.completionModel.incomplete, true);
assert.equal(event.completionModel.incomplete.size, 1);
assert.equal(event.completionModel.items.length, 1);

return assertEvent(model.onDidSuggest, () => {
Expand All @@ -511,7 +510,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
editor.trigger('keyboard', Handler.Type, { text: ';' });
}, event => {
assert.equal(event.auto, false);
assert.equal(event.completionModel.incomplete, true);
assert.equal(event.completionModel.incomplete.size, 1);
assert.equal(event.completionModel.items.length, 1);

});
Expand Down Expand Up @@ -704,7 +703,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
provideCompletionItems(doc, pos) {
return {
incomplete: true,
suggestions: [{ type: 'folder', label: 'CompleteNot', insertText: 'Incomplete', sortText: 'a', overwriteBefore: 1 }],
suggestions: [{ type: 'folder', label: 'CompleteNot', insertText: 'Incomplete', sortText: 'a', overwriteBefore: pos.column - 1 }],
dispose() { disposeA += 1; }
};
}
Expand All @@ -713,7 +712,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
provideCompletionItems(doc, pos) {
return {
incomplete: false,
suggestions: [{ type: 'folder', label: 'Complete', insertText: 'Complete', sortText: 'z', overwriteBefore: 1 }],
suggestions: [{ type: 'folder', label: 'Complete', insertText: 'Complete', sortText: 'z', overwriteBefore: pos.column - 1 }],
dispose() { disposeB += 1; }
};
},
Expand All @@ -724,27 +723,20 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {

return withOracle(async (model, editor) => {

let itemA: ICompletionItem;
let itemB: ICompletionItem;

await assertEvent(model.onDidSuggest, () => {
model.trigger({ auto: true });
editor.setValue('');
editor.setSelection(new Selection(1, 1, 1, 1));
editor.trigger('keyboard', Handler.Type, { text: 'c' });

}, event => {
assert.equal(event.auto, true);
assert.equal(event.completionModel.items.length, 2);
assert.equal(disposeA, 0);
assert.equal(disposeB, 0);

itemA = event.completionModel.items[0];
itemB = event.completionModel.items[1];

assert.equal(itemA.suggestion.label, 'CompleteNot');
assert.equal(itemB.suggestion.label, 'Complete');
});

await assertEvent(model.onDidSuggest, () => {
model.trigger({ auto: true }, true, [itemA.support], [itemB]);
editor.trigger('keyboard', Handler.Type, { text: 'o' });
}, event => {
assert.equal(event.auto, true);
assert.equal(event.completionModel.items.length, 2);
Expand Down

0 comments on commit ab78dc7

Please sign in to comment.