Initial VS Code 1.19 source merge (#571)
* Initial 1.19 xcopy * Fix yarn build * Fix numerous build breaks * Next batch of build break fixes * More build break fixes * Runtime breaks * Additional post merge fixes * Fix windows setup file * Fix test failures. * Update license header blocks to refer to source eula
@@ -5,7 +5,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { fuzzyScore } from 'vs/base/common/filters';
|
||||
import { fuzzyScore, fuzzyScoreGracefulAggressive } 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';
|
||||
@@ -14,6 +14,7 @@ export interface ICompletionItem extends ISuggestionItem {
|
||||
matches?: number[];
|
||||
score?: number;
|
||||
idx?: number;
|
||||
word?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,13 +38,20 @@ export class LineContext {
|
||||
characterCountDelta: number;
|
||||
}
|
||||
|
||||
const enum Refilter {
|
||||
Nothing = 0,
|
||||
All = 1,
|
||||
Incr = 2
|
||||
}
|
||||
|
||||
export class CompletionModel {
|
||||
|
||||
private readonly _column: number;
|
||||
private readonly _items: ISuggestionItem[];
|
||||
private readonly _items: ICompletionItem[];
|
||||
private readonly _snippetCompareFn = CompletionModel._compareCompletionItems;
|
||||
|
||||
private _lineContext: LineContext;
|
||||
private _refilterKind: Refilter;
|
||||
private _filteredItems: ICompletionItem[];
|
||||
private _isIncomplete: boolean;
|
||||
private _stats: ICompletionStats;
|
||||
@@ -51,6 +59,7 @@ export class CompletionModel {
|
||||
constructor(items: ISuggestionItem[], column: number, lineContext: LineContext, snippetConfig?: SnippetConfig) {
|
||||
this._items = items;
|
||||
this._column = column;
|
||||
this._refilterKind = Refilter.All;
|
||||
this._lineContext = lineContext;
|
||||
|
||||
if (snippetConfig === 'top') {
|
||||
@@ -78,10 +87,10 @@ export class CompletionModel {
|
||||
|
||||
set lineContext(value: LineContext) {
|
||||
if (this._lineContext.leadingLineContent !== value.leadingLineContent
|
||||
|| this._lineContext.characterCountDelta !== value.characterCountDelta) {
|
||||
|
||||
|| this._lineContext.characterCountDelta !== value.characterCountDelta
|
||||
) {
|
||||
this._refilterKind = this._lineContext.characterCountDelta < value.characterCountDelta && this._filteredItems ? Refilter.Incr : Refilter.All;
|
||||
this._lineContext = value;
|
||||
this._filteredItems = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,22 +125,30 @@ export class CompletionModel {
|
||||
}
|
||||
|
||||
private _ensureCachedState(): void {
|
||||
if (!this._filteredItems) {
|
||||
if (this._refilterKind !== Refilter.Nothing) {
|
||||
this._createCachedState();
|
||||
}
|
||||
}
|
||||
|
||||
private _createCachedState(): void {
|
||||
this._filteredItems = [];
|
||||
|
||||
this._isIncomplete = false;
|
||||
this._stats = { suggestionCount: 0, snippetCount: 0, textCount: 0 };
|
||||
|
||||
const { leadingLineContent, characterCountDelta } = this._lineContext;
|
||||
let word = '';
|
||||
|
||||
for (let i = 0; i < this._items.length; i++) {
|
||||
// incrementally filter less
|
||||
const source = this._refilterKind === Refilter.All ? this._items : this._filteredItems;
|
||||
const target: typeof source = [];
|
||||
|
||||
const item = <ICompletionItem>this._items[i];
|
||||
// picks a score function based on the number of
|
||||
// items that we have to score/filter
|
||||
const scoreFn = source.length > 2000 ? fuzzyScore : fuzzyScoreGracefulAggressive;
|
||||
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
|
||||
const item = source[i];
|
||||
const { suggestion, container } = item;
|
||||
|
||||
// collect those supports that signaled having
|
||||
@@ -140,12 +157,16 @@ export class CompletionModel {
|
||||
|
||||
// 'word' is that remainder of the current line that we
|
||||
// filter and score against. In theory each suggestion uses a
|
||||
// differnet word, but in practice not - that's why we cache
|
||||
// different word, but in practice not - that's why we cache
|
||||
const wordLen = suggestion.overwriteBefore + characterCountDelta - (item.position.column - this._column);
|
||||
if (word.length !== wordLen) {
|
||||
word = wordLen === 0 ? '' : leadingLineContent.slice(-wordLen);
|
||||
}
|
||||
|
||||
// remember the word against which this item was
|
||||
// scored
|
||||
item.word = word;
|
||||
|
||||
if (wordLen === 0) {
|
||||
// when there is nothing to score against, don't
|
||||
// event try to do. Use a const rank and rely on
|
||||
@@ -159,19 +180,19 @@ export class CompletionModel {
|
||||
// if it matches we check with the label to compute highlights
|
||||
// and if that doesn't yield a result we have no highlights,
|
||||
// despite having the match
|
||||
let match = fuzzyScore(word, suggestion.filterText, suggestion.overwriteBefore);
|
||||
let match = scoreFn(word, suggestion.filterText, suggestion.overwriteBefore);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
item.score = match[0];
|
||||
item.matches = [];
|
||||
match = fuzzyScore(word, suggestion.label, suggestion.overwriteBefore);
|
||||
match = scoreFn(word, suggestion.label, suggestion.overwriteBefore);
|
||||
if (match) {
|
||||
item.matches = match[1];
|
||||
}
|
||||
} else {
|
||||
// by default match `word` against the `label`
|
||||
let match = fuzzyScore(word, suggestion.label, suggestion.overwriteBefore);
|
||||
let match = scoreFn(word, suggestion.label, suggestion.overwriteBefore);
|
||||
if (match) {
|
||||
item.score = match[0];
|
||||
item.matches = match[1];
|
||||
@@ -182,7 +203,7 @@ export class CompletionModel {
|
||||
|
||||
item.idx = i;
|
||||
|
||||
this._filteredItems.push(item);
|
||||
target.push(item);
|
||||
|
||||
// update stats
|
||||
this._stats.suggestionCount++;
|
||||
@@ -192,7 +213,8 @@ export class CompletionModel {
|
||||
}
|
||||
}
|
||||
|
||||
this._filteredItems.sort(this._snippetCompareFn);
|
||||
this._filteredItems = target.sort(this._snippetCompareFn);
|
||||
this._refilterKind = Refilter.Nothing;
|
||||
}
|
||||
|
||||
private static _compareCompletionItems(a: ICompletionItem, b: ICompletionItem): number {
|
||||
|
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 584 B |
|
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 584 B |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 823 B After Width: | Height: | Size: 823 B |
|
Before Width: | Height: | Size: 823 B After Width: | Height: | Size: 823 B |
|
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
|
Before Width: | Height: | Size: 552 B After Width: | Height: | Size: 552 B |
|
Before Width: | Height: | Size: 664 B After Width: | Height: | Size: 664 B |
|
Before Width: | Height: | Size: 664 B After Width: | Height: | Size: 664 B |
|
Before Width: | Height: | Size: 789 B After Width: | Height: | Size: 789 B |
|
Before Width: | Height: | Size: 789 B After Width: | Height: | Size: 789 B |
|
Before Width: | Height: | Size: 499 B After Width: | Height: | Size: 499 B |
|
Before Width: | Height: | Size: 499 B After Width: | Height: | Size: 499 B |
|
Before Width: | Height: | Size: 579 B After Width: | Height: | Size: 579 B |
|
Before Width: | Height: | Size: 579 B After Width: | Height: | Size: 579 B |
|
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 750 B |
|
Before Width: | Height: | Size: 760 B After Width: | Height: | Size: 760 B |
|
Before Width: | Height: | Size: 929 B After Width: | Height: | Size: 929 B |
|
Before Width: | Height: | Size: 929 B After Width: | Height: | Size: 929 B |
|
Before Width: | Height: | Size: 589 B After Width: | Height: | Size: 589 B |
|
Before Width: | Height: | Size: 589 B After Width: | Height: | Size: 589 B |
|
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 923 B |
|
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 923 B |
|
Before Width: | Height: | Size: 853 B After Width: | Height: | Size: 853 B |
|
Before Width: | Height: | Size: 853 B After Width: | Height: | Size: 853 B |
|
Before Width: | Height: | Size: 831 B After Width: | Height: | Size: 831 B |
|
Before Width: | Height: | Size: 831 B After Width: | Height: | Size: 831 B |
|
Before Width: | Height: | Size: 594 B After Width: | Height: | Size: 594 B |
|
Before Width: | Height: | Size: 594 B After Width: | Height: | Size: 594 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 710 B After Width: | Height: | Size: 710 B |
|
Before Width: | Height: | Size: 710 B After Width: | Height: | Size: 710 B |
|
Before Width: | Height: | Size: 922 B After Width: | Height: | Size: 922 B |
|
Before Width: | Height: | Size: 922 B After Width: | Height: | Size: 922 B |
|
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 577 B |
|
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 577 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B |
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 449 B |
|
Before Width: | Height: | Size: 714 B After Width: | Height: | Size: 714 B |
|
Before Width: | Height: | Size: 714 B After Width: | Height: | Size: 714 B |
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 307 B |
|
Before Width: | Height: | Size: 307 B After Width: | Height: | Size: 307 B |
|
Before Width: | Height: | Size: 243 B After Width: | Height: | Size: 243 B |
@@ -10,11 +10,12 @@ import { compareIgnoreCase } from 'vs/base/common/strings';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { onUnexpectedExternalError } from 'vs/base/common/errors';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IModel, IEditorContribution, ICommonCodeEditor } from 'vs/editor/common/editorCommon';
|
||||
import { CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
|
||||
import { IModel, IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { ISuggestResult, ISuggestSupport, ISuggestion, SuggestRegistry, SuggestContext, SuggestTriggerKind } from 'vs/editor/common/modes';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
||||
export const Context = {
|
||||
Visible: new RawContextKey<boolean>('suggestWidgetVisible', false),
|
||||
@@ -201,7 +202,7 @@ export function getSuggestionComparator(snippetConfig: SnippetConfig): (a: ISugg
|
||||
}
|
||||
}
|
||||
|
||||
CommonEditorRegistry.registerDefaultLanguageCommand('_executeCompletionItemProvider', (model, position, args) => {
|
||||
registerDefaultLanguageCommand('_executeCompletionItemProvider', (model, position, args) => {
|
||||
|
||||
const result: ISuggestResult = {
|
||||
incomplete: false,
|
||||
@@ -237,11 +238,10 @@ SuggestRegistry.register('*', _provider);
|
||||
* @param editor
|
||||
* @param suggestions
|
||||
*/
|
||||
export function showSimpleSuggestions(editor: ICommonCodeEditor, suggestions: ISuggestion[]) {
|
||||
export function showSimpleSuggestions(editor: ICodeEditor, suggestions: ISuggestion[]) {
|
||||
setTimeout(() => {
|
||||
_suggestions = suggestions;
|
||||
editor.getContribution<SuggestController>('editor.contrib.suggestController').triggerSuggest([_provider]);
|
||||
_suggestions = undefined;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
@@ -10,24 +10,24 @@ import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ICommonCodeEditor, IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { editorAction, ServicesAccessor, EditorAction, EditorCommand, CommonEditorRegistry } from 'vs/editor/common/editorCommonExtensions';
|
||||
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { alert } from 'vs/base/browser/ui/aria/aria';
|
||||
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ISuggestSupport } from 'vs/editor/common/modes';
|
||||
import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
|
||||
import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
|
||||
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
|
||||
import { Context as SuggestContext } from './suggest';
|
||||
import { SuggestModel, State } from './suggestModel';
|
||||
import { ICompletionItem } from './completionModel';
|
||||
import { SuggestWidget } from './suggestWidget';
|
||||
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { SuggestMemories } from 'vs/editor/contrib/suggest/suggestMemory';
|
||||
|
||||
class AcceptOnCharacterOracle {
|
||||
|
||||
@@ -75,30 +75,51 @@ class AcceptOnCharacterOracle {
|
||||
}
|
||||
}
|
||||
|
||||
@editorContribution
|
||||
export class SuggestController implements IEditorContribution {
|
||||
|
||||
private static ID: string = 'editor.contrib.suggestController';
|
||||
|
||||
public static get(editor: ICommonCodeEditor): SuggestController {
|
||||
public static get(editor: ICodeEditor): SuggestController {
|
||||
return editor.getContribution<SuggestController>(SuggestController.ID);
|
||||
}
|
||||
|
||||
private _model: SuggestModel;
|
||||
private _widget: SuggestWidget;
|
||||
private _memory: SuggestMemories;
|
||||
private _toDispose: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private _editor: ICodeEditor,
|
||||
@ICommandService private _commandService: ICommandService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService,
|
||||
@IContextKeyService _contextKeyService: IContextKeyService,
|
||||
@IInstantiationService _instantiationService: IInstantiationService
|
||||
@IContextKeyService private _contextKeyService: IContextKeyService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
) {
|
||||
this._model = new SuggestModel(this._editor);
|
||||
this._toDispose.push(this._model.onDidTrigger(e => this._widget.showTriggered(e.auto)));
|
||||
this._toDispose.push(this._model.onDidSuggest(e => this._widget.showSuggestions(e.completionModel, e.isFrozen, e.auto)));
|
||||
this._toDispose.push(this._model.onDidCancel(e => !e.retrigger && this._widget.hideWidget()));
|
||||
this._memory = _instantiationService.createInstance(SuggestMemories);
|
||||
|
||||
this._toDispose.push(this._model.onDidTrigger(e => {
|
||||
if (!this._widget) {
|
||||
this._createSuggestWidget();
|
||||
}
|
||||
this._widget.showTriggered(e.auto);
|
||||
}));
|
||||
let lastSelectedItem: ICompletionItem;
|
||||
this._toDispose.push(this._model.onDidSuggest(e => {
|
||||
let index = this._memory.select(this._editor.getModel().getLanguageIdentifier(), e.completionModel.items, lastSelectedItem);
|
||||
if (index >= 0) {
|
||||
lastSelectedItem = e.completionModel.items[index];
|
||||
} else {
|
||||
index = 0;
|
||||
lastSelectedItem = undefined;
|
||||
}
|
||||
this._widget.showSuggestions(e.completionModel, index, e.isFrozen, e.auto);
|
||||
}));
|
||||
this._toDispose.push(this._model.onDidCancel(e => {
|
||||
if (this._widget && !e.retrigger) {
|
||||
this._widget.hideWidget();
|
||||
lastSelectedItem = undefined;
|
||||
}
|
||||
}));
|
||||
|
||||
// Manage the acceptSuggestionsOnEnter context key
|
||||
let acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService);
|
||||
@@ -108,12 +129,15 @@ export class SuggestController implements IEditorContribution {
|
||||
};
|
||||
this._toDispose.push(this._editor.onDidChangeConfiguration((e) => updateFromConfig()));
|
||||
updateFromConfig();
|
||||
}
|
||||
|
||||
this._widget = _instantiationService.createInstance(SuggestWidget, this._editor);
|
||||
private _createSuggestWidget(): void {
|
||||
|
||||
this._widget = this._instantiationService.createInstance(SuggestWidget, this._editor);
|
||||
this._toDispose.push(this._widget.onDidSelect(this._onDidSelectItem, this));
|
||||
|
||||
// Wire up logic to accept a suggestion on certain characters
|
||||
const autoAcceptOracle = new AcceptOnCharacterOracle(_editor, this._widget, item => this._onDidSelectItem(item));
|
||||
const autoAcceptOracle = new AcceptOnCharacterOracle(this._editor, this._widget, item => this._onDidSelectItem(item));
|
||||
this._toDispose.push(
|
||||
autoAcceptOracle,
|
||||
this._model.onDidSuggest(e => {
|
||||
@@ -123,7 +147,7 @@ export class SuggestController implements IEditorContribution {
|
||||
})
|
||||
);
|
||||
|
||||
let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(_contextKeyService);
|
||||
let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
|
||||
this._toDispose.push(this._widget.onDidFocus(item => {
|
||||
|
||||
const position = this._editor.getPosition();
|
||||
@@ -176,7 +200,8 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
|
||||
const { suggestion, position } = item;
|
||||
const columnDelta = this._editor.getPosition().column - position.column;
|
||||
const editorColumn = this._editor.getPosition().column;
|
||||
const columnDelta = editorColumn - position.column;
|
||||
|
||||
if (Array.isArray(suggestion.additionalTextEdits)) {
|
||||
this._editor.pushUndoStop();
|
||||
@@ -184,6 +209,9 @@ export class SuggestController implements IEditorContribution {
|
||||
this._editor.pushUndoStop();
|
||||
}
|
||||
|
||||
// remember this word for future invocations
|
||||
this._memory.remember(this._editor.getModel().getLanguageIdentifier(), item);
|
||||
|
||||
let { insertText } = suggestion;
|
||||
if (suggestion.snippetType !== 'textmate') {
|
||||
insertText = SnippetParser.escape(insertText);
|
||||
@@ -210,15 +238,6 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
|
||||
this._alertCompletionItem(item);
|
||||
/* __GDPR__
|
||||
"suggestSnippetInsert" : {
|
||||
"suggestionType" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
|
||||
"${include}": [
|
||||
"${EditorTelemetryData}"
|
||||
]
|
||||
}
|
||||
*/
|
||||
this._telemetryService.publicLog('suggestSnippetInsert', { ...this._editor.getTelemetryData(), suggestionType: suggestion.type });
|
||||
}
|
||||
|
||||
private _alertCompletionItem({ suggestion }: ICompletionItem): void {
|
||||
@@ -295,7 +314,6 @@ export class SuggestController implements IEditorContribution {
|
||||
}
|
||||
}
|
||||
|
||||
@editorAction
|
||||
export class TriggerSuggestAction extends EditorAction {
|
||||
|
||||
static readonly id = 'editor.action.triggerSuggest';
|
||||
@@ -314,7 +332,7 @@ export class TriggerSuggestAction extends EditorAction {
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: ICommonCodeEditor): void {
|
||||
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
|
||||
const controller = SuggestController.get(editor);
|
||||
|
||||
if (!controller) {
|
||||
@@ -325,12 +343,15 @@ export class TriggerSuggestAction extends EditorAction {
|
||||
}
|
||||
}
|
||||
|
||||
const weight = CommonEditorRegistry.commandWeight(90);
|
||||
registerEditorContribution(SuggestController);
|
||||
registerEditorAction(TriggerSuggestAction);
|
||||
|
||||
const weight = KeybindingsRegistry.WEIGHT.editorContrib(90);
|
||||
|
||||
const SuggestCommand = EditorCommand.bindToContribution<SuggestController>(SuggestController.get);
|
||||
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'acceptSelectedSuggestion',
|
||||
precondition: SuggestContext.Visible,
|
||||
handler: x => x.acceptSelectedSuggestion(),
|
||||
@@ -341,7 +362,7 @@ CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
}
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'acceptSelectedSuggestionOnEnter',
|
||||
precondition: SuggestContext.Visible,
|
||||
handler: x => x.acceptSelectedSuggestion(),
|
||||
@@ -352,7 +373,7 @@ CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
}
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'hideSuggestWidget',
|
||||
precondition: SuggestContext.Visible,
|
||||
handler: x => x.cancelSuggestWidget(),
|
||||
@@ -364,7 +385,7 @@ CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
}
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'selectNextSuggestion',
|
||||
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
|
||||
handler: c => c.selectNextSuggestion(),
|
||||
@@ -377,7 +398,7 @@ CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
}
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'selectNextPageSuggestion',
|
||||
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
|
||||
handler: c => c.selectNextPageSuggestion(),
|
||||
@@ -389,13 +410,13 @@ CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
}
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'selectLastSuggestion',
|
||||
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
|
||||
handler: c => c.selectLastSuggestion()
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'selectPrevSuggestion',
|
||||
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
|
||||
handler: c => c.selectPrevSuggestion(),
|
||||
@@ -408,7 +429,7 @@ CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
}
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'selectPrevPageSuggestion',
|
||||
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
|
||||
handler: c => c.selectPrevPageSuggestion(),
|
||||
@@ -420,13 +441,13 @@ CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
}
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'selectFirstSuggestion',
|
||||
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
|
||||
handler: c => c.selectFirstSuggestion()
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'toggleSuggestionDetails',
|
||||
precondition: SuggestContext.Visible,
|
||||
handler: x => x.toggleSuggestionDetails(),
|
||||
@@ -438,7 +459,7 @@ CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
}
|
||||
}));
|
||||
|
||||
CommonEditorRegistry.registerEditorCommand(new SuggestCommand({
|
||||
registerEditorCommand(new SuggestCommand({
|
||||
id: 'toggleSuggestionFocus',
|
||||
precondition: SuggestContext.Visible,
|
||||
handler: x => x.toggleSuggestionFocus(),
|
||||
107
src/vs/editor/contrib/suggest/suggestMemory.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { ICompletionItem } from 'vs/editor/contrib/suggest/completionModel';
|
||||
import { LRUCache } from 'vs/base/common/map';
|
||||
import { LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export class SuggestMemories {
|
||||
|
||||
private readonly _storagePrefix = 'suggest/memories';
|
||||
private readonly _data = new Map<string, SuggestMemory>();
|
||||
|
||||
constructor(
|
||||
@IStorageService private _storageService: IStorageService
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
remember({ language }: LanguageIdentifier, item: ICompletionItem): void {
|
||||
let memory = this._data.get(language);
|
||||
if (!memory) {
|
||||
memory = new SuggestMemory();
|
||||
this._data.set(language, memory);
|
||||
}
|
||||
memory.remember(item);
|
||||
this._storageService.store(`${this._storagePrefix}/${language}`, JSON.stringify(memory), StorageScope.WORKSPACE);
|
||||
}
|
||||
|
||||
select({ language }: LanguageIdentifier, items: ICompletionItem[], last: ICompletionItem): number {
|
||||
let memory = this._data.get(language);
|
||||
if (!memory) {
|
||||
const key: string = `${this._storagePrefix}/${language}`;
|
||||
const raw = this._storageService.get(key, StorageScope.WORKSPACE);
|
||||
if (raw) {
|
||||
try {
|
||||
const tuples = <[string, MemoryItem][]>JSON.parse(raw);
|
||||
memory = new SuggestMemory(tuples);
|
||||
last = undefined;
|
||||
this._data.set(language, memory);
|
||||
} catch (e) {
|
||||
this._storageService.remove(key, StorageScope.WORKSPACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (memory) {
|
||||
return memory.select(items, last);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export interface MemoryItem {
|
||||
type: string;
|
||||
insertText: string;
|
||||
}
|
||||
|
||||
export class SuggestMemory {
|
||||
|
||||
private readonly _memory = new LRUCache<string, MemoryItem>(400, 0.75);
|
||||
|
||||
constructor(tuples?: [string, MemoryItem][]) {
|
||||
if (tuples) {
|
||||
for (const [word, item] of tuples) {
|
||||
this._memory.set(word, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remember(item: ICompletionItem): void {
|
||||
if (item.word) {
|
||||
this._memory.set(item.word, { insertText: item.suggestion.insertText, type: item.suggestion.type });
|
||||
}
|
||||
}
|
||||
|
||||
select(items: ICompletionItem[], last: ICompletionItem): number {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i] === last) {
|
||||
// prefer the last selected item when
|
||||
// there is one
|
||||
return i;
|
||||
}
|
||||
if (items[i].word) {
|
||||
const item = this._memory.get(items[i].word);
|
||||
if (this._matches(item, items[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private _matches(item: MemoryItem, candidate: ICompletionItem): boolean {
|
||||
return item && item.insertText === candidate.suggestion.insertText && item.type === candidate.suggestion.type;
|
||||
}
|
||||
|
||||
toJSON(): [string, MemoryItem][] {
|
||||
const tuples: [string, MemoryItem][] = [];
|
||||
this._memory.forEach((value, key) => tuples.push([key, value]));
|
||||
return tuples;
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,13 @@ import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { ICommonCodeEditor, IModel, IWordAtPosition } from 'vs/editor/common/editorCommon';
|
||||
import { IModel, IWordAtPosition } from 'vs/editor/common/editorCommon';
|
||||
import { ISuggestSupport, SuggestRegistry, StandardTokenType, SuggestTriggerKind } from 'vs/editor/common/modes';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { provideSuggestionItems, getSuggestionComparator, ISuggestionItem } from './suggest';
|
||||
import { CompletionModel } from './completionModel';
|
||||
import { CursorChangeReason, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
|
||||
export interface ICancelEvent {
|
||||
retrigger: boolean;
|
||||
@@ -38,7 +39,7 @@ export interface SuggestTriggerContext {
|
||||
|
||||
export class LineContext {
|
||||
|
||||
static shouldAutoTrigger(editor: ICommonCodeEditor): boolean {
|
||||
static shouldAutoTrigger(editor: ICodeEditor): boolean {
|
||||
const model = editor.getModel();
|
||||
if (!model) {
|
||||
return false;
|
||||
@@ -58,7 +59,7 @@ export class LineContext {
|
||||
return true;
|
||||
}
|
||||
|
||||
static isInEditableRange(editor: ICommonCodeEditor): boolean {
|
||||
static isInEditableRange(editor: ICodeEditor): boolean {
|
||||
const model = editor.getModel();
|
||||
const position = editor.getPosition();
|
||||
if (model.hasEditableRange()) {
|
||||
@@ -93,7 +94,7 @@ export const enum State {
|
||||
|
||||
export class SuggestModel implements IDisposable {
|
||||
|
||||
private _editor: ICommonCodeEditor;
|
||||
private _editor: ICodeEditor;
|
||||
private _toDispose: IDisposable[] = [];
|
||||
private _quickSuggestDelay: number;
|
||||
private _triggerCharacterListener: IDisposable;
|
||||
@@ -114,7 +115,7 @@ export class SuggestModel implements IDisposable {
|
||||
readonly onDidTrigger: Event<ITriggerEvent> = this._onDidTrigger.event;
|
||||
readonly onDidSuggest: Event<ISuggestEvent> = this._onDidSuggest.event;
|
||||
|
||||
constructor(editor: ICommonCodeEditor) {
|
||||
constructor(editor: ICodeEditor) {
|
||||
this._editor = editor;
|
||||
this._state = State.Idle;
|
||||
this._triggerAutoSuggestPromise = null;
|
||||
@@ -222,6 +223,8 @@ export class SuggestModel implements IDisposable {
|
||||
|
||||
cancel(retrigger: boolean = false): void {
|
||||
|
||||
this._triggerRefilter.cancel();
|
||||
|
||||
if (this._triggerAutoSuggestPromise) {
|
||||
this._triggerAutoSuggestPromise.cancel();
|
||||
this._triggerAutoSuggestPromise = null;
|
||||
@@ -256,17 +259,14 @@ export class SuggestModel implements IDisposable {
|
||||
this._currentPosition = this._editor.getPosition();
|
||||
|
||||
if (!e.selection.isEmpty()
|
||||
|| e.source !== 'keyboard'
|
||||
|| e.reason !== CursorChangeReason.NotSet
|
||||
|| (e.source !== 'keyboard' && e.source !== 'deleteLeft')
|
||||
) {
|
||||
|
||||
if (this._state === State.Idle) {
|
||||
// Early exit if nothing needs to be done!
|
||||
// Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
|
||||
return;
|
||||
// Early exit if nothing needs to be done!
|
||||
// Leave some form of early exit check here if you wish to continue being a cursor position change listener ;)
|
||||
if (this._state !== State.Idle) {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/browser/markdownRenderer';
|
||||
import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
||||
const sticky = true; // for development purposes
|
||||
const sticky = false; // for development purposes
|
||||
const expandSuggestionDocsByDefault = false;
|
||||
const maxSuggestionsToShow = 12;
|
||||
|
||||
@@ -153,6 +153,7 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
|
||||
}
|
||||
|
||||
data.highlightedLabel.set(suggestion.label, createMatches(element.matches));
|
||||
// data.highlightedLabel.set(`${suggestion.label} <${element.score}=score(${element.word}, ${suggestion.filterText || suggestion.label})>`, createMatches(element.matches));
|
||||
data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
|
||||
|
||||
if (canExpandCompletionItem(element)) {
|
||||
@@ -222,7 +223,7 @@ class SuggestionDetails {
|
||||
|
||||
this.header = append(this.body, $('.header'));
|
||||
this.close = append(this.header, $('span.close'));
|
||||
this.close.title = nls.localize('readLess', "Read less...{0}", triggerKeybindingLabel);
|
||||
this.close.title = nls.localize('readLess', "Read less...{0}", this.triggerKeybindingLabel);
|
||||
this.type = append(this.header, $('p.type'));
|
||||
|
||||
this.docs = append(this.body, $('p.docs'));
|
||||
@@ -425,7 +426,8 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
|
||||
this.list = new List(this.listElement, this, [renderer], {
|
||||
useShadows: false,
|
||||
selectOnMouseDown: true
|
||||
selectOnMouseDown: true,
|
||||
focusOnMouseDown: false
|
||||
});
|
||||
|
||||
this.toDispose = [
|
||||
@@ -497,7 +499,9 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
}
|
||||
|
||||
const item = e.elements[0];
|
||||
this.onDidSelectEmitter.fire(item);
|
||||
item.resolve().then(() => {
|
||||
this.onDidSelectEmitter.fire(item);
|
||||
});
|
||||
|
||||
alert(nls.localize('suggestionAriaAccepted', "{0}, accepted", item.suggestion.label));
|
||||
|
||||
@@ -557,21 +561,12 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
}
|
||||
|
||||
this._ariaAlert(null);
|
||||
// TODO@Alex: Chromium bug
|
||||
// this.editor.setAriaActiveDescendant(null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const item = e.elements[0];
|
||||
this._ariaAlert(this._getSuggestionAriaAlertLabel(item));
|
||||
|
||||
// TODO@Alex: Chromium bug
|
||||
// // TODO@Alex: the list is not done rendering...
|
||||
// setTimeout(() => {
|
||||
// this.editor.setAriaActiveDescendant(this.list.getElementId(e.indexes[0]));
|
||||
// }, 100);
|
||||
|
||||
if (item === this.focusedItem) {
|
||||
return;
|
||||
}
|
||||
@@ -699,7 +694,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
}
|
||||
}
|
||||
|
||||
showSuggestions(completionModel: CompletionModel, isFrozen: boolean, isAuto: boolean): void {
|
||||
showSuggestions(completionModel: CompletionModel, selectionIndex: number, isFrozen: boolean, isAuto: boolean): void {
|
||||
if (this.loadingTimeout) {
|
||||
clearTimeout(this.loadingTimeout);
|
||||
this.loadingTimeout = null;
|
||||
@@ -743,8 +738,8 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
this.focusedItem = null;
|
||||
this.focusedItemIndex = null;
|
||||
this.list.splice(0, this.list.length, this.completionModel.items);
|
||||
this.list.setFocus([0]);
|
||||
this.list.reveal(0, 0);
|
||||
this.list.setFocus([selectionIndex]);
|
||||
this.list.reveal(selectionIndex, selectionIndex);
|
||||
|
||||
if (isFrozen) {
|
||||
this.setState(State.Frozen);
|
||||
@@ -1106,7 +1101,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
let matchHighlight = theme.getColor(editorSuggestWidgetHighlightForeground);
|
||||
if (matchHighlight) {
|
||||
collector.addRule(`.monaco-editor .suggest-widget:not(.frozen) .monaco-list .monaco-list-row .monaco-highlighted-label .highlight { color: ${matchHighlight}; }`);
|
||||
collector.addRule(`.monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight { color: ${matchHighlight}; }`);
|
||||
}
|
||||
let foreground = theme.getColor(editorSuggestWidgetForeground);
|
||||
if (foreground) {
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { ISuggestion, ISuggestResult, ISuggestSupport, SuggestionType } from 'vs/editor/common/modes';
|
||||
import { ISuggestionItem, getSuggestionComparator } from 'vs/editor/contrib/suggest/browser/suggest';
|
||||
import { CompletionModel } from 'vs/editor/contrib/suggest/browser/completionModel';
|
||||
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';
|
||||
|
||||
@@ -225,7 +225,74 @@ suite('CompletionModel', function () {
|
||||
const [first, second] = model.items;
|
||||
assert.equal(first.suggestion.label, 'source');
|
||||
assert.equal(second.suggestion.label, '<- groups');
|
||||
|
||||
});
|
||||
|
||||
test('Score only filtered items when typing more, score all when typing less', function () {
|
||||
model = new CompletionModel([
|
||||
createSuggestItem('console', 0, 'property'),
|
||||
createSuggestItem('co_new', 0, 'property'),
|
||||
createSuggestItem('bar', 0, 'property'),
|
||||
createSuggestItem('car', 0, 'property'),
|
||||
createSuggestItem('foo', 0, 'property'),
|
||||
], 1, {
|
||||
leadingLineContent: '',
|
||||
characterCountDelta: 0
|
||||
}, 'inline');
|
||||
|
||||
assert.equal(model.items.length, 5);
|
||||
|
||||
// narrow down once
|
||||
model.lineContext = { leadingLineContent: 'c', characterCountDelta: 1 };
|
||||
assert.equal(model.items.length, 3);
|
||||
|
||||
// query gets longer, narrow down the narrow-down'ed-set from before
|
||||
model.lineContext = { leadingLineContent: 'cn', characterCountDelta: 2 };
|
||||
assert.equal(model.items.length, 2);
|
||||
|
||||
// query gets shorter, refilter everything
|
||||
model.lineContext = { leadingLineContent: '', characterCountDelta: 0 };
|
||||
assert.equal(model.items.length, 5);
|
||||
});
|
||||
|
||||
test('Have more relaxed suggest matching algorithm #15419', function () {
|
||||
model = new CompletionModel([
|
||||
createSuggestItem('result', 0, 'property'),
|
||||
createSuggestItem('replyToUser', 0, 'property'),
|
||||
createSuggestItem('randomLolut', 0, 'property'),
|
||||
createSuggestItem('car', 0, 'property'),
|
||||
createSuggestItem('foo', 0, 'property'),
|
||||
], 1, {
|
||||
leadingLineContent: '',
|
||||
characterCountDelta: 0
|
||||
}, 'inline');
|
||||
|
||||
// query gets longer, narrow down the narrow-down'ed-set from before
|
||||
model.lineContext = { leadingLineContent: 'rlut', characterCountDelta: 4 };
|
||||
assert.equal(model.items.length, 3);
|
||||
|
||||
const [first, second, third] = model.items;
|
||||
assert.equal(first.suggestion.label, 'result'); // best with `rult`
|
||||
assert.equal(second.suggestion.label, 'replyToUser'); // best with `rltu`
|
||||
assert.equal(third.suggestion.label, 'randomLolut'); // best with `rlut`
|
||||
});
|
||||
|
||||
test('Emmet suggestion not appearing at the top of the list in jsx files, #39518', function () {
|
||||
model = new CompletionModel([
|
||||
createSuggestItem('from', 0, 'property'),
|
||||
createSuggestItem('form', 0, 'property'),
|
||||
createSuggestItem('form:get', 0, 'property'),
|
||||
createSuggestItem('testForeignMeasure', 0, 'property'),
|
||||
createSuggestItem('fooRoom', 0, 'property'),
|
||||
], 1, {
|
||||
leadingLineContent: '',
|
||||
characterCountDelta: 0
|
||||
}, 'inline');
|
||||
|
||||
model.lineContext = { leadingLineContent: 'form', characterCountDelta: 4 };
|
||||
assert.equal(model.items.length, 5);
|
||||
const [first, second, third] = model.items;
|
||||
assert.equal(first.suggestion.label, 'form'); // best with `form`
|
||||
assert.equal(second.suggestion.label, 'form:get'); // best with `form`
|
||||
assert.equal(third.suggestion.label, 'from'); // best with `from`
|
||||
});
|
||||
});
|
||||
@@ -8,7 +8,7 @@ import * as assert from 'assert';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { SuggestRegistry } from 'vs/editor/common/modes';
|
||||
import { provideSuggestionItems } from 'vs/editor/contrib/suggest/browser/suggest';
|
||||
import { provideSuggestionItems } from 'vs/editor/contrib/suggest/suggest';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Model } from 'vs/editor/common/model/model';
|
||||
|
||||
@@ -10,10 +10,10 @@ import URI from 'vs/base/common/uri';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { Model } from 'vs/editor/common/model/model';
|
||||
import { ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon';
|
||||
import { Handler } from 'vs/editor/common/editorCommon';
|
||||
import { ISuggestSupport, ISuggestResult, SuggestRegistry, SuggestTriggerKind } from 'vs/editor/common/modes';
|
||||
import { SuggestModel, LineContext } from 'vs/editor/contrib/suggest/browser/suggestModel';
|
||||
import { MockCodeEditor, MockScopeLocation } from 'vs/editor/test/common/mocks/mockCodeEditor';
|
||||
import { SuggestModel, LineContext } from 'vs/editor/contrib/suggest/suggestModel';
|
||||
import { TestCodeEditor, MockScopeLocation } from 'vs/editor/test/browser/testCodeEditor';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
@@ -22,8 +22,10 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
|
||||
|
||||
function createMockEditor(model: Model): MockCodeEditor {
|
||||
function createMockEditor(model: Model): TestCodeEditor {
|
||||
const contextKeyService = new MockContextKeyService();
|
||||
const telemetryService = NullTelemetryService;
|
||||
const instantiationService = new InstantiationService(new ServiceCollection(
|
||||
@@ -31,7 +33,7 @@ function createMockEditor(model: Model): MockCodeEditor {
|
||||
[ITelemetryService, telemetryService]
|
||||
));
|
||||
|
||||
const editor = new MockCodeEditor(new MockScopeLocation(), {}, instantiationService, contextKeyService);
|
||||
const editor = new TestCodeEditor(new MockScopeLocation(), {}, instantiationService, contextKeyService);
|
||||
editor.setModel(model);
|
||||
return editor;
|
||||
}
|
||||
@@ -70,8 +72,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
|
||||
|
||||
const alwaysEmptySupport: ISuggestSupport = {
|
||||
provideCompletionItems(doc, pos) {
|
||||
return <ISuggestResult>{
|
||||
provideCompletionItems(doc, pos): ISuggestResult {
|
||||
return {
|
||||
incomplete: false,
|
||||
suggestions: []
|
||||
};
|
||||
@@ -79,8 +81,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
};
|
||||
|
||||
const alwaysSomethingSupport: ISuggestSupport = {
|
||||
provideCompletionItems(doc, pos) {
|
||||
return <ISuggestResult>{
|
||||
provideCompletionItems(doc, pos): ISuggestResult {
|
||||
return {
|
||||
incomplete: false,
|
||||
suggestions: [{
|
||||
label: doc.getWordUntilPosition(pos).word,
|
||||
@@ -100,7 +102,7 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
disposables.push(model);
|
||||
});
|
||||
|
||||
function withOracle(callback: (model: SuggestModel, editor: ICommonCodeEditor) => any): TPromise<any> {
|
||||
function withOracle(callback: (model: SuggestModel, editor: ICodeEditor) => any): TPromise<any> {
|
||||
|
||||
return new TPromise((resolve, reject) => {
|
||||
const editor = createMockEditor(model);
|
||||
@@ -222,9 +224,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
test('#17400: Keep filtering suggestModel.ts after space', function () {
|
||||
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
provideCompletionItems(doc, pos) {
|
||||
return <ISuggestResult>{
|
||||
currentWord: '',
|
||||
provideCompletionItems(doc, pos): ISuggestResult {
|
||||
return {
|
||||
incomplete: false,
|
||||
suggestions: [{
|
||||
label: 'My Table',
|
||||
@@ -272,9 +273,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
test('#21484: Trigger character always force a new completion session', function () {
|
||||
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
provideCompletionItems(doc, pos) {
|
||||
return <ISuggestResult>{
|
||||
currentWord: '',
|
||||
provideCompletionItems(doc, pos): ISuggestResult {
|
||||
return {
|
||||
incomplete: false,
|
||||
suggestions: [{
|
||||
label: 'foo.bar',
|
||||
@@ -288,9 +288,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
triggerCharacters: ['.'],
|
||||
provideCompletionItems(doc, pos) {
|
||||
return <ISuggestResult>{
|
||||
currentWord: '',
|
||||
provideCompletionItems(doc, pos): ISuggestResult {
|
||||
return {
|
||||
incomplete: false,
|
||||
suggestions: [{
|
||||
label: 'boom',
|
||||
@@ -383,8 +382,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
test('Incomplete suggestion results cause re-triggering when typing w/o further context, #28400 (1/2)', function () {
|
||||
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
provideCompletionItems(doc, pos) {
|
||||
return <ISuggestResult>{
|
||||
provideCompletionItems(doc, pos): ISuggestResult {
|
||||
return {
|
||||
incomplete: true,
|
||||
suggestions: [{
|
||||
label: 'foo',
|
||||
@@ -420,8 +419,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
test('Incomplete suggestion results cause re-triggering when typing w/o further context, #28400 (2/2)', function () {
|
||||
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
provideCompletionItems(doc, pos) {
|
||||
return <ISuggestResult>{
|
||||
provideCompletionItems(doc, pos): ISuggestResult {
|
||||
return {
|
||||
incomplete: true,
|
||||
suggestions: [{
|
||||
label: 'foo;',
|
||||
@@ -464,11 +463,10 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
let triggerCharacter = '';
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
triggerCharacters: ['.'],
|
||||
provideCompletionItems(doc, pos, context) {
|
||||
provideCompletionItems(doc, pos, context): ISuggestResult {
|
||||
assert.equal(context.triggerKind, SuggestTriggerKind.TriggerCharacter);
|
||||
triggerCharacter = context.triggerCharacter;
|
||||
return <ISuggestResult>{
|
||||
currentWord: '',
|
||||
return {
|
||||
incomplete: false,
|
||||
suggestions: [
|
||||
{
|
||||
@@ -497,8 +495,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
|
||||
test('Mac press and hold accent character insertion does not update suggestions, #35269', function () {
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, {
|
||||
provideCompletionItems(doc, pos) {
|
||||
return <ISuggestResult>{
|
||||
provideCompletionItems(doc, pos): ISuggestResult {
|
||||
return {
|
||||
incomplete: true,
|
||||
suggestions: [{
|
||||
label: 'abc',
|
||||
@@ -537,4 +535,33 @@ suite('SuggestModel - TriggerAndCancelOracle', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Backspace should not always cancel code completion, #36491', function () {
|
||||
disposables.push(SuggestRegistry.register({ scheme: 'test' }, alwaysSomethingSupport));
|
||||
|
||||
return withOracle(async (model, editor) => {
|
||||
await assertEvent(model.onDidSuggest, () => {
|
||||
editor.setPosition({ lineNumber: 1, column: 4 });
|
||||
editor.trigger('keyboard', Handler.Type, { text: 'd' });
|
||||
|
||||
}, event => {
|
||||
assert.equal(event.auto, true);
|
||||
assert.equal(event.completionModel.items.length, 1);
|
||||
const [first] = event.completionModel.items;
|
||||
|
||||
assert.equal(first.support, alwaysSomethingSupport);
|
||||
});
|
||||
|
||||
await assertEvent(model.onDidSuggest, () => {
|
||||
CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null);
|
||||
|
||||
}, event => {
|
||||
assert.equal(event.auto, true);
|
||||
assert.equal(event.completionModel.items.length, 1);
|
||||
const [first] = event.completionModel.items;
|
||||
|
||||
assert.equal(first.support, alwaysSomethingSupport);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||