diff --git a/src/Surface.zig b/src/Surface.zig index cfc0b14aa..989495309 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1342,6 +1342,8 @@ fn searchCallback_( self: *Surface, event: terminal.search.Thread.Event, ) !void { + // NOTE: This runs on the search thread. + switch (event) { .viewport_matches => |matches_unowned| { var arena: ArenaAllocator = .init(self.alloc); @@ -1361,6 +1363,18 @@ fn searchCallback_( try self.renderer_thread.wakeup.notify(); }, + // When we quit, tell our renderer to reset any search state. + .quit => { + _ = self.renderer_thread.mailbox.push( + .{ .search_viewport_matches = .{ + .arena = .init(self.alloc), + .matches = &.{}, + } }, + .forever, + ); + try self.renderer_thread.wakeup.notify(); + }, + // Unhandled, so far. .total_matches, .complete, diff --git a/src/terminal/search/Thread.zig b/src/terminal/search/Thread.zig index 7ca9df0b7..2c5607809 100644 --- a/src/terminal/search/Thread.zig +++ b/src/terminal/search/Thread.zig @@ -163,7 +163,14 @@ fn threadMain_(self: *Thread) !void { // Run log.debug("starting search thread", .{}); - defer log.debug("starting search thread shutdown", .{}); + defer { + log.debug("starting search thread shutdown", .{}); + + // Send the quit message + if (self.opts.event_cb) |cb| { + cb(.quit, self.opts.event_userdata); + } + } // Unlike some of our other threads, we interleave search work // with our xev loop so that we can try to make forward search progress @@ -247,6 +254,18 @@ fn changeNeedle(self: *Thread, needle: []const u8) !void { if (self.search) |*s| { s.deinit(); self.search = null; + + // When the search changes then we need to emit that it stopped. + if (self.opts.event_cb) |cb| { + cb( + .{ .total_matches = 0 }, + self.opts.event_userdata, + ); + cb( + .{ .viewport_matches = &.{} }, + self.opts.event_userdata, + ); + } } // No needle means stop the search. @@ -381,6 +400,9 @@ pub const Message = union(enum) { /// Events that can be emitted from the search thread. The caller /// chooses to handle these as they see fit. pub const Event = union(enum) { + /// Search is quitting. The search thread is exiting. + quit, + /// Search is complete for the given needle on all screens. complete, @@ -668,6 +690,7 @@ test { fn callback(event: Event, userdata: ?*anyopaque) void { const ud: *Self = @ptrCast(@alignCast(userdata.?)); switch (event) { + .quit => {}, .complete => ud.reset.set(), .total_matches => |v| ud.total = v, .viewport_matches => |v| { diff --git a/src/terminal/search/viewport.zig b/src/terminal/search/viewport.zig index 6a266f47a..55eedb724 100644 --- a/src/terminal/search/viewport.zig +++ b/src/terminal/search/viewport.zig @@ -326,15 +326,16 @@ test "clear screen and search dirty tracking" { try testing.expect(try search.update(&t.screens.active.pages)); { - const sel = search.next().?; + const h = search.next().?; + const sel = h.untracked(); try testing.expectEqual(point.Point{ .active = .{ .x = 0, .y = 1, - } }, t.screens.active.pages.pointFromPin(.active, sel.start()).?); + } }, t.screens.active.pages.pointFromPin(.active, sel.start).?); try testing.expectEqual(point.Point{ .active = .{ .x = 3, .y = 1, - } }, t.screens.active.pages.pointFromPin(.active, sel.end()).?); + } }, t.screens.active.pages.pointFromPin(.active, sel.end).?); } try testing.expect(search.next() == null); }