Skip to content

Commit

Permalink
Fix Subtle FileManager Bug, Fix Duplicate File Reads
Browse files Browse the repository at this point in the history
  • Loading branch information
thecoolwinter committed Sep 17, 2024
1 parent 3127468 commit f7e0253
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ extension CEWorkspaceFileManager {
}
}

var largestValue = 0
fileExtension = fileExtensions.sorted(by: { $0.value > $1.value }).first?.key ?? "txt"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,9 @@ final class CEWorkspaceFileManager {
/// - Parameter file: The parent element.
/// - Returns: A child element with an associated parent.
func createChild(_ url: URL, forParent file: CEWorkspaceFile) -> CEWorkspaceFile {
let childId = URL(filePath: file.id).appendingPathComponent(url.lastPathComponent).relativePath
let newFileItem = CEWorkspaceFile(id: childId, url: url)
let relativeURL = URL(filePath: file.id).appendingPathComponent(url.lastPathComponent)
let childId = relativeURL.relativePath
let newFileItem = CEWorkspaceFile(id: childId, url: relativeURL)
newFileItem.parent = file
return newFileItem
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,17 @@ final class CodeFileDocument: NSDocument, ObservableObject {
suffixBuffer: content?.string.getLastLines(5)
)
}

func findWorkspace() -> WorkspaceDocument? {
CodeEditDocumentController.shared.documents.first(where: { doc in
guard let workspace = doc as? WorkspaceDocument, let path = self.languageServerURI else { return false }
// createIfNotFound is safe here because it will still exit if the file and the workspace
// do not share a path prefix
return workspace
.workspaceFileManager?
.getFile(path, createIfNotFound: true)?
.fileDocument?
.isEqual(self) ?? false
}) as? WorkspaceDocument
}
}
1 change: 0 additions & 1 deletion CodeEdit/Features/Editor/Models/Editor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,6 @@ final class Editor: ObservableObject, Identifiable {
let contentType = item.file.resolvedURL.contentType
let codeFile = try CodeFileDocument(
for: item.file.url,
// TODO: FILE CONTENTS ARE READ MULTIPLE TIMES
withContentsOf: item.file.resolvedURL,
ofType: contentType?.identifier ?? ""
)
Expand Down
25 changes: 8 additions & 17 deletions CodeEdit/Features/Editor/Views/EditorAreaView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import SwiftUI
import CodeEditTextView

struct EditorAreaView: View {
@AppSettings(\.general.showEditorPathBar)
Expand All @@ -25,6 +26,12 @@ struct EditorAreaView: View {

@State var codeFile: CodeFileDocument?

init(editor: Editor, focus: FocusState<Editor?>.Binding) {
self.editor = editor
self._focus = focus
self.codeFile = editor.selectedTab?.file.fileDocument
}

var body: some View {
var shouldShowTabBar: Bool {
return navigationStyle == .openInTabs
Expand Down Expand Up @@ -54,22 +61,6 @@ struct EditorAreaView: View {
.opacity(dimEditorsWithoutFocus && editor != editorManager.activeEditor ? 0.5 : 1)
} else {
LoadingFileView(selected.file.name)
.task {
do {
let contentType = selected.file.resolvedURL.contentType
let newCodeFile = try CodeFileDocument(
for: selected.file.url,
withContentsOf: selected.file.resolvedURL,
ofType: contentType?.identifier ?? ""
)

selected.file.fileDocument = newCodeFile
CodeEditDocumentController.shared.addDocument(newCodeFile)
self.codeFile = newCodeFile
} catch {
print(error.localizedDescription)
}
}
}

} else {
Expand Down Expand Up @@ -108,7 +99,7 @@ struct EditorAreaView: View {
.background(EffectView(.headerView))
}
.focused($focus, equals: editor)
.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("CodeEditor.didBeginEditing"))) { _ in
.onReceive(NotificationCenter.default.publisher(for: TextView.textDidChangeNotification)) { _ in
if navigationStyle == .openInTabs {
editor.temporaryTab = nil
}
Expand Down
104 changes: 53 additions & 51 deletions CodeEdit/Features/LSP/Service/LSPService+Events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,62 +32,64 @@ extension LSPService {
}

private func handleEvent(_ event: ServerEvent, for key: ClientKey) {
switch event {
case let .request(id, request):
print("Request ID: \(id) for \(key.languageId.rawValue)")
handleRequest(request)
case let .notification(notification):
handleNotification(notification)
case let .error(error):
print("Error from EventStream for \(key.languageId.rawValue): \(error)")
}
// TODO: Handle Events
// switch event {
// case let .request(id, request):
// print("Request ID: \(id) for \(key.languageId.rawValue)")
// handleRequest(request)
// case let .notification(notification):
// handleNotification(notification)
// case let .error(error):
// print("Error from EventStream for \(key.languageId.rawValue): \(error)")
// }
}

// swiftlint:disable:next cyclomatic_complexity
private func handleRequest(_ request: ServerRequest) {
switch request {
case let .workspaceConfiguration(params, _):
print("workspaceConfiguration: \(params)")
case let .workspaceFolders(handler):
print("workspaceFolders: \(String(describing: handler))")
case let .workspaceApplyEdit(params, _):
print("workspaceApplyEdit: \(params)")
case let .clientRegisterCapability(params, _):
print("clientRegisterCapability: \(params)")
case let .clientUnregisterCapability(params, _):
print("clientUnregisterCapability: \(params)")
case let .workspaceCodeLensRefresh(handler):
print("workspaceCodeLensRefresh: \(String(describing: handler))")
case let .workspaceSemanticTokenRefresh(handler):
print("workspaceSemanticTokenRefresh: \(String(describing: handler))")
case let .windowShowMessageRequest(params, _):
print("windowShowMessageRequest: \(params)")
case let .windowShowDocument(params, _):
print("windowShowDocument: \(params)")
case let .windowWorkDoneProgressCreate(params, _):
print("windowWorkDoneProgressCreate: \(params)")

default:
print()
}
// TODO: Handle Requests
// switch request {
// case let .workspaceConfiguration(params, _):
// print("workspaceConfiguration: \(params)")
// case let .workspaceFolders(handler):
// print("workspaceFolders: \(String(describing: handler))")
// case let .workspaceApplyEdit(params, _):
// print("workspaceApplyEdit: \(params)")
// case let .clientRegisterCapability(params, _):
// print("clientRegisterCapability: \(params)")
// case let .clientUnregisterCapability(params, _):
// print("clientUnregisterCapability: \(params)")
// case let .workspaceCodeLensRefresh(handler):
// print("workspaceCodeLensRefresh: \(String(describing: handler))")
// case let .workspaceSemanticTokenRefresh(handler):
// print("workspaceSemanticTokenRefresh: \(String(describing: handler))")
// case let .windowShowMessageRequest(params, _):
// print("windowShowMessageRequest: \(params)")
// case let .windowShowDocument(params, _):
// print("windowShowDocument: \(params)")
// case let .windowWorkDoneProgressCreate(params, _):
// print("windowWorkDoneProgressCreate: \(params)")
//
// default:
// print()
// }
}

private func handleNotification(_ notification: ServerNotification) {
switch notification {
case let .windowLogMessage(params):
print("windowLogMessage \(params.type)\n```\n\(params.message)\n```\n")
case let .windowShowMessage(params):
print("windowShowMessage \(params.type)\n```\n\(params.message)\n```\n")
case let .textDocumentPublishDiagnostics(params):
print("textDocumentPublishDiagnostics: \(params)")
case let .telemetryEvent(params):
print("telemetryEvent: \(params)")
case let .protocolCancelRequest(params):
print("protocolCancelRequest: \(params)")
case let .protocolProgress(params):
print("protocolProgress: \(params)")
case let .protocolLogTrace(params):
print("protocolLogTrace: \(params)")
}
// TODO: Handle Notifications
// switch notification {
// case let .windowLogMessage(params):
// print("windowLogMessage \(params.type)\n```\n\(params.message)\n```\n")
// case let .windowShowMessage(params):
// print("windowShowMessage \(params.type)\n```\n\(params.message)\n```\n")
// case let .textDocumentPublishDiagnostics(params):
// print("textDocumentPublishDiagnostics: \(params)")
// case let .telemetryEvent(params):
// print("telemetryEvent: \(params)")
// case let .protocolCancelRequest(params):
// print("protocolCancelRequest: \(params)")
// case let .protocolProgress(params):
// print("protocolProgress: \(params)")
// case let .protocolLogTrace(params):
// print("protocolLogTrace: \(params)")
// }
}
}
19 changes: 5 additions & 14 deletions CodeEdit/Features/LSP/Service/LSPService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,6 @@ import LanguageClient
import LanguageServerProtocol
import CodeEditLanguages

extension CodeFileDocument {
func findWorkspace() -> WorkspaceDocument? {
CodeEditDocumentController.shared.documents.first(where: { doc in
guard let workspace = doc as? WorkspaceDocument, let path = self.languageServerURI else { return false }
return workspace.workspaceFileManager?.getFile(path)?.fileDocument?.isEqual(self) ?? false
}) as? WorkspaceDocument
}
}

/// `LSPService` is a service class responsible for managing the lifecycle and event handling
/// of Language Server Protocol (LSP) clients within the CodeEdit application. It handles the initialization,
/// communication, and termination of language servers, ensuring that code assistance features
Expand Down Expand Up @@ -184,12 +175,12 @@ final class LSPService: ObservableObject {
/// - Note: Must be invoked after the contents of the file are available.
/// - Parameter document: The code document that was opened.
func openDocument(_ document: CodeFileDocument) {
guard let workspace = document.findWorkspace(),
let workspacePath = workspace.fileURL?.absoluteURL.path(),
let lspLanguage = document.getLanguage().lspLanguage else {
return
}
Task.detached {
guard let workspace = await document.findWorkspace(),
let workspacePath = workspace.fileURL?.absoluteURL.path(),
let lspLanguage = await document.getLanguage().lspLanguage else {
return
}
let languageServer: LanguageServer
do {
if let server = await self.languageClients[ClientKey(lspLanguage, workspacePath)] {
Expand Down
7 changes: 3 additions & 4 deletions CodeEditTests/Features/LSP/LanguageServer+DocumentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,10 @@ final class LanguageServerDocumentTests: XCTestCase {
}

// Create a CodeFileDocument to test with, attach it to the workspace and file
let contentType = try file.url.resourceValues(forKeys: [.contentTypeKey]).contentType
let codeFile = try CodeFileDocument(
for: file.url,
withContentsOf: file.url,
ofType: contentType?.identifier ?? ""
ofType: "public.swift-source"
)
file.fileDocument = codeFile

Expand All @@ -113,7 +112,7 @@ final class LanguageServerDocumentTests: XCTestCase {
eventCountExpectation.fulfill()
}

await fulfillment(of: [eventCountExpectation], timeout: 5)
await fulfillment(of: [eventCountExpectation], timeout: 2)

// This should then trigger a documentDidClose event
codeFile.close()
Expand All @@ -125,7 +124,7 @@ final class LanguageServerDocumentTests: XCTestCase {
}
eventCloseExpectation.fulfill()
}
await fulfillment(of: [eventCloseExpectation], timeout: 5.0)
await fulfillment(of: [eventCloseExpectation], timeout: 2)

XCTAssertEqual(
connection.clientRequests.map { $0.method },
Expand Down

0 comments on commit f7e0253

Please sign in to comment.