From 1e82d0829092863255ce40b78f0adde75bdc7aeb Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Fri, 9 May 2025 10:16:00 -0500 Subject: [PATCH 1/3] Open New Files in Workspace --- .../Models/CEWorkspaceFileManager.swift | 8 ++--- .../CodeEditDocumentController.swift | 26 +++++++++++++- .../Extensions/URL/URL+componentCompare.swift | 35 +++++++++++++++++++ .../Utils/UnitTests_Extensions.swift | 34 ++++++++++++++++++ 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager.swift b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager.swift index 078d85d5b..2e0f824d1 100644 --- a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager.swift +++ b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager.swift @@ -103,12 +103,12 @@ final class CEWorkspaceFileManager { return nil } - // Drill down towards the file, indexing any directories needed. - // If file is not in the `workspaceSettingsFolderURL` or subdirectories, exit. - guard url.absoluteString.starts(with: folderUrl.absoluteString), - url.pathComponents.count > folderUrl.pathComponents.count else { + // If file is not in the `folderUrl` or subdirectories, exit. + guard folderUrl.containsSubPath(url) else { return nil } + + // Drill down towards the file, indexing any directories needed. let pathComponents = url.pathComponents.dropFirst(folderUrl.pathComponents.count) var currentURL = folderUrl diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift index 034d3de75..fb4e64119 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift @@ -59,8 +59,11 @@ final class CodeEditDocumentController: NSDocumentController { display displayDocument: Bool, completionHandler: @escaping (NSDocument?, Bool, Error?) -> Void ) { - super.openDocument(withContentsOf: url, display: displayDocument) { document, documentWasAlreadyOpen, error in + guard !openFileInExistingWorkspace(url: url) else { + return + } + super.openDocument(withContentsOf: url, display: displayDocument) { document, documentWasAlreadyOpen, error in if let document { self.addDocument(document) } else { @@ -72,6 +75,27 @@ final class CodeEditDocumentController: NSDocumentController { completionHandler(document, documentWasAlreadyOpen, error) } } + + /// Attempt to open the file URL in an open workspace, finding the nearest workspace to open it in if possible. + /// - Parameter url: The file URL to open. + /// - Returns: True, if the document was opened in a workspace. + private func openFileInExistingWorkspace(url: URL) -> Bool { + guard url.isFileURL else { return false } + let workspaces = documents.compactMap({ $0 as? WorkspaceDocument }) + + // Check open workspaces for the file being opened. Sorted by shared components with the url so we + // open the nearest workspace possible. + for workspace in workspaces.sorted(by: { + ($0.fileURL?.sharedComponents(url) ?? 0) > ($1.fileURL?.sharedComponents(url) ?? 0) + }) { + // createIfNotFound will still return `nil` if the files don't share a common ancestor. + if let newFile = workspace.workspaceFileManager?.getFile(url.absolutePath, createIfNotFound: true) { + workspace.editorManager?.openTab(item: newFile) + return true + } + } + return false + } override func removeDocument(_ document: NSDocument) { super.removeDocument(document) diff --git a/CodeEdit/Utils/Extensions/URL/URL+componentCompare.swift b/CodeEdit/Utils/Extensions/URL/URL+componentCompare.swift index 3d411e914..c0d352098 100644 --- a/CodeEdit/Utils/Extensions/URL/URL+componentCompare.swift +++ b/CodeEdit/Utils/Extensions/URL/URL+componentCompare.swift @@ -15,4 +15,39 @@ extension URL { func componentCompare(_ other: URL) -> Bool { return self.pathComponents == other.pathComponents } + + /// Determines if another URL is lower in the file system than this URL. + /// + /// Examples: + /// ``` + /// URL(filePath: "/Users/Bob/Desktop").containsSubPath(URL(filePath: "/Users/Bob/Desktop/file.txt")) // true + /// URL(filePath: "/Users/Bob/Desktop").containsSubPath(URL(filePath: "/Users/Bob/Desktop/")) // false + /// URL(filePath: "/Users/Bob/Desktop").containsSubPath(URL(filePath: "/Users/Bob/")) // false + /// URL(filePath: "/Users/Bob/Desktop").containsSubPath(URL(filePath: "/Users/Bob/Desktop/Folder")) // true + /// ``` + /// + /// - Parameter other: The URL to compare. + /// - Returns: True, if the other URL is lower in the file system. + func containsSubPath(_ other: URL) -> Bool { + other.absoluteString.starts(with: absoluteString) + && other.pathComponents.count > pathComponents.count + } + + /// Compares this url with another, counting the number of shared path components. Stops counting once a + /// different component is found. + /// + /// - Note: URL treats a leading `/` as a component, so `/Users` and `/` will return `1`. + /// - Parameter other: The URL to compare against. + /// - Returns: The number of shared components. + func sharedComponents(_ other: URL) -> Int { + var count = 0 + for (component, otherComponent) in zip(pathComponents, other.pathComponents) { + if component == otherComponent { + count += 1 + } else { + return count + } + } + return count + } } diff --git a/CodeEditTests/Utils/UnitTests_Extensions.swift b/CodeEditTests/Utils/UnitTests_Extensions.swift index de7a0fec3..9118f1a1f 100644 --- a/CodeEditTests/Utils/UnitTests_Extensions.swift +++ b/CodeEditTests/Utils/UnitTests_Extensions.swift @@ -196,4 +196,38 @@ final class CodeEditUtilsExtensionsUnitTests: XCTestCase { let path = #"/Hello World/ With Spaces/ And " Characters "# XCTAssertEqual(path.escapedDirectory(), #""/Hello World/ With Spaces/ And \" Characters ""#) } + + // MARK: - URL + Contains + + func testURLContainsSubPath() { + XCTAssertTrue(URL(filePath: "/Users/Bob/Desktop").containsSubPath(URL(filePath: "/Users/Bob/Desktop/file.txt"))) + XCTAssertFalse(URL(filePath: "/Users/Bob/Desktop").containsSubPath(URL(filePath: "/Users/Bob/Desktop/"))) + XCTAssertFalse(URL(filePath: "/Users/Bob/Desktop").containsSubPath(URL(filePath: "/Users/Bob/"))) + XCTAssertTrue(URL(filePath: "/Users/Bob/Desktop").containsSubPath(URL(filePath: "/Users/Bob/Desktop/Folder"))) + } + + func testURLSharedComponentsCount() { + // URL Treats the leading `/` as a component, so these all appear to have + 1 but are correct. + XCTAssertEqual( + URL(filePath: "/Users/Bob/Desktop").sharedComponents(URL(filePath: "/Users/Bob/Desktop/file.txt")), + 4 + ) + XCTAssertEqual( + URL(filePath: "/Users/Bob/Desktop").sharedComponents(URL(filePath: "/Users/Bob/Desktop/")), + 4 + ) + XCTAssertEqual( + URL(filePath: "/Users/Bob/Desktop").sharedComponents(URL(filePath: "/Users/Bob/")), + 3 + ) + XCTAssertEqual( + URL(filePath: "/Users/Bob/Desktop").sharedComponents(URL(filePath: "/Users/Bob/Desktop/Folder")), + 4 + ) + + XCTAssertEqual( + URL(filePath: "/Users/Bob/Desktop").sharedComponents(URL(filePath: "/Some Other/ Path ")), + 1 + ) + } } From 833fa499d0b46338407899c6443b469a94904452 Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Fri, 9 May 2025 10:19:54 -0500 Subject: [PATCH 2/3] Trailing Whitespace --- .../Documents/Controllers/CodeEditDocumentController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift index fb4e64119..c3ea842d3 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift @@ -75,7 +75,7 @@ final class CodeEditDocumentController: NSDocumentController { completionHandler(document, documentWasAlreadyOpen, error) } } - + /// Attempt to open the file URL in an open workspace, finding the nearest workspace to open it in if possible. /// - Parameter url: The file URL to open. /// - Returns: True, if the document was opened in a workspace. From 80ee2cc70f6e7ca5f64cd18861b3f0c02d03829f Mon Sep 17 00:00:00 2001 From: Khan Winter <35942988+thecoolwinter@users.noreply.github.com> Date: Fri, 9 May 2025 10:25:01 -0500 Subject: [PATCH 3/3] Show Window --- .../Documents/Controllers/CodeEditDocumentController.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift index c3ea842d3..446fcb5e6 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditDocumentController.swift @@ -80,7 +80,7 @@ final class CodeEditDocumentController: NSDocumentController { /// - Parameter url: The file URL to open. /// - Returns: True, if the document was opened in a workspace. private func openFileInExistingWorkspace(url: URL) -> Bool { - guard url.isFileURL else { return false } + guard !url.isFolder else { return false } let workspaces = documents.compactMap({ $0 as? WorkspaceDocument }) // Check open workspaces for the file being opened. Sorted by shared components with the url so we @@ -91,6 +91,7 @@ final class CodeEditDocumentController: NSDocumentController { // createIfNotFound will still return `nil` if the files don't share a common ancestor. if let newFile = workspace.workspaceFileManager?.getFile(url.absolutePath, createIfNotFound: true) { workspace.editorManager?.openTab(item: newFile) + workspace.showWindows() return true } }