Skip to content

Commit a2de090

Browse files
Autocomplete updates
1 parent 6f80528 commit a2de090

File tree

3 files changed

+78
-64
lines changed

3 files changed

+78
-64
lines changed

CodeEdit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 1 addition & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CodeEdit/Features/Editor/AutoCompleteCoordinator.swift

Lines changed: 76 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@ import LanguageServerProtocol
1212

1313
class 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-
94103
extension 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

CodeEdit/Features/Editor/Models/EditorInstance.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class EditorInstance: Hashable {
3939
self.file = file
4040
self.cursorSubject.send(cursorPositions)
4141
self.rangeTranslator = RangeTranslator(cursorSubject: cursorSubject)
42-
self.autoCompleteCoordinator = AutoCompleteCoordinator()
42+
self.autoCompleteCoordinator = AutoCompleteCoordinator(file)
4343
}
4444

4545
func hash(into hasher: inout Hasher) {

0 commit comments

Comments
 (0)