show IntelliSense window when having multiple tab completions, #22216

This commit is contained in:
Johannes Rieken
2017-07-20 12:21:19 +02:00
parent a549412d56
commit ae0bdbfd9a
3 changed files with 74 additions and 28 deletions
@@ -21,6 +21,7 @@ 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 { Context as SuggestContext } from './suggest';
@@ -210,8 +211,8 @@ export class SuggestController implements IEditorContribution {
alert(msg);
}
triggerSuggest(): void {
this._model.trigger(false, false);
triggerSuggest(onlyFrom?: ISuggestSupport[]): void {
this._model.trigger(false, false, onlyFrom);
this._editor.revealLine(this._editor.getPosition().lineNumber);
this._editor.focus();
}
@@ -85,7 +85,7 @@ export interface ISimpleModel {
getLineContent(lineNumber: number): string;
}
class SnippetSuggestion implements ISuggestion {
export class SnippetSuggestion implements ISuggestion {
private static _userSnippet = localize('source.snippet', "User Snippet");
@@ -9,16 +9,50 @@ import { localize } from 'vs/nls';
import { KeyCode } from 'vs/base/common/keyCodes';
import { RawContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { ISnippetsService, getNonWhitespacePrefix, ISnippet } from 'vs/workbench/parts/snippets/electron-browser/snippetsService';
import { ISnippetsService, getNonWhitespacePrefix, ISnippet, SnippetSuggestion } from 'vs/workbench/parts/snippets/electron-browser/snippetsService';
import { Registry } from 'vs/platform/registry/common/platform';
import { endsWith } from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { CommonEditorRegistry, commonEditorContribution, EditorCommand } from 'vs/editor/common/editorCommonExtensions';
import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2';
import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController';
import { ISuggestSupport, ISuggestResult, SuggestRegistry } from 'vs/editor/common/modes';
import { Position } from 'vs/editor/common/core/position';
import { IConfigurationRegistry, Extensions as ConfigExt } from 'vs/platform/configuration/common/configurationRegistry';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
class TabCompletionProvider implements ISuggestSupport {
position: Position;
snippets: ISnippet[];
reset(): this {
return this.set(undefined, undefined);
}
set(position: Position, snippets: ISnippet[]): this {
this.position = position;
this.snippets = snippets;
return this;
}
provideCompletionItems(model: editorCommon.IModel, position: Position): ISuggestResult {
if (!this.snippets
|| this.position.lineNumber !== position.lineNumber
|| this.position.column > position.column
) {
return undefined;
}
const delta = position.column - this.position.column;
const suggestions: SnippetSuggestion[] = [];
for (const snippet of this.snippets) {
suggestions.push(new SnippetSuggestion(snippet, delta + snippet.prefix.length));
}
return { suggestions };
}
}
@commonEditorContribution
export class TabCompletionController implements editorCommon.IEditorContribution {
@@ -29,20 +63,27 @@ export class TabCompletionController implements editorCommon.IEditorContribution
return editor.getContribution<TabCompletionController>(TabCompletionController.ID);
}
private _snippetController: SnippetController2;
private _cursorChangeSubscription: IDisposable;
private _currentSnippets: ISnippet[] = [];
private readonly _editor: editorCommon.ICommonCodeEditor;
private readonly _snippetController: SnippetController2;
private readonly _suggestController: SuggestController;
private readonly _tabCompletionProvider: TabCompletionProvider;
private readonly _dispoables: IDisposable[] = [];
constructor(
editor: editorCommon.ICommonCodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
@ISnippetsService snippetService: ISnippetsService
) {
this._editor = editor;
this._snippetController = SnippetController2.get(editor);
const hasSnippets = TabCompletionController.ContextKey.bindTo(contextKeyService);
this._cursorChangeSubscription = editor.onDidChangeCursorSelection(e => {
this._suggestController = SuggestController.get(editor);
this._tabCompletionProvider = new TabCompletionProvider();
this._dispoables.push(SuggestRegistry.register('*', this._tabCompletionProvider));
this._currentSnippets.length = 0;
const hasSnippets = TabCompletionController.ContextKey.bindTo(contextKeyService);
this._dispoables.push(editor.onDidChangeCursorSelection(e => {
let snippets: ISnippet[] = [];
let selectFn: (snippet: ISnippet) => boolean;
if (e.selection.isEmpty()) {
@@ -59,32 +100,36 @@ export class TabCompletionController implements editorCommon.IEditorContribution
if (selectFn) {
snippetService.visitSnippets(editor.getModel().getLanguageIdentifier().id, s => {
if (selectFn(s)) {
this._currentSnippets.push(s);
snippets.push(s);
}
return true;
});
}
hasSnippets.set(this._currentSnippets.length === 1); //todo@joh make it work with N
});
}
dispose(): void {
this._cursorChangeSubscription.dispose();
}
performSnippetCompletions(): void {
if (this._currentSnippets.length === 1) {
const snippet = this._currentSnippets[0];
this._snippetController.insert(snippet.codeSnippet, snippet.prefix.length, 0);
// } else {
// todo@joh - show suggest widget with proposals
}
this._tabCompletionProvider.set(editor.getPosition(), snippets);
hasSnippets.set(this._tabCompletionProvider.snippets.length > 0);
}));
}
getId(): string {
return TabCompletionController.ID;
}
dispose(): void {
dispose(this._dispoables);
}
performSnippetCompletions(): void {
const { snippets } = this._tabCompletionProvider;
if (snippets.length === 1) {
// one -> just insert
const [snippet] = snippets;
this._snippetController.insert(snippet.codeSnippet, snippet.prefix.length, 0);
} else if (snippets.length > 1) {
// two or more -> show IntelliSense box
this._suggestController.triggerSuggest([this._tabCompletionProvider]);
}
}
}
const TabCompletionCommand = EditorCommand.bindToContribution<TabCompletionController>(TabCompletionController.get);