Skip to content

Commit a432688

Browse files
Update cursor positioning
1 parent a2de090 commit a432688

File tree

2 files changed

+57
-28
lines changed

2 files changed

+57
-28
lines changed

CodeEdit/Features/Editor/AutoCompleteCoordinator.swift

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ import CodeEditSourceEditor
1111
import LanguageServerProtocol
1212

1313
class AutoCompleteCoordinator: TextViewCoordinator {
14+
/// A reference to the `TextViewController`, to be able to make edits
1415
private weak var textViewController: TextViewController?
16+
/// A reference to the file we are working with, to be able to query file information
1517
private unowned var file: CEWorkspaceFile
18+
/// The event monitor that looks for the keyboard shortcut to bring up the autocomplete menu
1619
private var localEventMonitor: Any?
17-
20+
/// The `ItemBoxWindowController` lets us display the autocomplete items
1821
private var itemBoxController: ItemBoxWindowController?
1922

2023
init(_ file: CEWorkspaceFile) {
@@ -39,6 +42,7 @@ class AutoCompleteCoordinator: TextViewCoordinator {
3942
}
4043
}
4144

45+
/// Will query the language server for autocomplete suggestions and then display the window.
4246
@MainActor
4347
func showAutocompleteWindow() {
4448
guard let cursorPos = textViewController?.cursorPositions.first,
@@ -67,14 +71,16 @@ class AutoCompleteCoordinator: TextViewCoordinator {
6771

6872
@Service var lspService: LSPService
6973
guard let client = await lspService.languageClient(
70-
for: language, workspacePath: workspacePath
74+
for: language,
75+
workspacePath: workspacePath
7176
) else {
7277
return []
7378
}
7479

7580
do {
7681
let completions = try await client.requestCompletion(
77-
for: file.url.absoluteURL.path(), position: position
82+
for: file.url.absoluteURL.path(),
83+
position: position
7884
)
7985

8086
// Extract the completion items list
@@ -101,43 +107,50 @@ class AutoCompleteCoordinator: TextViewCoordinator {
101107
}
102108

103109
extension AutoCompleteCoordinator: ItemBoxDelegate {
110+
/// Takes a `CompletionItem` and modifies the text view with the new string
104111
func applyCompletionItem(_ item: CompletionItem) {
105112
guard let cursorPos = textViewController?.cursorPositions.first,
106113
let textView = textViewController?.textView else {
107114
return
108115
}
109116

110-
do {
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
117+
let textPosition = Position(
118+
line: cursorPos.line - 1,
119+
character: cursorPos.column - 1
120+
)
121+
var textEdits = LSPCompletionItemsUtil.getCompletionItemEdits(
122+
startPosition: textPosition,
123+
item: item
124+
)
125+
// Appropriately order the text edits
126+
textEdits = TextEdit.makeApplicable(textEdits)
127+
128+
// Make the updates
129+
textView.undoManager?.beginUndoGrouping()
130+
for textEdit in textEdits {
131+
textView.replaceString(
132+
in: cursorPos.range,
133+
with: textEdit.newText
118134
)
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-
)
129-
}
130-
textView.undoManager?.endUndoGrouping()
135+
}
136+
textView.undoManager?.endUndoGrouping()
131137

132-
// textViewController?.textView.applyMutations(<#T##mutations: [TextMutation]##[TextMutation]#>)
138+
// Set the cursor to the end of the completion
139+
let insertText = LSPCompletionItemsUtil.getInsertText(from: item)
140+
guard let newCursorPos = cursorPos.range.shifted(by: insertText.count) else {
141+
return
142+
}
143+
textViewController?.setCursorPositions([CursorPosition(range: newCursorPos)])
144+
145+
// do {
133146
// let token = try textViewController?.treeSitterClient?.nodesAt(range: cursorPos.range)
134147
// guard let token = token?.first else {
135148
// return
136149
// }
137150
// print("Token \(token)")
138-
} catch {
139-
print("\(error)")
140-
return
141-
}
151+
// } catch {
152+
// print("\(error)")
153+
// return
154+
// }
142155
}
143156
}

CodeEdit/Features/LSP/LSPUtil.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ enum LSPCompletionItemsUtil {
3838
return edits
3939
}
4040

41+
static func getInsertText(from completionItem: CompletionItem) -> String {
42+
// According to LSP spec, textEdit takes precedence if present, then insertText, then label
43+
if let textEdit = completionItem.textEdit {
44+
switch textEdit {
45+
case .optionA(let edit):
46+
return edit.newText
47+
case .optionB(let insertReplaceEdit):
48+
return insertReplaceEdit.newText
49+
}
50+
}
51+
if let insertText = completionItem.insertText {
52+
return insertText
53+
}
54+
return completionItem.label
55+
}
56+
4157
private static func editOrReplaceItem(edit: TwoTypeOption<TextEdit, InsertReplaceEdit>, _ edits: inout [TextEdit]) {
4258
switch edit {
4359
case .optionA(let textEdit):

0 commit comments

Comments
 (0)