From 0870490a756a2de3bd85f8e807c736e505fd9a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=A4fe?= Date: Wed, 4 Mar 2026 12:20:14 +0100 Subject: [PATCH] Replace cachedScreenContents/cachedVisibleContents with computed properties Derive full screen text and visible viewport text from the single cachedScreenTextInfo cache, removing two separate CachedValue instances and their ghostty_surface_read_text fetch closures. This eliminates code duplication and ensures all text queries go through one cache. Co-Authored-By: Claude Opus 4.6 --- .../GetTerminalDetailsIntent.swift | 4 +- .../Surface View/SurfaceView_AppKit.swift | 57 ++++--------------- 2 files changed, 12 insertions(+), 49 deletions(-) diff --git a/macos/Sources/Features/App Intents/GetTerminalDetailsIntent.swift b/macos/Sources/Features/App Intents/GetTerminalDetailsIntent.swift index 99d6e39ba..f7c51a4c2 100644 --- a/macos/Sources/Features/App Intents/GetTerminalDetailsIntent.swift +++ b/macos/Sources/Features/App Intents/GetTerminalDetailsIntent.swift @@ -37,13 +37,13 @@ struct GetTerminalDetailsIntent: AppIntent { case .workingDirectory: return .result(value: terminal.workingDirectory) case .allContents: guard let view = terminal.surfaceView else { throw GhosttyIntentError.surfaceNotFound } - return .result(value: view.cachedScreenContents.get()) + return .result(value: view.screenContents) case .selectedText: guard let view = terminal.surfaceView else { throw GhosttyIntentError.surfaceNotFound } return .result(value: view.accessibilitySelectedText()) case .visibleText: guard let view = terminal.surfaceView else { throw GhosttyIntentError.surfaceNotFound } - return .result(value: view.cachedVisibleContents.get()) + return .result(value: view.visibleContents) } } } diff --git a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift index 5c35387b9..25c4832ed 100644 --- a/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/Surface View/SurfaceView_AppKit.swift @@ -240,8 +240,15 @@ extension Ghostty { private var titleFromTerminal: String? // The cached contents of the screen. - private(set) var cachedScreenContents: CachedValue - private(set) var cachedVisibleContents: CachedValue + /// Full screen text, derived from the cached accessibility context. + var screenContents: String { cachedScreenTextInfo.get().text } + + /// Visible viewport text, extracted from the cached accessibility context. + var visibleContents: String { + let info = cachedScreenTextInfo.get() + guard let range = Range(info.viewportRange, in: info.text) else { return "" } + return String(info.text[range]) + } /// Wraps the opaque Zig accessibility context pointer so ARC /// automatically frees it when the owning ScreenTextInfo is @@ -312,8 +319,6 @@ extension Ghostty { // We need to initialize this so it does something but we want to set // it back up later so we can reference `self`. This is a hack we should // fix at some point. - self.cachedScreenContents = .init(duration: .milliseconds(500)) { "" } - self.cachedVisibleContents = self.cachedScreenContents self.cachedScreenTextInfo = .init(duration: .milliseconds(500)) { ScreenTextInfo.empty } @@ -323,49 +328,7 @@ extension Ghostty { // can do SOMETHING. super.init(frame: NSRect(x: 0, y: 0, width: 800, height: 600)) - // Our cache of screen data - cachedScreenContents = .init(duration: .milliseconds(500)) { [weak self] in - guard let self else { return "" } - guard let surface = self.surface else { return "" } - var text = ghostty_text_s() - let sel = ghostty_selection_s( - top_left: ghostty_point_s( - tag: GHOSTTY_POINT_SCREEN, - coord: GHOSTTY_POINT_COORD_TOP_LEFT, - x: 0, - y: 0), - bottom_right: ghostty_point_s( - tag: GHOSTTY_POINT_SCREEN, - coord: GHOSTTY_POINT_COORD_BOTTOM_RIGHT, - x: 0, - y: 0), - rectangle: false) - guard ghostty_surface_read_text(surface, sel, &text) else { return "" } - defer { ghostty_surface_free_text(surface, &text) } - return String(cString: text.text) - } - cachedVisibleContents = .init(duration: .milliseconds(500)) { [weak self] in - guard let self else { return "" } - guard let surface = self.surface else { return "" } - var text = ghostty_text_s() - let sel = ghostty_selection_s( - top_left: ghostty_point_s( - tag: GHOSTTY_POINT_VIEWPORT, - coord: GHOSTTY_POINT_COORD_TOP_LEFT, - x: 0, - y: 0), - bottom_right: ghostty_point_s( - tag: GHOSTTY_POINT_VIEWPORT, - coord: GHOSTTY_POINT_COORD_BOTTOM_RIGHT, - x: 0, - y: 0), - rectangle: false) - guard ghostty_surface_read_text(surface, sel, &text) else { return "" } - defer { ghostty_surface_free_text(surface, &text) } - return String(cString: text.text) - } - - // Cache for screen text with viewport range, used by accessibility. + // Cache for screen text with viewport range and pre-computed grid mappings. // Creates a Zig accessibility context that pre-computes all grid // mappings; subsequent bounds/offset/cursor queries use this context // for O(1) lookups instead of rebuilding the PinMap each time.