@@ -110,34 +110,52 @@ const compareBySortText = (a: CompletionItem, b: CompletionItem) => {
110
110
}
111
111
} ;
112
112
113
- // compute from
114
- const itemFrom = ( item : CompletionItem , contextPos : number ) => {
115
- // compute from
116
- return item . textEdit
117
- ? InsertReplaceEdit . is ( item . textEdit )
118
- ? contextPos - ( item . textEdit . insert . end . character - item . textEdit . insert . start . character )
119
- : TextEdit . is ( item . textEdit )
120
- ? contextPos - ( item . textEdit . range . end . character - item . textEdit . range . start . character )
121
- : contextPos
122
- : contextPos ;
113
+ /**
114
+ * returns the offset into the document of the beginning of a completion item.
115
+ */
116
+ const itemFrom = ( item : CompletionItem , cvContext : CodeViewCompletionContext , contextPos : number ) => {
117
+ if ( item . textEdit !== undefined ) {
118
+ if ( InsertReplaceEdit . is ( item . textEdit ) ) {
119
+ const endLine = cvContext . code [ item . textEdit . insert . end . line ] ;
120
+ // we have to "snap" (Math.min) the end of the insertion to the end of the line
121
+ // because completions give positions past the end of the line.
122
+ // e.g. typing `lib` gives completion `library` with start.character = 0, end.character = 7
123
+ // but we only expect it to replace characters 0 thru 3.
124
+ const end = Math . min ( item . textEdit . insert . end . character , endLine . length ) ;
125
+
126
+ const replaceLength = end - item . textEdit . insert . start . character ;
127
+ return contextPos - replaceLength ;
128
+ }
129
+ if ( TextEdit . is ( item . textEdit ) ) {
130
+ const endLine = cvContext . code [ item . textEdit . range . end . line ] ;
131
+ // see comment above for an explanation of why we use Math.min here.
132
+ const end = Math . min ( item . textEdit . range . end . character , endLine . length ) ;
133
+
134
+ const replaceLength = end - item . textEdit . range . start . character ;
135
+
136
+ return contextPos - replaceLength ;
137
+ }
138
+ }
139
+ return contextPos ;
123
140
} ;
124
141
125
142
/**
126
143
* replaceText for a given CompletionItem is the text that is already in the document
127
144
* that that CompletionItem will replace.
128
145
*
146
+ *
129
147
* Example 1: if you are typing `lib` and get the completion `library`, then this function
130
148
* will give `lib`.
131
149
* Example 2: if you are typing `os.a` and get the completion `abc`, then this function
132
150
* will give `a`.
133
151
*/
134
- const getReplaceText = ( context : CompletionContext , item : CompletionItem ) =>
135
- context . state . sliceDoc ( itemFrom ( item , context . pos ) , context . pos ) ;
152
+ const getReplaceText = ( context : CompletionContext , cvContext : CodeViewCompletionContext , item : CompletionItem ) =>
153
+ context . state . sliceDoc ( itemFrom ( item , cvContext , context . pos ) , context . pos ) ;
136
154
137
- const makeCompletionItemApplier = ( item : CompletionItem , context : CompletionContext ) =>
155
+ const makeCompletionItemApplier = ( item : CompletionItem , cvContext : CodeViewCompletionContext , context : CompletionContext ) =>
138
156
( view : EditorView , completion : Completion ) => {
139
157
// compute from
140
- const from = itemFrom ( item , context . pos ) ;
158
+ const from = itemFrom ( item , cvContext , context . pos ) ;
141
159
142
160
// handle snippets
143
161
const insertText = item . textEdit ?. newText ?? ( item . insertText || item . label ) ;
@@ -156,11 +174,11 @@ const makeCompletionItemApplier = (item: CompletionItem, context: CompletionCont
156
174
}
157
175
} ;
158
176
159
- const sortTextItemsBoostScore = ( context : CompletionContext , items : CompletionItem [ ] , index : number ) => {
177
+ const sortTextItemsBoostScore = ( context : CompletionContext , cvContext : CodeViewCompletionContext , items : CompletionItem [ ] , index : number ) => {
160
178
const total = items . length ;
161
179
const item = items [ index ] ;
162
180
// compute replaceText
163
- const replaceText = getReplaceText ( context , item ) ;
181
+ const replaceText = getReplaceText ( context , cvContext , item ) ;
164
182
165
183
// if the replaceText doesn't start with "." then bury items that do
166
184
if ( ! replaceText . startsWith ( "." ) && item . label . startsWith ( "." ) ) {
@@ -177,14 +195,14 @@ const sortTextItemsBoostScore = (context: CompletionContext, items: CompletionIt
177
195
}
178
196
} ;
179
197
180
- const defaultBoostScore = ( context : CompletionContext , items : CompletionItem [ ] , index : number ) => {
198
+ const defaultBoostScore = ( context : CompletionContext , cvContext : CodeViewCompletionContext , items : CompletionItem [ ] , index : number ) => {
181
199
const item = items [ index ] ;
182
200
183
- const replaceText = getReplaceText ( context , item ) ;
201
+ const replaceText = getReplaceText ( context , cvContext , item ) ;
184
202
185
203
// if you haven't typed into the completions yet (for example after a `.`) then
186
- // score items starting with non-alphabetic characters -1 , everything else 0.
187
- if ( replaceText . length === 0 ) return isLetter ( item . label [ 0 ] ) ? 0 : - 1 ;
204
+ // score items starting with non-alphabetic characters -100 , everything else 0.
205
+ if ( replaceText . length === 0 ) return isLetter ( item . label [ 0 ] ) ? 0 : - 100 ;
188
206
189
207
// We filter items by replaceText inclusion before scoring,
190
208
// so i is garaunteed to be an index into `item.label`...
@@ -233,7 +251,7 @@ async function getCompletions(
233
251
if ( item . textEdit === undefined && token ) return false ;
234
252
235
253
// require at least inclusion
236
- const replaceText = getReplaceText ( context , item ) . toLowerCase ( ) ;
254
+ const replaceText = getReplaceText ( context , cvContext , item ) . toLowerCase ( ) ;
237
255
return item . label . toLowerCase ( ) . includes ( replaceText ) ||
238
256
item . insertText ?. toLowerCase ( ) . includes ( replaceText ) ;
239
257
} ) ;
@@ -249,8 +267,8 @@ async function getCompletions(
249
267
detail : ! item . documentation ? item . detail : undefined ,
250
268
type : vsKindToType ( item . kind ) ,
251
269
info : ( ) => infoNodeForItem ( item ) ,
252
- apply : makeCompletionItemApplier ( item , context ) ,
253
- boost : boostScore ( context , filteredItems , index )
270
+ apply : makeCompletionItemApplier ( item , cvContext , context ) ,
271
+ boost : boostScore ( context , cvContext , filteredItems , index )
254
272
} ;
255
273
} ) ;
256
274
0 commit comments