`search` binding action starts a search thread on surface
parent
6623c20c2d
commit
e49f4a6dbc
|
|
@ -155,6 +155,9 @@ selection_scroll_active: bool = false,
|
|||
/// the wall clock time that has elapsed between timestamps.
|
||||
command_timer: ?std.time.Instant = null,
|
||||
|
||||
/// Search state
|
||||
search: ?Search = null,
|
||||
|
||||
/// The effect of an input event. This can be used by callers to take
|
||||
/// the appropriate action after an input event. For example, key
|
||||
/// input can be forwarded to the OS for further processing if it
|
||||
|
|
@ -174,6 +177,26 @@ pub const InputEffect = enum {
|
|||
closed,
|
||||
};
|
||||
|
||||
/// The search state for the surface.
|
||||
const Search = struct {
|
||||
state: terminal.search.Thread,
|
||||
thread: std.Thread,
|
||||
|
||||
pub fn deinit(self: *Search) void {
|
||||
// Notify the thread to stop
|
||||
self.state.stop.notify() catch |err| log.err(
|
||||
"error notifying search thread to stop, may stall err={}",
|
||||
.{err},
|
||||
);
|
||||
|
||||
// Wait for the OS thread to quit
|
||||
self.thread.join();
|
||||
|
||||
// Now it is safe to deinit the state
|
||||
self.state.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
/// Mouse state for the surface.
|
||||
const Mouse = struct {
|
||||
/// The last tracked mouse button state by button.
|
||||
|
|
@ -728,6 +751,9 @@ pub fn init(
|
|||
}
|
||||
|
||||
pub fn deinit(self: *Surface) void {
|
||||
// Stop search thread
|
||||
if (self.search) |*s| s.deinit();
|
||||
|
||||
// Stop rendering thread
|
||||
{
|
||||
self.renderer_thread.stop.notify() catch |err|
|
||||
|
|
@ -1301,6 +1327,12 @@ fn reportColorScheme(self: *Surface, force: bool) void {
|
|||
self.io.queueMessage(.{ .write_stable = output }, .unlocked);
|
||||
}
|
||||
|
||||
fn searchCallback(event: terminal.search.Thread.Event, ud: ?*anyopaque) void {
|
||||
const self: *Surface = @ptrCast(@alignCast(ud.?));
|
||||
_ = self;
|
||||
_ = event;
|
||||
}
|
||||
|
||||
/// Call this when modifiers change. This is safe to call even if modifiers
|
||||
/// match the previous state.
|
||||
///
|
||||
|
|
@ -4770,6 +4802,49 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||
self.renderer_state.terminal.fullReset();
|
||||
},
|
||||
|
||||
.search => |text| search: {
|
||||
const s: *Search = if (self.search) |*s| s else init: {
|
||||
// If we're stopping the search and we had no prior search,
|
||||
// then there is nothing to do.
|
||||
if (text.len == 0) break :search;
|
||||
|
||||
// We need to assign directly to self.search because we need
|
||||
// a stable pointer back to the thread state.
|
||||
self.search = .{
|
||||
.state = try .init(self.alloc, .{
|
||||
.mutex = self.renderer_state.mutex,
|
||||
.terminal = self.renderer_state.terminal,
|
||||
.event_cb = &searchCallback,
|
||||
.event_userdata = self,
|
||||
}),
|
||||
.thread = undefined,
|
||||
};
|
||||
const s: *Search = &self.search.?;
|
||||
errdefer s.state.deinit();
|
||||
|
||||
s.thread = try .spawn(
|
||||
.{},
|
||||
terminal.search.Thread.threadMain,
|
||||
.{&s.state},
|
||||
);
|
||||
s.thread.setName("search") catch {};
|
||||
|
||||
break :init s;
|
||||
};
|
||||
|
||||
// Zero-length text means stop searching.
|
||||
if (text.len == 0) {
|
||||
s.deinit();
|
||||
self.search = null;
|
||||
break :search;
|
||||
}
|
||||
|
||||
_ = s.state.mailbox.push(
|
||||
.{ .change_needle = text },
|
||||
.forever,
|
||||
);
|
||||
},
|
||||
|
||||
.copy_to_clipboard => |format| {
|
||||
// We can read from the renderer state without holding
|
||||
// the lock because only we will write to this field.
|
||||
|
|
|
|||
|
|
@ -332,6 +332,10 @@ pub const Action = union(enum) {
|
|||
/// to 14.5 points.
|
||||
set_font_size: f32,
|
||||
|
||||
/// Start a search for the given text. If the text is empty, then
|
||||
/// the search is canceled. If a previous search is active, it is replaced.
|
||||
search: []const u8,
|
||||
|
||||
/// Clear the screen and all scrollback.
|
||||
clear_screen,
|
||||
|
||||
|
|
@ -1152,6 +1156,7 @@ pub const Action = union(enum) {
|
|||
.esc,
|
||||
.text,
|
||||
.cursor_key,
|
||||
.search,
|
||||
.reset,
|
||||
.copy_to_clipboard,
|
||||
.copy_url_to_clipboard,
|
||||
|
|
|
|||
|
|
@ -604,6 +604,7 @@ fn actionCommands(action: Action.Key) []const Command {
|
|||
.csi,
|
||||
.esc,
|
||||
.cursor_key,
|
||||
.search,
|
||||
.set_font_size,
|
||||
.scroll_to_row,
|
||||
.scroll_page_fractional,
|
||||
|
|
|
|||
|
|
@ -591,6 +591,7 @@ const Search = struct {
|
|||
// Check our total match data
|
||||
const total = screen_search.matchesLen();
|
||||
if (total != self.last_total) {
|
||||
log.debug("notifying total matches={}", .{total});
|
||||
self.last_total = total;
|
||||
cb(.{ .total_matches = total }, ud);
|
||||
}
|
||||
|
|
@ -626,11 +627,13 @@ const Search = struct {
|
|||
};
|
||||
}
|
||||
|
||||
log.debug("notifying viewport matches len={}", .{results.items.len});
|
||||
cb(.{ .viewport_matches = results.items }, ud);
|
||||
}
|
||||
|
||||
// Send our complete notification if we just completed.
|
||||
if (!self.last_complete and self.isComplete()) {
|
||||
log.debug("notifying search complete", .{});
|
||||
self.last_complete = true;
|
||||
cb(.complete, ud);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue