@@ -110,34 +110,52 @@ const compareBySortText = (a: CompletionItem, b: CompletionItem) => {
110110 }
111111} ;
112112
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 ;
123140} ;
124141
125142/**
126143 * replaceText for a given CompletionItem is the text that is already in the document
127144 * that that CompletionItem will replace.
128145 *
146+ *
129147 * Example 1: if you are typing `lib` and get the completion `library`, then this function
130148 * will give `lib`.
131149 * Example 2: if you are typing `os.a` and get the completion `abc`, then this function
132150 * will give `a`.
133151 */
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 ) ;
136154
137- const makeCompletionItemApplier = ( item : CompletionItem , context : CompletionContext ) =>
155+ const makeCompletionItemApplier = ( item : CompletionItem , cvContext : CodeViewCompletionContext , context : CompletionContext ) =>
138156 ( view : EditorView , completion : Completion ) => {
139157 // compute from
140- const from = itemFrom ( item , context . pos ) ;
158+ const from = itemFrom ( item , cvContext , context . pos ) ;
141159
142160 // handle snippets
143161 const insertText = item . textEdit ?. newText ?? ( item . insertText || item . label ) ;
@@ -156,11 +174,11 @@ const makeCompletionItemApplier = (item: CompletionItem, context: CompletionCont
156174 }
157175 } ;
158176
159- const sortTextItemsBoostScore = ( context : CompletionContext , items : CompletionItem [ ] , index : number ) => {
177+ const sortTextItemsBoostScore = ( context : CompletionContext , cvContext : CodeViewCompletionContext , items : CompletionItem [ ] , index : number ) => {
160178 const total = items . length ;
161179 const item = items [ index ] ;
162180 // compute replaceText
163- const replaceText = getReplaceText ( context , item ) ;
181+ const replaceText = getReplaceText ( context , cvContext , item ) ;
164182
165183 // if the replaceText doesn't start with "." then bury items that do
166184 if ( ! replaceText . startsWith ( "." ) && item . label . startsWith ( "." ) ) {
@@ -177,14 +195,14 @@ const sortTextItemsBoostScore = (context: CompletionContext, items: CompletionIt
177195 }
178196} ;
179197
180- const defaultBoostScore = ( context : CompletionContext , items : CompletionItem [ ] , index : number ) => {
198+ const defaultBoostScore = ( context : CompletionContext , cvContext : CodeViewCompletionContext , items : CompletionItem [ ] , index : number ) => {
181199 const item = items [ index ] ;
182200
183- const replaceText = getReplaceText ( context , item ) ;
201+ const replaceText = getReplaceText ( context , cvContext , item ) ;
184202
185203 // 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 ;
188206
189207 // We filter items by replaceText inclusion before scoring,
190208 // so i is garaunteed to be an index into `item.label`...
@@ -236,7 +254,7 @@ async function getCompletions(
236254 if ( item . textEdit === undefined && token ) return false ;
237255
238256 // require at least inclusion
239- const replaceText = getReplaceText ( context , item ) . toLowerCase ( ) ;
257+ const replaceText = getReplaceText ( context , cvContext , item ) . toLowerCase ( ) ;
240258 return item . label . toLowerCase ( ) . includes ( replaceText ) ||
241259 item . insertText ?. toLowerCase ( ) . includes ( replaceText ) ;
242260 } ) ;
@@ -252,8 +270,8 @@ async function getCompletions(
252270 detail : ! item . documentation ? item . detail : undefined ,
253271 type : vsKindToType ( item . kind ) ,
254272 info : ( ) => infoNodeForItem ( item ) ,
255- apply : makeCompletionItemApplier ( item , context ) ,
256- boost : boostScore ( context , filteredItems , index )
273+ apply : makeCompletionItemApplier ( item , cvContext , context ) ,
274+ boost : boostScore ( context , cvContext , filteredItems , index )
257275 } ;
258276 } ) ;
259277
0 commit comments