@@ -13,12 +13,15 @@ import {
13
13
createConnection , IConnection , TextDocumentSyncKind ,
14
14
TextDocument , Diagnostic , DiagnosticSeverity ,
15
15
InitializeParams , InitializeResult , TextDocumentPositionParams ,
16
- CompletionItem , CompletionItemKind , TextDocumentIdentifier , Range
16
+ CompletionItem , CompletionItemKind , Definition , TextDocumentIdentifier ,
17
+ Position , Range , TextEdit
17
18
} from 'vscode-languageserver' ;
18
19
19
20
import { TextDocuments , TextDocumentEvent } from './documents' ;
20
21
import { ErrorCollector } from './errors' ;
21
22
23
+ import { Completion , Span } from '@angular/language-service' ;
24
+
22
25
// Create a connection for the server. The connection uses Node's IPC as a transport
23
26
let connection : IConnection = createConnection ( ) ;
24
27
@@ -80,6 +83,45 @@ function compiletionKindToCompletionItemKind(kind: string): number {
80
83
return CompletionItemKind . Text ;
81
84
}
82
85
86
+ const wordRe = / ( \w | \( | \) | \[ | \] | \* | \- | \_ | \. ) + / g;
87
+ const special = / \( | \) | \[ | \] | \* | \- | \_ | \. / ;
88
+
89
+ // Convert attribute names with non-\w chracters into a text edit.
90
+ function insertionToEdit ( range : Range , insertText : string ) : TextEdit {
91
+ if ( insertText . match ( special ) && range ) {
92
+ return TextEdit . replace ( range , insertText ) ;
93
+ }
94
+ }
95
+
96
+
97
+ function getReplaceRange ( document : TextDocumentIdentifier , offset : number ) : Range {
98
+ const line = documents . getDocumentLine ( document , offset ) ;
99
+ if ( line && line . text && line . start <= offset && line . start + line . text . length >= offset ) {
100
+ const lineOffset = offset - line . start - 1 ;
101
+
102
+ // Find the word that contains the offset
103
+ let found : number , len : number ;
104
+ line . text . replace ( wordRe , < any > ( ( word : string , _ : string , wordOffset : number ) => {
105
+ if ( wordOffset <= lineOffset && wordOffset + word . length >= lineOffset && word . match ( special ) ) {
106
+ found = wordOffset ;
107
+ len = word . length ;
108
+ }
109
+ } ) ) ;
110
+ if ( found != null ) {
111
+ return Range . create ( Position . create ( line . line - 1 , found ) , Position . create ( line . line - 1 , found + len ) ) ;
112
+ }
113
+ }
114
+ }
115
+
116
+ function insertTextOf ( completion : Completion ) : string {
117
+ switch ( completion . kind ) {
118
+ case 'attribute' :
119
+ case 'html attribute' :
120
+ return `${ completion . name } ="{{}}"`
121
+ }
122
+ return completion . name ;
123
+ }
124
+
83
125
// This handler provides the initial list of the completion items.
84
126
connection . onCompletion ( ( textDocumentPosition : TextDocumentPositionParams ) : CompletionItem [ ] => {
85
127
const { fileName, service, offset, languageId} = documents . getServiceInfo ( textDocumentPosition . textDocument ,
@@ -91,16 +133,18 @@ connection.onCompletion((textDocumentPosition: TextDocumentPositionParams): Comp
91
133
result = result . filter ( completion => completion . kind != 'element' ) ;
92
134
}
93
135
if ( result ) {
136
+ const replaceRange = getReplaceRange ( textDocumentPosition . textDocument , offset ) ;
94
137
return result . map ( completion => ( {
95
138
label : completion . name ,
96
139
kind : compiletionKindToCompletionItemKind ( completion . kind ) ,
97
140
detail : completion . kind ,
98
- sortText : completion . sort
141
+ sortText : completion . sort ,
142
+ textEdit : insertionToEdit ( replaceRange , insertTextOf ( completion ) ) ,
143
+ insertText : insertTextOf ( completion )
99
144
} ) ) ;
100
145
}
101
146
}
102
147
} ) ;
103
148
104
-
105
149
// Listen on the connection
106
150
connection . listen ( ) ;
0 commit comments