`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.
|
/// the wall clock time that has elapsed between timestamps.
|
||||||
command_timer: ?std.time.Instant = null,
|
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 effect of an input event. This can be used by callers to take
|
||||||
/// the appropriate action after an input event. For example, key
|
/// the appropriate action after an input event. For example, key
|
||||||
/// input can be forwarded to the OS for further processing if it
|
/// input can be forwarded to the OS for further processing if it
|
||||||
|
|
@ -174,6 +177,26 @@ pub const InputEffect = enum {
|
||||||
closed,
|
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.
|
/// Mouse state for the surface.
|
||||||
const Mouse = struct {
|
const Mouse = struct {
|
||||||
/// The last tracked mouse button state by button.
|
/// The last tracked mouse button state by button.
|
||||||
|
|
@ -728,6 +751,9 @@ pub fn init(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Surface) void {
|
pub fn deinit(self: *Surface) void {
|
||||||
|
// Stop search thread
|
||||||
|
if (self.search) |*s| s.deinit();
|
||||||
|
|
||||||
// Stop rendering thread
|
// Stop rendering thread
|
||||||
{
|
{
|
||||||
self.renderer_thread.stop.notify() catch |err|
|
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);
|
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
|
/// Call this when modifiers change. This is safe to call even if modifiers
|
||||||
/// match the previous state.
|
/// match the previous state.
|
||||||
///
|
///
|
||||||
|
|
@ -4770,6 +4802,49 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
||||||
self.renderer_state.terminal.fullReset();
|
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| {
|
.copy_to_clipboard => |format| {
|
||||||
// We can read from the renderer state without holding
|
// We can read from the renderer state without holding
|
||||||
// the lock because only we will write to this field.
|
// the lock because only we will write to this field.
|
||||||
|
|
|
||||||
|
|
@ -332,6 +332,10 @@ pub const Action = union(enum) {
|
||||||
/// to 14.5 points.
|
/// to 14.5 points.
|
||||||
set_font_size: f32,
|
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 the screen and all scrollback.
|
||||||
clear_screen,
|
clear_screen,
|
||||||
|
|
||||||
|
|
@ -1152,6 +1156,7 @@ pub const Action = union(enum) {
|
||||||
.esc,
|
.esc,
|
||||||
.text,
|
.text,
|
||||||
.cursor_key,
|
.cursor_key,
|
||||||
|
.search,
|
||||||
.reset,
|
.reset,
|
||||||
.copy_to_clipboard,
|
.copy_to_clipboard,
|
||||||
.copy_url_to_clipboard,
|
.copy_url_to_clipboard,
|
||||||
|
|
|
||||||
|
|
@ -604,6 +604,7 @@ fn actionCommands(action: Action.Key) []const Command {
|
||||||
.csi,
|
.csi,
|
||||||
.esc,
|
.esc,
|
||||||
.cursor_key,
|
.cursor_key,
|
||||||
|
.search,
|
||||||
.set_font_size,
|
.set_font_size,
|
||||||
.scroll_to_row,
|
.scroll_to_row,
|
||||||
.scroll_page_fractional,
|
.scroll_page_fractional,
|
||||||
|
|
|
||||||
|
|
@ -591,6 +591,7 @@ const Search = struct {
|
||||||
// Check our total match data
|
// Check our total match data
|
||||||
const total = screen_search.matchesLen();
|
const total = screen_search.matchesLen();
|
||||||
if (total != self.last_total) {
|
if (total != self.last_total) {
|
||||||
|
log.debug("notifying total matches={}", .{total});
|
||||||
self.last_total = total;
|
self.last_total = total;
|
||||||
cb(.{ .total_matches = total }, ud);
|
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);
|
cb(.{ .viewport_matches = results.items }, ud);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send our complete notification if we just completed.
|
// Send our complete notification if we just completed.
|
||||||
if (!self.last_complete and self.isComplete()) {
|
if (!self.last_complete and self.isComplete()) {
|
||||||
|
log.debug("notifying search complete", .{});
|
||||||
self.last_complete = true;
|
self.last_complete = true;
|
||||||
cb(.complete, ud);
|
cb(.complete, ud);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue