terminal: search thread active screen reconciliation loop
parent
1867928b84
commit
bfa397b196
|
|
@ -161,25 +161,19 @@ fn threadMain_(self: *Thread) !void {
|
||||||
// for data loading, etc.
|
// for data loading, etc.
|
||||||
switch (s.tick()) {
|
switch (s.tick()) {
|
||||||
// We're complete now when we were not before. Notify!
|
// We're complete now when we were not before. Notify!
|
||||||
.complete => if (self.opts.event_cb) |cb| {
|
.complete => {},
|
||||||
cb(.complete, self.opts.event_userdata);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Forward progress was made.
|
// Forward progress was made.
|
||||||
.progress => {},
|
.progress => {},
|
||||||
|
|
||||||
// All searches are blocked. Let's grab the lock and feed data.
|
// All searches are blocked. Let's grab the lock and feed data.
|
||||||
.blocked => {
|
.blocked => {
|
||||||
try s.feed(self.opts.mutex, self.opts.terminal);
|
self.opts.mutex.lock();
|
||||||
|
defer self.opts.mutex.unlock();
|
||||||
// Feeding can result in completion if there is no more
|
try s.feed(
|
||||||
// data to feed. If we transitioned to complete, notify!
|
self.alloc,
|
||||||
if (self.opts.event_cb) |cb| {
|
self.opts.terminal,
|
||||||
if (s.isComplete()) cb(
|
);
|
||||||
.complete,
|
|
||||||
self.opts.event_userdata,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,6 +183,13 @@ fn threadMain_(self: *Thread) !void {
|
||||||
cb,
|
cb,
|
||||||
self.opts.event_userdata,
|
self.opts.event_userdata,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If our forward progress resulted in us becoming complete,
|
||||||
|
// then notify our callback.
|
||||||
|
if (s.isComplete()) cb(
|
||||||
|
.complete,
|
||||||
|
self.opts.event_userdata,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have an active search, so we only want to process messages
|
// We have an active search, so we only want to process messages
|
||||||
|
|
@ -221,42 +222,11 @@ fn changeNeedle(self: *Thread, needle: []const u8) !void {
|
||||||
// No needle means stop the search.
|
// No needle means stop the search.
|
||||||
if (needle.len == 0) return;
|
if (needle.len == 0) return;
|
||||||
|
|
||||||
// Our new search state
|
|
||||||
var search: Search = .empty;
|
|
||||||
errdefer search.deinit();
|
|
||||||
|
|
||||||
// We need to grab the terminal lock to setup our search state.
|
// We need to grab the terminal lock to setup our search state.
|
||||||
self.opts.mutex.lock();
|
self.opts.mutex.lock();
|
||||||
defer self.opts.mutex.unlock();
|
defer self.opts.mutex.unlock();
|
||||||
const t: *Terminal = self.opts.terminal;
|
const t: *Terminal = self.opts.terminal;
|
||||||
|
self.search = try .init(self.alloc, needle, t);
|
||||||
// Go through all our screens, setup our search state.
|
|
||||||
//
|
|
||||||
// NOTE(mitchellh): Maybe we should only initialize the screen we're
|
|
||||||
// currently looking at (the active screen) and then let our screen
|
|
||||||
// reconciliation timer add the others later in order to minimize
|
|
||||||
// startup latency.
|
|
||||||
var it = t.screens.all.iterator();
|
|
||||||
while (it.next()) |entry| {
|
|
||||||
var screen_search: ScreenSearch = ScreenSearch.init(
|
|
||||||
self.alloc,
|
|
||||||
entry.value.*,
|
|
||||||
needle,
|
|
||||||
) catch |err| switch (err) {
|
|
||||||
error.OutOfMemory => {
|
|
||||||
// We can ignore this (although OOM probably means the whole
|
|
||||||
// ship is sinking). Our reconciliation timer will try again
|
|
||||||
// later.
|
|
||||||
log.warn("error initializing screen search key={} err={}", .{ entry.key, err });
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
errdefer screen_search.deinit();
|
|
||||||
search.screens.put(entry.key, screen_search);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Our search state is setup
|
|
||||||
self.search = search;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wakeupCallback(
|
fn wakeupCallback(
|
||||||
|
|
@ -340,11 +310,27 @@ const Search = struct {
|
||||||
/// The last total matches reported.
|
/// The last total matches reported.
|
||||||
last_total: ?usize,
|
last_total: ?usize,
|
||||||
|
|
||||||
pub const empty: Search = .{
|
pub fn init(
|
||||||
.screens = .init(.{}),
|
alloc: Allocator,
|
||||||
.last_active_screen = .primary,
|
needle: []const u8,
|
||||||
.last_total = null,
|
t: *Terminal,
|
||||||
};
|
) Allocator.Error!Search {
|
||||||
|
// We only initialize the primary screen for now. Our reconciler
|
||||||
|
// via feed will handle setting up our other screens. We just need
|
||||||
|
// to setup at least one here so that we can store our needle.
|
||||||
|
var screen_search: ScreenSearch = try .init(
|
||||||
|
alloc,
|
||||||
|
t.screens.get(.primary).?,
|
||||||
|
needle,
|
||||||
|
);
|
||||||
|
errdefer screen_search.deinit();
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.screens = .init(.{ .primary = screen_search }),
|
||||||
|
.last_active_screen = .primary,
|
||||||
|
.last_total = null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Search) void {
|
pub fn deinit(self: *Search) void {
|
||||||
var it = self.screens.iterator();
|
var it = self.screens.iterator();
|
||||||
|
|
@ -412,16 +398,63 @@ const Search = struct {
|
||||||
|
|
||||||
/// Grab the mutex and update any state that requires it, such as
|
/// Grab the mutex and update any state that requires it, such as
|
||||||
/// feeding additional data to the searches or updating the active screen.
|
/// feeding additional data to the searches or updating the active screen.
|
||||||
pub fn feed(self: *Search, mutex: *Mutex, t: *Terminal) !void {
|
pub fn feed(
|
||||||
mutex.lock();
|
self: *Search,
|
||||||
defer mutex.unlock();
|
alloc: Allocator,
|
||||||
|
t: *Terminal,
|
||||||
|
) !void {
|
||||||
// Update our active screen
|
// Update our active screen
|
||||||
if (t.screens.active_key != self.last_active_screen) {
|
if (t.screens.active_key != self.last_active_screen) {
|
||||||
self.last_active_screen = t.screens.active_key;
|
self.last_active_screen = t.screens.active_key;
|
||||||
self.last_total = null; // force notification
|
self.last_total = null; // force notification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reconcile our screens with the terminal screens. Remove
|
||||||
|
// searchers for screens that no longer exist and add searchers
|
||||||
|
// for screens that do exist but we don't have yet.
|
||||||
|
{
|
||||||
|
// Remove screens we have that no longer exist or changed.
|
||||||
|
var it = self.screens.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
const remove: bool = remove: {
|
||||||
|
// If the screen doesn't exist at all, remove it.
|
||||||
|
const actual = t.screens.all.get(entry.key) orelse break :remove true;
|
||||||
|
|
||||||
|
// If the screen pointer changed, remove it, the screen
|
||||||
|
// was totally reinitialized.
|
||||||
|
break :remove actual != entry.value.screen;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (remove) {
|
||||||
|
entry.value.deinit();
|
||||||
|
_ = self.screens.remove(entry.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Add screens that exist but we don't have yet.
|
||||||
|
var it = t.screens.all.iterator();
|
||||||
|
while (it.next()) |entry| {
|
||||||
|
if (self.screens.contains(entry.key)) continue;
|
||||||
|
self.screens.put(entry.key, ScreenSearch.init(
|
||||||
|
alloc,
|
||||||
|
entry.value.*,
|
||||||
|
self.screens.get(.primary).?.needle(),
|
||||||
|
) catch |err| switch (err) {
|
||||||
|
error.OutOfMemory => {
|
||||||
|
// OOM is probably going to sink the entire ship but
|
||||||
|
// we can just ignore it and wait on the next
|
||||||
|
// reconciliation to try again.
|
||||||
|
log.warn(
|
||||||
|
"error initializing screen search for key={} err={}",
|
||||||
|
.{ entry.key, err },
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Feed data
|
// Feed data
|
||||||
var it = self.screens.iterator();
|
var it = self.screens.iterator();
|
||||||
while (it.next()) |entry| {
|
while (it.next()) |entry| {
|
||||||
|
|
|
||||||
|
|
@ -98,11 +98,11 @@ pub const ScreenSearch = struct {
|
||||||
pub fn init(
|
pub fn init(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
screen: *Screen,
|
screen: *Screen,
|
||||||
needle: []const u8,
|
needle_unowned: []const u8,
|
||||||
) Allocator.Error!ScreenSearch {
|
) Allocator.Error!ScreenSearch {
|
||||||
var result: ScreenSearch = .{
|
var result: ScreenSearch = .{
|
||||||
.screen = screen,
|
.screen = screen,
|
||||||
.active = try .init(alloc, needle),
|
.active = try .init(alloc, needle_unowned),
|
||||||
.history = null,
|
.history = null,
|
||||||
.state = .active,
|
.state = .active,
|
||||||
.active_results = .empty,
|
.active_results = .empty,
|
||||||
|
|
@ -128,6 +128,12 @@ pub const ScreenSearch = struct {
|
||||||
return self.active.window.alloc;
|
return self.active.window.alloc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The needle that this search is using.
|
||||||
|
pub fn needle(self: *const ScreenSearch) []const u8 {
|
||||||
|
assert(self.active.window.direction == .forward);
|
||||||
|
return self.active.window.needle;
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the total number of matches found so far.
|
/// Returns the total number of matches found so far.
|
||||||
pub fn matchesLen(self: *const ScreenSearch) usize {
|
pub fn matchesLen(self: *const ScreenSearch) usize {
|
||||||
return self.active_results.items.len + self.history_results.items.len;
|
return self.active_results.items.len + self.history_results.items.len;
|
||||||
|
|
@ -310,12 +316,9 @@ pub const ScreenSearch = struct {
|
||||||
// No history search yet, but we now have history. So let's
|
// No history search yet, but we now have history. So let's
|
||||||
// initialize.
|
// initialize.
|
||||||
|
|
||||||
// Our usage of needle below depends on this
|
|
||||||
assert(self.active.window.direction == .forward);
|
|
||||||
|
|
||||||
var search: PageListSearch = try .init(
|
var search: PageListSearch = try .init(
|
||||||
self.allocator(),
|
self.allocator(),
|
||||||
self.active.window.needle,
|
self.needle(),
|
||||||
list,
|
list,
|
||||||
history_node,
|
history_node,
|
||||||
);
|
);
|
||||||
|
|
@ -347,7 +350,7 @@ pub const ScreenSearch = struct {
|
||||||
var window: SlidingWindow = try .init(
|
var window: SlidingWindow = try .init(
|
||||||
alloc,
|
alloc,
|
||||||
.forward,
|
.forward,
|
||||||
self.active.window.needle,
|
self.needle(),
|
||||||
);
|
);
|
||||||
defer window.deinit();
|
defer window.deinit();
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue