From 83bcded107a09d822e4e46cd73f5cc1c21bc3ec1 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 19 Jan 2017 10:07:22 +0100 Subject: [PATCH] Add SnippetString to standalone languages --- .../browser/standalone/standaloneLanguages.ts | 111 ++++++++++++------ src/vs/monaco.d.ts | 43 +++++-- 2 files changed, 113 insertions(+), 41 deletions(-) diff --git a/src/vs/editor/browser/standalone/standaloneLanguages.ts b/src/vs/editor/browser/standalone/standaloneLanguages.ts index 985eb430033..ddaf6333497 100644 --- a/src/vs/editor/browser/standalone/standaloneLanguages.ts +++ b/src/vs/editor/browser/standalone/standaloneLanguages.ts @@ -395,6 +395,25 @@ export enum CompletionItemKind { Reference, Folder } + +/** + * A snippet string is a template which allows to insert text + * and to control the editor cursor when insertion happens. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Variables are defined with `$name` and + * `${name:default value}`. The full snippet syntax is documented + * [here](http://code.visualstudio.com/docs/customization/userdefinedsnippets#_creating-your-own-snippets). + */ +export interface SnippetString { + + /** + * The snippet string. + */ + value: string; +} + /** * A completion item represents a text snippet that is * proposed to complete text that is being typed. @@ -433,18 +452,30 @@ export interface CompletionItem { */ filterText?: string; /** - * A string that should be inserted in a document when selecting + * A string or snippet that should be inserted in a document when selecting * this completion. When `falsy` the [label](#CompletionItem.label) * is used. */ - insertText?: string; + insertText?: string | SnippetString; /** - * An [edit](#TextEdit) which is applied to a document when selecting - * this completion. When an edit is provided the value of - * [insertText](#CompletionItem.insertText) is ignored. + * A range of text that should be replaced by this completion item. * - * The [range](#Range) of the edit must be single-line and one the same - * line completions where [requested](#CompletionItemProvider.provideCompletionItems) at. + * Defaults to a range from the start of the [current word](#TextDocument.getWordRangeAtPosition) to the + * current position. + * + * *Note:* The range must be a [single line](#Range.isSingleLine) and it must + * [contain](#Range.contains) the position at which completion has been [requested](#CompletionItemProvider.provideCompletionItems). + */ + range?: Range; + /** + * @deprecated **Deprecated** in favor of `CompletionItem.insertText` and `CompletionItem.range`. + * + * ~~An [edit](#TextEdit) which is applied to a document when selecting + * this completion. When an edit is provided the value of + * [insertText](#CompletionItem.insertText) is ignored.~~ + * + * ~~The [range](#Range) of the edit must be single-line and on the same + * line completions were [requested](#CompletionItemProvider.provideCompletionItems) at.~~ */ textEdit?: editorCommon.ISingleEditOperation; } @@ -524,11 +555,11 @@ class SuggestAdapter { this._provider = provider; } - private static from(item: CompletionItem): ISuggestion2 { - return { + private static from(item: CompletionItem, position: Position, wordStartPos: Position): ISuggestion2 { + let suggestion: ISuggestion2 = { _actual: item, label: item.label, - insertText: item.insertText || item.label, + insertText: item.label, type: convertKind(item.kind), detail: item.detail, documentation: item.documentation, @@ -536,6 +567,33 @@ class SuggestAdapter { filterText: item.filterText, snippetType: 'internal' }; + let editRange = item.textEdit ? item.textEdit.range : item.range; + if (editRange) { + let isSingleLine = (editRange.startLineNumber === editRange.endLineNumber); + + // invalid text edit + if (!isSingleLine || editRange.startLineNumber !== position.lineNumber) { + console.warn('INVALID range, must be single line and on the same line'); + return null; + } + + // insert the text of the edit and create a dedicated + // suggestion-container with overwrite[Before|After] + suggestion.overwriteBefore = position.column - editRange.startColumn; + suggestion.overwriteAfter = editRange.endColumn - position.column; + } else { + suggestion.overwriteBefore = position.column - wordStartPos.column; + suggestion.overwriteAfter = 0; + } + if (item.textEdit) { + suggestion.insertText = item.textEdit.text; + } else if (typeof item.insertText === 'object' && typeof item.insertText.value === 'string') { + suggestion.insertText = item.insertText.value; + suggestion.snippetType = 'textmate'; + } else if (typeof item.insertText === 'string') { + suggestion.insertText = item.insertText; + } + return suggestion; } provideCompletionItems(model: editorCommon.IReadOnlyModel, position: Position, token: CancellationToken): Thenable { @@ -571,30 +629,10 @@ class SuggestAdapter { for (let i = 0; i < list.items.length; i++) { const item = list.items[i]; - const suggestion = SuggestAdapter.from(item); - - if (item.textEdit) { - - let editRange = item.textEdit.range; - let isSingleLine = (editRange.startLineNumber === editRange.endLineNumber); - - // invalid text edit - if (!isSingleLine || editRange.startLineNumber !== position.lineNumber) { - console.warn('INVALID text edit, must be single line and on the same line'); - continue; - } - - // insert the text of the edit and create a dedicated - // suggestion-container with overwrite[Before|After] - suggestion.insertText = item.textEdit.text; - suggestion.overwriteBefore = position.column - editRange.startColumn; - suggestion.overwriteAfter = editRange.endColumn - position.column; - } else { - suggestion.overwriteBefore = position.column - wordStartPos.column; - suggestion.overwriteAfter = 0; + const suggestion = SuggestAdapter.from(item, position, wordStartPos); + if (suggestion) { + result.suggestions.push(suggestion); } - - result.suggestions.push(suggestion); } return result; @@ -612,7 +650,12 @@ class SuggestAdapter { } return toThenable(this._provider.resolveCompletionItem(item, token)).then(resolvedItem => { - return SuggestAdapter.from(resolvedItem); + let wordStartPos = position; + const word = model.getWordUntilPosition(position); + if (word) { + wordStartPos = new Position(wordStartPos.lineNumber, word.startColumn); + } + return SuggestAdapter.from(resolvedItem, position, wordStartPos); }); } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b49b661d8f3..41ec714f38a 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4015,6 +4015,23 @@ declare module monaco.languages { Folder = 18, } + /** + * A snippet string is a template which allows to insert text + * and to control the editor cursor when insertion happens. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Variables are defined with `$name` and + * `${name:default value}`. The full snippet syntax is documented + * [here](http://code.visualstudio.com/docs/customization/userdefinedsnippets#_creating-your-own-snippets). + */ + export interface SnippetString { + /** + * The snippet string. + */ + value: string; + } + /** * A completion item represents a text snippet that is * proposed to complete text that is being typed. @@ -4053,18 +4070,30 @@ declare module monaco.languages { */ filterText?: string; /** - * A string that should be inserted in a document when selecting + * A string or snippet that should be inserted in a document when selecting * this completion. When `falsy` the [label](#CompletionItem.label) * is used. */ - insertText?: string; + insertText?: string | SnippetString; /** - * An [edit](#TextEdit) which is applied to a document when selecting - * this completion. When an edit is provided the value of - * [insertText](#CompletionItem.insertText) is ignored. + * A range of text that should be replaced by this completion item. * - * The [range](#Range) of the edit must be single-line and one the same - * line completions where [requested](#CompletionItemProvider.provideCompletionItems) at. + * Defaults to a range from the start of the [current word](#TextDocument.getWordRangeAtPosition) to the + * current position. + * + * *Note:* The range must be a [single line](#Range.isSingleLine) and it must + * [contain](#Range.contains) the position at which completion has been [requested](#CompletionItemProvider.provideCompletionItems). + */ + range?: Range; + /** + * @deprecated **Deprecated** in favor of `CompletionItem.insertText` and `CompletionItem.range`. + * + * ~~An [edit](#TextEdit) which is applied to a document when selecting + * this completion. When an edit is provided the value of + * [insertText](#CompletionItem.insertText) is ignored.~~ + * + * ~~The [range](#Range) of the edit must be single-line and on the same + * line completions were [requested](#CompletionItemProvider.provideCompletionItems) at.~~ */ textEdit?: editor.ISingleEditOperation; }