Skip to content

Commit 8efba40

Browse files
Adding LSP features
1 parent 546a437 commit 8efba40

36 files changed

+1319
-30
lines changed

CodeEdit.xcodeproj/project.pbxproj

Lines changed: 109 additions & 5 deletions
Large diffs are not rendered by default.

CodeEdit/Features/About/Views/AboutView.swift

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,16 @@ public struct AboutView: View {
4444
// if anyone knows of a better way to do this feel free to refactor
4545
.background(.regularMaterial.opacity(0))
4646
.background(EffectView(.popover, blendingMode: .behindWindow).ignoresSafeArea())
47-
.background {
48-
Button("") {
49-
dismiss()
50-
}
51-
.keyboardShortcut(.escape, modifiers: [])
52-
.hidden()
53-
}
47+
// .background {
48+
// Button("") {
49+
// dismiss()
50+
// }
51+
// .keyboardShortcut(.escape, modifiers: [])
52+
// .hidden()
53+
// }
54+
.onExitCommand(perform: {
55+
dismiss()
56+
})
5457
.task {
5558
if let window = NSApp.findWindow(.about) {
5659
window.styleMask = [.closable, .fullSizeContentView, .titled, .nonactivatingPanel]

CodeEdit/Features/Documents/WorkspaceDocument.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
// Created by Pavel Kasila on 17.03.22.
66
//
77

8-
import Foundation
98
import AppKit
109
import SwiftUI
1110
import Combine
11+
import Foundation
1212
import WindowManagement
13+
import LanguageServerProtocol
1314

1415
@objc(WorkspaceDocument)
1516
final class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
@@ -33,6 +34,7 @@ final class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
3334

3435
var utilityAreaModel = UtilityAreaViewModel()
3536
var searchState: SearchState?
37+
var lspManager: LanguageServerManager?
3638
var quickOpenViewModel: QuickOpenViewModel?
3739
var commandsPaletteState: CommandPaletteViewModel?
3840
var listenerModel: WorkspaceNotificationModel = .init()
@@ -115,6 +117,17 @@ final class WorkspaceDocument: NSDocument, ObservableObject, NSToolbarDelegate {
115117
workspaceURL: url,
116118
editorManager: editorManager
117119
)
120+
self.lspManager = LanguageServerManager()
121+
122+
// TODO: ACTIVATION EVENTS HERE
123+
Task {
124+
try await self.lspManager?.startServer(
125+
for: .python,
126+
projectURL: url,
127+
workspaceFolders: [WorkspaceFolder(uri: url.absoluteString, name: "workspace_folder_name")]
128+
)
129+
}
130+
118131
self.workspaceFileManager = .init(
119132
folderUrl: url,
120133
ignoredFilesAndFolders: Set(ignoredFilesAndDirectory),

CodeEdit/Features/Editor/Models/Editor.swift

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,21 @@ final class Editor: ObservableObject, Identifiable {
138138
/// - file: the file to open.
139139
/// - asTemporary: indicates whether the tab should be opened as a temporary tab or a permanent tab.
140140
func openTab(file: CEWorkspaceFile, asTemporary: Bool) {
141+
print("Attempting to open file: \(file.url)")
141142
let item = EditorInstance(file: file)
142143
// Item is already opened in a tab.
143144
guard !tabs.contains(item) || !asTemporary else {
144145
selectedTab = item
145146
history.prepend(item)
147+
print("File already opened!")
146148
return
147149
}
148150

149151
switch (temporaryTab, asTemporary) {
150152
case (.some(let tab), true):
153+
print("Temporary tab 1")
151154
if let index = tabs.firstIndex(of: tab) {
155+
print("Going inside 1")
152156
history.prepend(item)
153157
tabs.remove(tab)
154158
tabs.insert(item, at: index)
@@ -157,24 +161,21 @@ final class Editor: ObservableObject, Identifiable {
157161
}
158162

159163
case (.some(let tab), false) where tab == item:
164+
print("2")
160165
temporaryTab = nil
161166

162167
case (.none, true):
168+
print("3")
163169
openTab(file: item.file)
164170
temporaryTab = item
165171

166172
case (.none, false):
173+
print("4")
167174
openTab(file: item.file)
168175

169176
default:
170177
break
171178
}
172-
// TODO: IS THIS NEEDED? openFile ALREADY GETS CALLED INSIDE openTab
173-
// do {
174-
// try openFile(item: item)
175-
// } catch {
176-
// print(error)
177-
// }
178179
}
179180

180181
/// Opens a tab in the editor.
@@ -184,11 +185,16 @@ final class Editor: ObservableObject, Identifiable {
184185
/// - fromHistory: Indicates whether the tab has been opened from going back in history.
185186
func openTab(file: CEWorkspaceFile, at index: Int? = nil, fromHistory: Bool = false) {
186187
let item = Tab(file: file)
188+
print("Tabs before update: \(tabs)")
187189
if let index {
188190
tabs.insert(item, at: index)
189191
} else {
190192
tabs.append(item)
191193
}
194+
195+
print("Updated tabs: \(tabs)")
196+
print("New item: \(item.file.url)")
197+
192198
selectedTab = item
193199
if !fromHistory {
194200
history.removeFirst(historyOffset)
@@ -198,12 +204,14 @@ final class Editor: ObservableObject, Identifiable {
198204
do {
199205
try openFile(item: item)
200206
} catch {
207+
print("openFile error")
201208
print(error)
202209
}
203210
}
204211

205212
private func openFile(item: Tab) throws {
206213
guard item.file.fileDocument == nil else {
214+
print("File document is nil, returning!")
207215
return
208216
}
209217

@@ -216,6 +224,7 @@ final class Editor: ObservableObject, Identifiable {
216224
)
217225
item.file.fileDocument = codeFile
218226
CodeEditDocumentController.shared.addDocument(codeFile)
227+
print("Successfully opened file")
219228
}
220229

221230
func goBackInHistory() {

CodeEdit/Features/Editor/Models/EditorManager.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ class EditorManager: ObservableObject {
3030
/// History of last-used editors.
3131
var activeEditorHistory: Deque<() -> Editor?> = []
3232

33-
var fileDocuments: [CEWorkspaceFile: CodeFileDocument] = [:]
34-
3533
/// notify listeners whenever tab selection changes on the active editor.
3634
var tabBarTabIdSubject = PassthroughSubject<String?, Never>()
3735
var cancellable: AnyCancellable?
@@ -74,7 +72,7 @@ class EditorManager: ObservableObject {
7472
/// - editor: The editor to add the tab to. If nil, it is added to the active tab group.
7573
func openTab(item: CEWorkspaceFile, in editor: Editor? = nil) {
7674
let editor = editor ?? activeEditor
77-
editor.openTab(file: item)
75+
editor.openTab(file: item, asTemporary: false)
7876
}
7977

8078
/// bind active tap group to listen to file selection changes.

CodeEdit/Features/LSP/LSPCache.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class LSPCache {
7878
self.evictIfNeeded()
7979
}
8080
}
81-
81+
8282
private func evictIfNeeded() {
8383
evictionQueue.async(flags: .barrier) {
8484
while self.cacheEntriesOrder.count > self.cacheSizeLimit {

CodeEdit/Features/LSP/LSPUtil.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//
2+
// LSPUtil.swift
3+
// CodeEdit
4+
//
5+
// Created by Abe Malla on 2/10/24.
6+
//
7+
8+
import Foundation
9+
import LanguageServerProtocol
10+
11+
/// Helper function to get the edits from a completion item
12+
/// - Parameters:
13+
/// - startPosition: The position where the completion was requested
14+
/// - item: The completion item
15+
/// - Returns: An array of TextEdit objects
16+
func getCompletionItemEdits(startPosition: Position, item: CompletionItem) -> [TextEdit] {
17+
var edits: [TextEdit] = []
18+
19+
// If a TextEdit or InsertReplaceEdit value was provided
20+
if let edit = item.textEdit {
21+
switch edit {
22+
case .optionA(let textEdit):
23+
edits.append(textEdit)
24+
case .optionB(let insertReplaceEdit):
25+
edits.append(
26+
TextEdit(range: insertReplaceEdit.insert, newText: insertReplaceEdit.newText)
27+
)
28+
edits.append(
29+
TextEdit(range: insertReplaceEdit.replace, newText: insertReplaceEdit.newText)
30+
)
31+
}
32+
}
33+
// If the `insertText` value was provided
34+
else if let insertText = item.insertText {
35+
// TODO: COMPUTE EDIT BY FINDING MATCHING TEXT, ALSO CHECK FOR TRIGGER CHARACTERS
36+
edits.append(
37+
TextEdit(
38+
range: LSPRange(from: startPosition, offsetCharacter: insertText.count),
39+
newText: insertText
40+
)
41+
)
42+
}
43+
// Fallback to the label
44+
else if item.label != "" {
45+
// TODO: COMPUTE EDIT BY FINDING MATCHING TEXT, ALSO CHECK FOR TRIGGER CHARACTERS
46+
edits.append(
47+
TextEdit(
48+
range: LSPRange(from: startPosition, offsetCharacter: item.label.count),
49+
newText: item.label
50+
)
51+
)
52+
}
53+
54+
// If additional edits were provided
55+
// An example would be to also include an 'import' statement at the top of the file
56+
if let additionalEdits = item.additionalTextEdits {
57+
edits.append(contentsOf: additionalEdits)
58+
}
59+
60+
return edits
61+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// LanguageClient+CallHierarchy.swift
3+
// CodeEdit
4+
//
5+
// Created by Abe Malla on 2/7/24.
6+
//
7+
8+
import Foundation
9+
import LanguageServerProtocol
10+
11+
extension LanguageServer {
12+
func requestPrepareCallHierarchy(
13+
document documentURI: String, _ position: Position
14+
) async -> CallHierarchyPrepareResponse {
15+
// TODO: SOMEHOW NEED TO INVALIDATE THIS. CURRENTLY STORING EVERYTHING IN callHierarchyItems VARIABLE
16+
// let prepareParams = CallHierarchyPrepareParams(
17+
// textDocument: TextDocumentIdentifier(uri: documentURI),
18+
// position: position,
19+
// workDoneToken: nil
20+
// )
21+
//
22+
// do {
23+
// guard let items = try await server.prepareCallHierarchy(params: prepareParams) else {
24+
// return []
25+
// }
26+
// callHierarchyItems[documentURI] = items
27+
// return items
28+
// } catch {
29+
// // TODO: LOGGING
30+
// print("requestPrepareCallHierarchy: Error \(error)")
31+
// }
32+
33+
return []
34+
}
35+
36+
func requestCallHierarchyIncomingCalls(
37+
_ callHierarchyItem: CallHierarchyItem
38+
) async -> CallHierarchyIncomingCallsResponse {
39+
// let incomingParams = CallHierarchyIncomingCallsParams(
40+
// item: callHierarchyItem,
41+
// workDoneToken: nil
42+
// )
43+
//
44+
// do {
45+
// guard let incomingCalls = try await server.callHierarchyIncomingCalls(params: incomingParams) else {
46+
// return []
47+
// }
48+
// return incomingCalls
49+
// } catch {
50+
// // TODO: LOGGING
51+
// print("requestCallHierarchyIncomingCalls: Error \(error)")
52+
// }
53+
return []
54+
}
55+
56+
func requestCallHierarchyOutgoingCalls(
57+
_ callHierarchyItem: CallHierarchyItem
58+
) async -> CallHierarchyOutgoingCallsResponse {
59+
let outgoingParams = CallHierarchyOutgoingCallsParams(
60+
item: callHierarchyItem,
61+
workDoneToken: nil
62+
)
63+
64+
do {
65+
guard let outgoingCalls = try await lspInstance.callHierarchyOutgoingCalls(outgoingParams) else {
66+
return []
67+
}
68+
return outgoingCalls
69+
} catch {
70+
// TODO: LOGGING
71+
print("requestCallHierarchyOutgoingCalls: Error \(error)")
72+
}
73+
return []
74+
}
75+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// LanguageClient+CodeLens.swift
3+
// CodeEdit
4+
//
5+
// Created by Abe Malla on 2/7/24.
6+
//
7+
8+
import Foundation
9+
import LanguageServerProtocol
10+
11+
extension LanguageServer {
12+
// func requestCodeLens() async -> CodeLensResponse {
13+
//
14+
// }
15+
//
16+
// func requestCodeLensResolve() async -> CodeLensResolveResponse {
17+
//
18+
// }
19+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// LanguageClient+ColorPresentation.swift
3+
// CodeEdit
4+
//
5+
// Created by Abe Malla on 2/7/24.
6+
//
7+
8+
import Foundation
9+
import LanguageServerProtocol
10+
11+
extension LanguageServer {
12+
func requestColorPresentation(document documentURI: String, _ color: Color, _ range: LSPRange) async -> ColorPresentationResponse {
13+
let params = ColorPresentationParams(
14+
workDoneToken: nil,
15+
partialResultToken: nil,
16+
textDocument: TextDocumentIdentifier(uri: documentURI),
17+
color: color,
18+
range: range
19+
)
20+
do {
21+
return try await lspInstance.colorPresentation(params)
22+
} catch {
23+
// TODO: LOGGING
24+
print("requestColorPresentation: Error \(error)")
25+
}
26+
return []
27+
}
28+
}

0 commit comments

Comments
 (0)