@@ -12,10 +12,15 @@ import LanguageServerProtocol
1212
1313class AutoCompleteCoordinator : TextViewCoordinator {
1414 private weak var textViewController : TextViewController ?
15+ private unowned var file : CEWorkspaceFile
1516 private var localEventMonitor : Any ?
1617
1718 private var itemBoxController : ItemBoxWindowController ?
1819
20+ init ( _ file: CEWorkspaceFile ) {
21+ self . file = file
22+ }
23+
1924 func prepareCoordinator( controller: TextViewController ) {
2025 itemBoxController = ItemBoxWindowController ( )
2126 itemBoxController? . delegate = self
@@ -25,45 +30,65 @@ class AutoCompleteCoordinator: TextViewCoordinator {
2530 localEventMonitor = NSEvent . addLocalMonitorForEvents ( matching: . keyDown) { event in
2631 // `ctrl + space` keyboard shortcut listener for the item box to show
2732 if event. modifierFlags. contains ( . control) && event. charactersIgnoringModifiers == " " {
28- self . showAutocompleteWindow ( )
33+ Task {
34+ await self . showAutocompleteWindow ( )
35+ }
2936 return nil
3037 }
3138 return event
3239 }
3340 }
3441
42+ @MainActor
3543 func showAutocompleteWindow( ) {
3644 guard let cursorPos = textViewController? . cursorPositions. first,
3745 let textView = textViewController? . textView,
3846 let window = NSApplication . shared. keyWindow,
39- let itemBoxController = itemBoxController,
40- !itemBoxController. isVisible
47+ let itemBoxController = itemBoxController
4148 else {
4249 return
4350 }
4451
52+ Task {
53+ let textPosition = Position ( line: cursorPos. line - 1 , character: cursorPos. column - 1 )
54+ let completionItems = await fetchCompletions ( position: textPosition)
55+ itemBoxController. items = completionItems
56+
57+ let cursorRect = textView. firstRect ( forCharacterRange: cursorPos. range, actualRange: nil )
58+ itemBoxController. constrainWindowToScreenEdges ( cursorRect: cursorRect)
59+ itemBoxController. showWindow ( attachedTo: window)
60+ }
61+ }
62+
63+ private func fetchCompletions( position: Position ) async -> [ CompletionItem ] {
64+ let workspace = await file. fileDocument? . findWorkspace ( )
65+ guard let workspacePath = workspace? . fileURL? . absoluteURL. path ( ) else { return [ ] }
66+ guard let language = await file. fileDocument? . getLanguage ( ) . lspLanguage else { return [ ] }
67+
4568 @Service var lspService : LSPService
69+ guard let client = await lspService. languageClient (
70+ for: language, workspacePath: workspacePath
71+ ) else {
72+ return [ ]
73+ }
4674
47- // lspService.
48-
49- itemBoxController. items = [
50- CompletionItem ( label: " CETable " , kind: . class) ,
51- CompletionItem ( label: " CETask " , kind: . enum) ,
52- CompletionItem ( label: " CETarget " , kind: . function) ,
53- CompletionItem ( label: " CEItem " , kind: . color) ,
54- CompletionItem ( label: " tableView " , kind: . constant) ,
55- CompletionItem ( label: " itemBoxController " , kind: . constructor) ,
56- CompletionItem ( label: " showAutocompleteWindow " , kind: . enumMember) ,
57- CompletionItem ( label: " NSApplication " , kind: . field) ,
58- CompletionItem ( label: " CECell " , kind: . file) ,
59- CompletionItem ( label: " Item10 " , kind: . folder) ,
60- CompletionItem ( label: " Item11 " , kind: . snippet) ,
61- CompletionItem ( label: " Item12 " , kind: . reference) ,
62- ]
63-
64- let cursorRect = textView. firstRect ( forCharacterRange: cursorPos. range, actualRange: nil )
65- itemBoxController. constrainWindowToScreenEdges ( cursorRect: cursorRect)
66- itemBoxController. showWindow ( attachedTo: window)
75+ do {
76+ let completions = try await client. requestCompletion (
77+ for: file. url. absoluteURL. path ( ) , position: position
78+ )
79+
80+ // Extract the completion items list
81+ switch completions {
82+ case . optionA( let completionItems) :
83+ return completionItems
84+ case . optionB( let completionList) :
85+ return completionList. items
86+ case . none:
87+ return [ ]
88+ }
89+ } catch {
90+ return [ ]
91+ }
6792 }
6893
6994 deinit {
@@ -75,34 +100,41 @@ class AutoCompleteCoordinator: TextViewCoordinator {
75100 }
76101}
77102
78- extension MarkupContent {
79- public init ( kind: MarkupKind , value: String ) {
80- do {
81- let dictionary : [ String : Any ] = [ " kind " : kind. rawValue, " value " : value]
82- let data = try JSONSerialization . data ( withJSONObject: dictionary)
83- self = try JSONDecoder ( ) . decode ( MarkupContent . self, from: data)
84- } catch {
85- print ( " Failed to create MarkupContent: \( error) " )
86- // swiftlint:disable:next force_try
87- self = try ! JSONDecoder ( ) . decode ( MarkupContent . self, from: """
88- { " kind " : " plaintext " , " value " : " " }
89- """ . data ( using: . utf8) !)
90- }
91- }
92- }
93-
94103extension AutoCompleteCoordinator : ItemBoxDelegate {
95104 func applyCompletionItem( _ item: CompletionItem ) {
96- guard let cursorPos = textViewController? . cursorPositions. first else {
105+ guard let cursorPos = textViewController? . cursorPositions. first,
106+ let textView = textViewController? . textView else {
97107 return
98108 }
99109
100110 do {
101- let token = try textViewController? . treeSitterClient? . nodesAt ( range: cursorPos. range)
102- guard let token = token? . first else {
103- return
111+ let textPosition = Position (
112+ line: cursorPos. line - 1 ,
113+ character: cursorPos. column - 1
114+ )
115+ var textEdits = LSPCompletionItemsUtil . getCompletionItemEdits (
116+ startPosition: textPosition,
117+ item: item
118+ )
119+ // Appropriately order the text edits
120+ textEdits = TextEdit . makeApplicable ( textEdits)
121+
122+ // Make the updates
123+ textView. undoManager? . beginUndoGrouping ( )
124+ for textEdit in textEdits {
125+ textView. replaceString (
126+ in: NSRange ( location: 0 , length: 0 ) ,
127+ with: textEdit. newText
128+ )
104129 }
105- print ( " Token \( token) " )
130+ textView. undoManager? . endUndoGrouping ( )
131+
132+ // textViewController?.textView.applyMutations(<#T##mutations: [TextMutation]##[TextMutation]#>)
133+ // let token = try textViewController?.treeSitterClient?.nodesAt(range: cursorPos.range)
134+ // guard let token = token?.first else {
135+ // return
136+ // }
137+ // print("Token \(token)")
106138 } catch {
107139 print ( " \( error) " )
108140 return
0 commit comments