@@ -395,6 +395,25 @@ export enum CompletionItemKind {
395395 Reference ,
396396 Folder
397397}
398+
399+ /**
400+ * A snippet string is a template which allows to insert text
401+ * and to control the editor cursor when insertion happens.
402+ *
403+ * A snippet can define tab stops and placeholders with `$1`, `$2`
404+ * and `${3:foo}`. `$0` defines the final tab stop, it defaults to
405+ * the end of the snippet. Variables are defined with `$name` and
406+ * `${name:default value}`. The full snippet syntax is documented
407+ * [here](http://code.visualstudio.com/docs/customization/userdefinedsnippets#_creating-your-own-snippets).
408+ */
409+ export interface SnippetString {
410+
411+ /**
412+ * The snippet string.
413+ */
414+ value : string ;
415+ }
416+
398417/**
399418 * A completion item represents a text snippet that is
400419 * proposed to complete text that is being typed.
@@ -433,18 +452,30 @@ export interface CompletionItem {
433452 */
434453 filterText ?: string ;
435454 /**
436- * A string that should be inserted in a document when selecting
455+ * A string or snippet that should be inserted in a document when selecting
437456 * this completion. When `falsy` the [label](#CompletionItem.label)
438457 * is used.
439458 */
440- insertText ?: string ;
459+ insertText ?: string | SnippetString ;
460+ /**
461+ * A range of text that should be replaced by this completion item.
462+ *
463+ * Defaults to a range from the start of the [current word](#TextDocument.getWordRangeAtPosition) to the
464+ * current position.
465+ *
466+ * *Note:* The range must be a [single line](#Range.isSingleLine) and it must
467+ * [contain](#Range.contains) the position at which completion has been [requested](#CompletionItemProvider.provideCompletionItems).
468+ */
469+ range ?: Range ;
441470 /**
442- * An [edit](#TextEdit) which is applied to a document when selecting
471+ * @deprecated **Deprecated** in favor of `CompletionItem.insertText` and `CompletionItem.range`.
472+ *
473+ * ~~An [edit](#TextEdit) which is applied to a document when selecting
443474 * this completion. When an edit is provided the value of
444- * [insertText](#CompletionItem.insertText) is ignored.
475+ * [insertText](#CompletionItem.insertText) is ignored.~~
445476 *
446- * The [range](#Range) of the edit must be single-line and one the same
447- * line completions where [requested](#CompletionItemProvider.provideCompletionItems) at.
477+ * ~~ The [range](#Range) of the edit must be single-line and on the same
478+ * line completions were [requested](#CompletionItemProvider.provideCompletionItems) at.~~
448479 */
449480 textEdit ?: editorCommon . ISingleEditOperation ;
450481}
@@ -524,18 +555,45 @@ class SuggestAdapter {
524555 this . _provider = provider ;
525556 }
526557
527- private static from ( item : CompletionItem ) : ISuggestion2 {
528- return {
558+ private static from ( item : CompletionItem , position : Position , wordStartPos : Position ) : ISuggestion2 {
559+ let suggestion : ISuggestion2 = {
529560 _actual : item ,
530561 label : item . label ,
531- insertText : item . insertText || item . label ,
562+ insertText : item . label ,
532563 type : convertKind ( item . kind ) ,
533564 detail : item . detail ,
534565 documentation : item . documentation ,
535566 sortText : item . sortText ,
536567 filterText : item . filterText ,
537568 snippetType : 'internal'
538569 } ;
570+ let editRange = item . textEdit ? item . textEdit . range : item . range ;
571+ if ( editRange ) {
572+ let isSingleLine = ( editRange . startLineNumber === editRange . endLineNumber ) ;
573+
574+ // invalid text edit
575+ if ( ! isSingleLine || editRange . startLineNumber !== position . lineNumber ) {
576+ console . warn ( 'INVALID range, must be single line and on the same line' ) ;
577+ return null ;
578+ }
579+
580+ // insert the text of the edit and create a dedicated
581+ // suggestion-container with overwrite[Before|After]
582+ suggestion . overwriteBefore = position . column - editRange . startColumn ;
583+ suggestion . overwriteAfter = editRange . endColumn - position . column ;
584+ } else {
585+ suggestion . overwriteBefore = position . column - wordStartPos . column ;
586+ suggestion . overwriteAfter = 0 ;
587+ }
588+ if ( item . textEdit ) {
589+ suggestion . insertText = item . textEdit . text ;
590+ } else if ( typeof item . insertText === 'object' && typeof item . insertText . value === 'string' ) {
591+ suggestion . insertText = item . insertText . value ;
592+ suggestion . snippetType = 'textmate' ;
593+ } else if ( typeof item . insertText === 'string' ) {
594+ suggestion . insertText = item . insertText ;
595+ }
596+ return suggestion ;
539597 }
540598
541599 provideCompletionItems ( model : editorCommon . IReadOnlyModel , position : Position , token : CancellationToken ) : Thenable < modes . ISuggestResult > {
@@ -571,30 +629,10 @@ class SuggestAdapter {
571629
572630 for ( let i = 0 ; i < list . items . length ; i ++ ) {
573631 const item = list . items [ i ] ;
574- const suggestion = SuggestAdapter . from ( item ) ;
575-
576- if ( item . textEdit ) {
577-
578- let editRange = item . textEdit . range ;
579- let isSingleLine = ( editRange . startLineNumber === editRange . endLineNumber ) ;
580-
581- // invalid text edit
582- if ( ! isSingleLine || editRange . startLineNumber !== position . lineNumber ) {
583- console . warn ( 'INVALID text edit, must be single line and on the same line' ) ;
584- continue ;
585- }
586-
587- // insert the text of the edit and create a dedicated
588- // suggestion-container with overwrite[Before|After]
589- suggestion . insertText = item . textEdit . text ;
590- suggestion . overwriteBefore = position . column - editRange . startColumn ;
591- suggestion . overwriteAfter = editRange . endColumn - position . column ;
592- } else {
593- suggestion . overwriteBefore = position . column - wordStartPos . column ;
594- suggestion . overwriteAfter = 0 ;
632+ const suggestion = SuggestAdapter . from ( item , position , wordStartPos ) ;
633+ if ( suggestion ) {
634+ result . suggestions . push ( suggestion ) ;
595635 }
596-
597- result . suggestions . push ( suggestion ) ;
598636 }
599637
600638 return result ;
@@ -612,7 +650,12 @@ class SuggestAdapter {
612650 }
613651
614652 return toThenable ( this . _provider . resolveCompletionItem ( item , token ) ) . then ( resolvedItem => {
615- return SuggestAdapter . from ( resolvedItem ) ;
653+ let wordStartPos = position ;
654+ const word = model . getWordUntilPosition ( position ) ;
655+ if ( word ) {
656+ wordStartPos = new Position ( wordStartPos . lineNumber , word . startColumn ) ;
657+ }
658+ return SuggestAdapter . from ( resolvedItem , position , wordStartPos ) ;
616659 } ) ;
617660 }
618661}
0 commit comments