diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index add8b9df096..8b81bb80476 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -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(); } diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index a121aeebcc4..7bd845976f0 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -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"); diff --git a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts index 79007e58847..0e7b07e1a76 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts @@ -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.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.get);