diff --git a/macos/Sources/Ghostty/Ghostty.App.swift b/macos/Sources/Ghostty/Ghostty.App.swift index 7a0c738e9..91fab32fe 100644 --- a/macos/Sources/Ghostty/Ghostty.App.swift +++ b/macos/Sources/Ghostty/Ghostty.App.swift @@ -2164,7 +2164,7 @@ extension Ghostty { DispatchQueue.main.async { if let searchState = surfaceView.searchState { if let needle = startSearch.needle, !needle.isEmpty { - searchState.needle = needle + searchState.setNeedle(needle) } } else { surfaceView.searchState = Ghostty.SurfaceView.SearchState(from: startSearch) diff --git a/macos/Sources/Ghostty/Surface View/OSSurfaceView.swift b/macos/Sources/Ghostty/Surface View/OSSurfaceView.swift index bc822cbd9..2f559f2ae 100644 --- a/macos/Sources/Ghostty/Surface View/OSSurfaceView.swift +++ b/macos/Sources/Ghostty/Surface View/OSSurfaceView.swift @@ -122,12 +122,12 @@ extension Ghostty.OSSurfaceView { /// The `.find` pasteboard lets us sync our needle across the system and other find bars. private let pasteboard: OSPasteboard - @Published var needle: String = "" + @Published private(set) var needle: String = "" @Published var selected: UInt? @Published var total: UInt? /// The range of the needle's text selection in the find bar. - @Published var needleSelection: Range? + @Published private(set) var needleSelection: Range? init( from startSearch: Ghostty.Action.StartSearch, @@ -135,18 +135,49 @@ extension Ghostty.OSSurfaceView { ) { self.pasteboard = pasteboard if let needle = startSearch.needle, !needle.isEmpty { - self.needle = needle + setNeedle(needle) writePasteboardNeedle() } else { readPasteboardNeedle() } } + /// Replaces the search needle while keeping its selection valid. + func setNeedle(_ needle: String, selectAll: Bool = false) { + if needle != self.needle { + // String.Index values are only valid for the string that created + // them, so publish a nil selection before changing the string. + needleSelection = nil + self.needle = needle + } + + if selectAll { + needleSelection = self.needle.startIndex..?) { + guard let selection else { + needleSelection = nil + return + } + + guard + let lowerBound = String.Index(selection.lowerBound, within: needle), + let upperBound = String.Index(selection.upperBound, within: needle) + else { + needleSelection = nil + return + } + + needleSelection = lowerBound..