Avoid main-thread hops for explicit socket scope

This commit is contained in:
Lawrence Chen 2026-02-20 23:58:47 -08:00
parent 68cf29cd2d
commit 167431b786
3 changed files with 66 additions and 0 deletions

View file

@ -49,6 +49,7 @@ final class PortScanner: @unchecked Sendable {
func registerTTY(workspaceId: UUID, panelId: UUID, ttyName: String) {
queue.async { [self] in
let key = PanelKey(workspaceId: workspaceId, panelId: panelId)
guard ttyNames[key] != ttyName else { return }
ttyNames[key] = ttyName
}
}

View file

@ -103,6 +103,20 @@ class TerminalController {
return currentSorted != nextSorted
}
nonisolated static func explicitSocketScope(
options: [String: String]
) -> (workspaceId: UUID, panelId: UUID)? {
guard let tabRaw = options["tab"]?.trimmingCharacters(in: .whitespacesAndNewlines),
!tabRaw.isEmpty,
let panelRaw = (options["panel"] ?? options["surface"])?.trimmingCharacters(in: .whitespacesAndNewlines),
!panelRaw.isEmpty,
let workspaceId = UUID(uuidString: tabRaw),
let panelId = UUID(uuidString: panelRaw) else {
return nil
}
return (workspaceId, panelId)
}
/// Update which window's TabManager receives socket commands.
/// This is used when the user switches between multiple terminal windows.
func setActiveTabManager(_ tabManager: TabManager?) {
@ -10830,6 +10844,17 @@ class TerminalController {
return "ERROR: Missing tty name — usage: report_tty <tty_name> [--tab=X] [--panel=Y]"
}
// Shell integration always provides explicit UUID handles.
// Handle that common path off-main to avoid sync-hopping on every report.
if let scope = Self.explicitSocketScope(options: parsed.options) {
PortScanner.shared.registerTTY(
workspaceId: scope.workspaceId,
panelId: scope.panelId,
ttyName: ttyName
)
return "OK"
}
var result = "OK"
DispatchQueue.main.sync {
guard let tab = resolveTabForReport(args) else {
@ -10872,6 +10897,14 @@ class TerminalController {
private func portsKick(_ args: String) -> String {
let parsed = parseOptions(args)
// Shell integration always provides explicit UUID handles.
// Handle that common path off-main to keep prompt hooks from blocking UI work.
if let scope = Self.explicitSocketScope(options: parsed.options) {
PortScanner.shared.kick(workspaceId: scope.workspaceId, panelId: scope.panelId)
return "OK"
}
var result = "OK"
DispatchQueue.main.sync {
guard let tab = resolveTabForReport(args) else {

View file

@ -3082,4 +3082,36 @@ final class TerminalControllerSidebarDedupeTests: XCTestCase {
)
)
}
func testExplicitSocketScopeParsesValidUUIDTabAndPanel() {
let workspaceId = UUID()
let panelId = UUID()
let scope = TerminalController.explicitSocketScope(
options: [
"tab": workspaceId.uuidString,
"panel": panelId.uuidString
]
)
XCTAssertEqual(scope?.workspaceId, workspaceId)
XCTAssertEqual(scope?.panelId, panelId)
}
func testExplicitSocketScopeAcceptsSurfaceAlias() {
let workspaceId = UUID()
let panelId = UUID()
let scope = TerminalController.explicitSocketScope(
options: [
"tab": workspaceId.uuidString,
"surface": panelId.uuidString
]
)
XCTAssertEqual(scope?.workspaceId, workspaceId)
XCTAssertEqual(scope?.panelId, panelId)
}
func testExplicitSocketScopeRejectsMissingOrInvalidValues() {
XCTAssertNil(TerminalController.explicitSocketScope(options: [:]))
XCTAssertNil(TerminalController.explicitSocketScope(options: ["tab": "workspace:1", "panel": UUID().uuidString]))
XCTAssertNil(TerminalController.explicitSocketScope(options: ["tab": UUID().uuidString, "panel": "surface:1"]))
}
}