@@ -21,6 +21,8 @@ class AutoCompleteCoordinator {
2121 private var currentFilterText : String = " "
2222 /// Stores the unfiltered completion items
2323 private var completionItems : [ AutoCompleteItem ] = [ ]
24+ /// Set to true when the server sends an incomplete list, indicating that we should not filter client-side.
25+ private var receivedIncompleteCompletionItems : Bool = false
2426
2527 init ( _ file: CEWorkspaceFile ) {
2628 self . file = file
@@ -51,9 +53,10 @@ class AutoCompleteCoordinator {
5153 // Extract the completion items list
5254 switch completions {
5355 case . optionA( let completionItems) :
54- return completionItems. map { AutoCompleteItem ( $0) }
56+ return completionItems. map { AutoCompleteItem ( $0) } . sorted ( )
5557 case . optionB( let completionList) :
56- return completionList. items. map { AutoCompleteItem ( $0) }
58+ receivedIncompleteCompletionItems = receivedIncompleteCompletionItems || completionList. isIncomplete
59+ return completionList. items. map { AutoCompleteItem ( $0) } . sorted ( )
5760 case . none:
5861 return [ ]
5962 }
@@ -64,24 +67,15 @@ class AutoCompleteCoordinator {
6467
6568 /// Filters completion items based on the current partial token input
6669 private func filterCompletionItems( _ items: [ AutoCompleteItem ] ) -> [ AutoCompleteItem ] {
67- guard !currentFilterText. isEmpty else {
68- return items
70+ guard !currentFilterText. isEmpty, !receivedIncompleteCompletionItems else {
71+ return items. sorted ( )
6972 }
7073
71- let items = items. filter { item in
72- let insertText = LSPCompletionItemsUtil . getInsertText ( from: item)
73- let label = item. label. lowercased ( )
74- let filterText = currentFilterText. lowercased ( )
75- if insertText. lowercased ( ) . hasPrefix ( filterText) {
76- return true
77- }
78- if label. hasPrefix ( filterText) {
79- return true
80- }
81- return false
82- }
74+ let items = items
75+ . map { ( $0. fuzzyMatch ( query: currentFilterText) , $0) }
76+ . compactMap { $0. 0 . weight > 0 ? $0. 1 : nil }
8377
84- return items
78+ return items. sorted ( )
8579 }
8680}
8781
@@ -91,22 +85,44 @@ extension AutoCompleteCoordinator: CodeSuggestionDelegate {
9185 textView: TextViewController ,
9286 cursorPosition: CursorPosition
9387 ) async -> ( windowPosition: CursorPosition , items: [ CodeSuggestionEntry ] ) ? {
94- let tokenSubstringCount = findTreeSitterNodeAtPosition ( textView: textView, cursorPosition: cursorPosition)
9588 currentFilterText = " "
89+ let tokenSubstringCount = findTreeSitterNodeAtPosition ( textView: textView, cursorPosition: cursorPosition)
90+
91+ let textPosition = Position ( line: cursorPosition. start. line - 1 , character: cursorPosition. start. column - 1 )
9692
97- var textPosition = Position ( line: cursorPosition. start. line - 1 , character: cursorPosition. start. column - 1 )
98- var cursorPosition = cursorPosition
9993 // If we are asking for completions in the middle of a token, then
10094 // query the language server for completion items at the start of the token
101- if currentNode != nil {
102- textPosition = Position (
95+ // but *only* if we haven't received an incomplete response.
96+ let queryPosition = if currentNode != nil && !receivedIncompleteCompletionItems {
97+ Position (
10398 line: cursorPosition. start. line - 1 ,
10499 character: cursorPosition. start. column - tokenSubstringCount - 1
105100 )
106- cursorPosition = CursorPosition ( line: textPosition. line + 1 , column: textPosition. character + 1 )
101+ } else {
102+ textPosition
107103 }
108- completionItems = await fetchCompletions ( position: textPosition)
109- return ( cursorPosition, completionItems)
104+ completionItems = await fetchCompletions ( position: queryPosition)
105+
106+ if receivedIncompleteCompletionItems && queryPosition != textPosition {
107+ // We need to re-request this. We've requested the wrong location and since know that the server
108+ // returns incomplete items (meaning we can't filter them ourselves).
109+ return await completionSuggestionsRequested ( textView: textView, cursorPosition: cursorPosition)
110+ }
111+
112+ // If we can detect that we're in a node, we still want to adjust the panel to be in the correct position
113+ let cursorPosition : CursorPosition = if currentNode != nil {
114+ CursorPosition (
115+ line: cursorPosition. start. line,
116+ column: cursorPosition. start. column - tokenSubstringCount
117+ )
118+ } else {
119+ CursorPosition (
120+ line: queryPosition. line + 1 ,
121+ column: queryPosition. character + 1
122+ )
123+ }
124+
125+ return ( cursorPosition, filterCompletionItems ( completionItems) )
110126 }
111127
112128 func findTreeSitterNodeAtPosition( textView: TextViewController , cursorPosition: CursorPosition ) -> Int {
@@ -140,7 +156,7 @@ extension AutoCompleteCoordinator: CodeSuggestionDelegate {
140156 textView: TextViewController ,
141157 cursorPosition: CursorPosition
142158 ) -> [ CodeSuggestionEntry ] ? {
143- guard var currentNode = currentNode, !completionItems. isEmpty else {
159+ guard var currentNode = currentNode, !completionItems. isEmpty, !receivedIncompleteCompletionItems else {
144160 return nil
145161 }
146162 _ = findTreeSitterNodeAtPosition ( textView: textView, cursorPosition: cursorPosition)
0 commit comments