Skip to content
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
527f205
test: cover terminal file line link routing
austinywang May 12, 2026
ca5a141
fix: open terminal file links in editor
austinywang May 12, 2026
bbf5d5a
Merge remote-tracking branch 'origin/main' into issue-1544-cmd-click-…
austinywang May 12, 2026
87f5af3
fix: address terminal link build failure
austinywang May 12, 2026
51da2b8
Merge remote-tracking branch 'origin/main' into issue-1544-cmd-click-…
austinywang May 12, 2026
8b3cf24
fix: preserve file URL line fragments
austinywang May 12, 2026
db76547
fix: avoid blocking terminal URL callbacks
austinywang May 12, 2026
b2d3c31
fix: resolve terminal file links off main
austinywang May 12, 2026
1ad6c0b
chore: clarify deferred markdown routing
austinywang May 12, 2026
9adc00e
Merge remote-tracking branch 'origin/main' into issue-1544-cmd-click-…
austinywang May 26, 2026
eb04850
merge: resolve conflicts with main
austinywang Jun 4, 2026
dfed0eb
test: avoid global cwd mutation in file link test
austinywang Jun 4, 2026
8301970
fix: preserve source workspace for file link routing
austinywang Jun 4, 2026
6a7e6ae
fix: resolve extensionless file line links
austinywang Jun 4, 2026
2ec3efc
fix: keep browser URLs out of file line resolver
austinywang Jun 4, 2026
935a7d2
fix: avoid host port file link false positives
austinywang Jun 4, 2026
bb5e389
test: tighten file link routing assertions
austinywang Jun 4, 2026
22509e3
fix: tighten terminal file open routing
austinywang Jun 4, 2026
a80502d
fix: preserve line jump editor routing
austinywang Jun 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 52 additions & 15 deletions Sources/CommandClickFileOpenRouter.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
import AppKit
import Foundation

enum CommandClickFileRouteKind: Sendable {
case markdown
case supportedFile
}

enum CommandClickFileOpenRouter {
nonisolated static func routeKind(path: String) -> CommandClickFileRouteKind? {
if CmdClickMarkdownRouteSettings.shouldRoute(path: path) {
return .markdown
}
if CmdClickSupportedFileRouteSettings.shouldRoute(path: path) {
return .supportedFile
}
return nil
}

nonisolated static func shouldRouteInCmux(path: String) -> Bool {
CmdClickMarkdownRouteSettings.shouldRoute(path: path)
|| CmdClickSupportedFileRouteSettings.shouldRoute(path: path)
routeKind(path: path) != nil
}

@MainActor
Expand All @@ -13,15 +27,34 @@ enum CommandClickFileOpenRouter {
sourcePanelId: UUID,
filePath: String
) -> Bool {
if CmdClickMarkdownRouteSettings.shouldRoute(path: filePath),
workspace.openOrFocusMarkdownSplit(from: sourcePanelId, filePath: filePath) != nil {
return true
}
guard let routeKind = routeKind(path: filePath) else { return false }
return openInCmux(
workspace: workspace,
sourcePanelId: sourcePanelId,
filePath: filePath,
routeKind: routeKind
)
}

guard CmdClickSupportedFileRouteSettings.shouldRoute(path: filePath) else {
return false
@MainActor
private static func openInCmux(
workspace: Workspace,
sourcePanelId: UUID,
filePath: String,
routeKind: CommandClickFileRouteKind
) -> Bool {
switch routeKind {
case .markdown:
return workspace.openOrFocusMarkdownSplit(
from: sourcePanelId,
filePath: filePath
) != nil
Comment thread
austinywang marked this conversation as resolved.
Resolved
case .supportedFile:
return workspace.openOrFocusFilePreviewSplit(
from: sourcePanelId,
filePath: filePath
) != nil
}
return workspace.openOrFocusFilePreviewSplit(from: sourcePanelId, filePath: filePath) != nil
}

/// Resolve the working directory for a terminal surface, preferring the
Expand Down Expand Up @@ -53,9 +86,9 @@ enum CommandClickFileOpenRouter {
/// Ghostty's `Surface.openUrl` holds an internal `os_unfair_lock` when it
/// dispatches into Swift; opening a new panel synchronously re-enters
/// Ghostty and deadlocks (#3370). This helper defers the split creation
/// via `DispatchQueue.main.async` and re-validates the workspace and path
/// at dispatch time (TOCTOU). When routing fails, `fallback` is called so
/// the caller can open the file externally.
/// via a main-actor task and re-validates the route off-main before the UI
/// mutation. When routing fails, `fallback` is called so the caller can
/// open the file externally.
@MainActor
static func deferredOpenFileInCmux(
workspace: Workspace,
Expand All @@ -64,7 +97,10 @@ enum CommandClickFileOpenRouter {
filePath: String,
fallback: (@MainActor @Sendable () -> Void)? = nil
) {
DispatchQueue.main.async {
Task { @MainActor [workspace, preferredWorkspaceId, surfaceId, filePath, fallback] in
let routeKind = await Task.detached(priority: .userInitiated) {
Self.routeKind(path: filePath)
}.value
let resolvedWorkspace = AppDelegate.shared?.workspaceContainingPanel(
panelId: surfaceId,
preferredWorkspaceId: preferredWorkspaceId
Expand All @@ -73,14 +109,15 @@ enum CommandClickFileOpenRouter {
fallback?()
return
}
guard shouldRouteInCmux(path: filePath) else {
guard let routeKind else {
fallback?()
return
}
if openInCmux(
workspace: resolvedWorkspace,
sourcePanelId: surfaceId,
filePath: filePath
filePath: filePath,
routeKind: routeKind
) {
return
}
Expand Down
Loading
Loading