@@ -77,9 +77,81 @@ export function tsCompletionEntryToLspCompletionItem(
77
77
// Text that actually gets inserted to the document. It could be different
78
78
// from 'entry.name'. For example, a method name could be 'greet', but the
79
79
// insertText is 'greet()'.
80
- const insertText = entry . insertText || entry . name ;
81
- item . textEdit = entry . replacementSpan ?
82
- lsp . TextEdit . replace ( tsTextSpanToLspRange ( scriptInfo , entry . replacementSpan ) , insertText ) :
80
+ let insertText = entry . insertText || entry . name ;
81
+ let replaceSpan = entry . replacementSpan ;
82
+ if ( kind === CompletionKind . attribute ) {
83
+ // <div [(ng|)]=></div>
84
+ // ^-------------- start
85
+ // ^------- end
86
+ let start = scriptInfo . lineOffsetToPosition ( position . line + 1 , position . character ) - 1 ;
87
+ let end = scriptInfo . lineOffsetToPosition ( position . line + 1 , position . character ) + 4 ;
88
+ if ( replaceSpan ) {
89
+ start = replaceSpan . start - 2 ;
90
+ end = replaceSpan . start + replaceSpan . length + 3 ;
91
+ }
92
+ const attributeText = scriptInfo . getSnapshot ( ) . getText ( start , end ) ;
93
+ switch ( attributeText [ 1 ] ) {
94
+ case '*' :
95
+ case '-' :
96
+ // *ngI| => *ngIf="|"
97
+ // on-cli| => on-click="|"
98
+ if ( attributeText [ attributeText . length - 3 ] !== '=' ) {
99
+ item . insertTextFormat = lsp . InsertTextFormat . Snippet ;
100
+ insertText = `${ insertText } ="\${0}"` ;
101
+ replaceSpan = { start : start + 2 , length : end - start - 5 } ;
102
+ }
103
+ break ;
104
+ case '[' :
105
+ // [ngMo|] => [ngModel]="|"
106
+ if ( attributeText [ attributeText . length - 2 ] !== '=' ) {
107
+ item . insertTextFormat = lsp . InsertTextFormat . Snippet ;
108
+ insertText = `${ insertText } ]="\${0}"` ;
109
+ replaceSpan = { start : start + 2 , length : end - start - 4 } ;
110
+ }
111
+ break ;
112
+ case '(' :
113
+ if ( attributeText [ 0 ] === '[' ) {
114
+ // [(ngMod|)] => [(ngModel)]="|"
115
+ if ( attributeText [ attributeText . length - 1 ] !== '=' ) {
116
+ item . insertTextFormat = lsp . InsertTextFormat . Snippet ;
117
+ insertText = `${ insertText } )]="\${0}"` ;
118
+ replaceSpan = { start : start + 2 , length : end - start - 3 } ;
119
+ }
120
+ } else {
121
+ // (clic|) => (click)="|"
122
+ if ( attributeText [ attributeText . length - 2 ] !== '=' ) {
123
+ item . insertTextFormat = lsp . InsertTextFormat . Snippet ;
124
+ insertText = `${ insertText } )="\${0}"` ;
125
+ replaceSpan = { start : start + 2 , length : end - start - 4 } ;
126
+ }
127
+ }
128
+ break ;
129
+ default :
130
+ break ;
131
+ }
132
+ }
133
+
134
+ if ( kind === CompletionKind . method ) {
135
+ let start = scriptInfo . lineOffsetToPosition ( position . line + 1 , position . character ) + 1 ;
136
+ let end = scriptInfo . lineOffsetToPosition ( position . line + 1 , position . character ) + 2 ;
137
+ if ( replaceSpan ) {
138
+ start = replaceSpan . start ;
139
+ end = replaceSpan . start + replaceSpan . length + 1 ;
140
+ }
141
+ const methodText = scriptInfo . getSnapshot ( ) . getText ( start , end ) ;
142
+ if ( methodText . endsWith ( '(' ) ) {
143
+ // meth|() => method(|)
144
+ replaceSpan = { start : start , length : end - start } ;
145
+ insertText = insertText . slice ( 0 , insertText . length - 1 ) + '\${0}' ;
146
+ } else {
147
+ // meth| => method(|)
148
+ insertText = insertText . slice ( 0 , insertText . length - 1 ) + '\${0})' ;
149
+ }
150
+ item . insertTextFormat = lsp . InsertTextFormat . Snippet ;
151
+ }
152
+
153
+ item . textEdit = replaceSpan ?
154
+ lsp . TextEdit . replace ( tsTextSpanToLspRange ( scriptInfo , replaceSpan ) , insertText ) :
83
155
lsp . TextEdit . insert ( position , insertText ) ;
84
156
return item ;
85
157
}
0 commit comments