feat(palette): sort command palette by last used time
This commit is contained in:
parent
7230e7de86
commit
c167257c89
1 changed files with 65 additions and 17 deletions
|
@ -28,7 +28,8 @@ menu: *Menu.State(*Self),
|
||||||
inputbox: *InputBox.State(*Self),
|
inputbox: *InputBox.State(*Self),
|
||||||
logger: log.Logger,
|
logger: log.Logger,
|
||||||
longest: usize = 0,
|
longest: usize = 0,
|
||||||
commands: Commands = undefined,
|
palette_commands: command.Collection(cmds) = undefined,
|
||||||
|
commands: std.ArrayList(Command) = undefined,
|
||||||
hints: ?*const tui.KeybindHints = null,
|
hints: ?*const tui.KeybindHints = null,
|
||||||
longest_hint: usize = 0,
|
longest_hint: usize = 0,
|
||||||
|
|
||||||
|
@ -37,7 +38,14 @@ view_rows: usize,
|
||||||
view_pos: usize = 0,
|
view_pos: usize = 0,
|
||||||
total_items: usize = 0,
|
total_items: usize = 0,
|
||||||
|
|
||||||
|
const Command = struct {
|
||||||
|
name: []const u8,
|
||||||
|
id: command.ID,
|
||||||
|
used_time: i64,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn create(a: std.mem.Allocator) !tui.Mode {
|
pub fn create(a: std.mem.Allocator) !tui.Mode {
|
||||||
|
if (mru_list == null) mru_list = std.ArrayList(i64).init(a);
|
||||||
const mv = if (tui.current().mainview.dynamic_cast(mainview)) |mv_| mv_ else return error.NotFound;
|
const mv = if (tui.current().mainview.dynamic_cast(mainview)) |mv_| mv_ else return error.NotFound;
|
||||||
const self: *Self = try a.create(Self);
|
const self: *Self = try a.create(Self);
|
||||||
self.* = .{
|
self.* = .{
|
||||||
|
@ -55,12 +63,21 @@ pub fn create(a: std.mem.Allocator) !tui.Mode {
|
||||||
}))).dynamic_cast(InputBox.State(*Self)) orelse unreachable,
|
}))).dynamic_cast(InputBox.State(*Self)) orelse unreachable,
|
||||||
.hints = if (tui.current().input_mode) |m| m.keybind_hints else null,
|
.hints = if (tui.current().input_mode) |m| m.keybind_hints else null,
|
||||||
.view_rows = get_view_rows(tui.current().screen()),
|
.view_rows = get_view_rows(tui.current().screen()),
|
||||||
|
.commands = std.ArrayList(Command).init(a),
|
||||||
};
|
};
|
||||||
if (self.hints) |hints| {
|
if (self.hints) |hints| {
|
||||||
for (hints.values()) |val|
|
for (hints.values()) |val|
|
||||||
self.longest_hint = @max(self.longest_hint, val.len);
|
self.longest_hint = @max(self.longest_hint, val.len);
|
||||||
}
|
}
|
||||||
try self.commands.init(self);
|
for (command.commands.items) |cmd_| if (cmd_) |p| {
|
||||||
|
(self.commands.addOne() catch @panic("oom")).* = .{
|
||||||
|
.name = p.name,
|
||||||
|
.id = p.id,
|
||||||
|
.used_time = get_used_time(p.id),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
self.sort_by_used_time();
|
||||||
|
try self.palette_commands.init(self);
|
||||||
try self.start_query();
|
try self.start_query();
|
||||||
try mv.floating_views.add(self.menu.container_widget);
|
try mv.floating_views.add(self.menu.container_widget);
|
||||||
return .{
|
return .{
|
||||||
|
@ -71,6 +88,7 @@ pub fn create(a: std.mem.Allocator) !tui.Mode {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.palette_commands.deinit();
|
||||||
self.commands.deinit();
|
self.commands.deinit();
|
||||||
tui.current().message_filters.remove_ptr(self);
|
tui.current().message_filters.remove_ptr(self);
|
||||||
if (tui.current().mainview.dynamic_cast(mainview)) |mv|
|
if (tui.current().mainview.dynamic_cast(mainview)) |mv|
|
||||||
|
@ -90,6 +108,9 @@ fn on_render_menu(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *c
|
||||||
var iter = button.opts.label; // label contains cbor, first the file name, then multiple match indexes
|
var iter = button.opts.label; // label contains cbor, first the file name, then multiple match indexes
|
||||||
if (!(cbor.matchString(&iter, &command_name) catch false))
|
if (!(cbor.matchString(&iter, &command_name) catch false))
|
||||||
command_name = "#ERROR#";
|
command_name = "#ERROR#";
|
||||||
|
var command_id: command.ID = undefined;
|
||||||
|
if (!(cbor.matchValue(&iter, cbor.extract(&command_id)) catch false))
|
||||||
|
command_id = 0;
|
||||||
if (!(cbor.matchString(&iter, &keybind_hint) catch false))
|
if (!(cbor.matchString(&iter, &keybind_hint) catch false))
|
||||||
keybind_hint = "";
|
keybind_hint = "";
|
||||||
const pointer = if (selected) "⏵" else " ";
|
const pointer = if (selected) "⏵" else " ";
|
||||||
|
@ -137,8 +158,11 @@ fn get_view_rows(screen: Widget.Box) usize {
|
||||||
|
|
||||||
fn menu_action_execute_command(menu: **Menu.State(*Self), button: *Button.State(*Menu.State(*Self))) void {
|
fn menu_action_execute_command(menu: **Menu.State(*Self), button: *Button.State(*Menu.State(*Self))) void {
|
||||||
var command_name: []const u8 = undefined;
|
var command_name: []const u8 = undefined;
|
||||||
|
var command_id: command.ID = undefined;
|
||||||
var iter = button.opts.label;
|
var iter = button.opts.label;
|
||||||
if (!(cbor.matchString(&iter, &command_name) catch false)) return;
|
if (!(cbor.matchString(&iter, &command_name) catch false)) return;
|
||||||
|
if (!(cbor.matchValue(&iter, cbor.extract(&command_id)) catch false)) return;
|
||||||
|
update_used_time(command_id);
|
||||||
tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err("navigate", e);
|
tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err("navigate", e);
|
||||||
tp.self_pid().send(.{ "cmd", command_name, .{} }) catch |e| menu.*.opts.ctx.logger.err("navigate", e);
|
tp.self_pid().send(.{ "cmd", command_name, .{} }) catch |e| menu.*.opts.ctx.logger.err("navigate", e);
|
||||||
}
|
}
|
||||||
|
@ -253,20 +277,19 @@ fn start_query(self: *Self) tp.result {
|
||||||
self.items = 0;
|
self.items = 0;
|
||||||
self.menu.reset_items();
|
self.menu.reset_items();
|
||||||
self.menu.selected = null;
|
self.menu.selected = null;
|
||||||
for (command.commands.items) |cmd_| if (cmd_) |p| {
|
for (self.commands.items) |cmd_|
|
||||||
self.longest = @max(self.longest, p.name.len);
|
self.longest = @max(self.longest, cmd_.name.len);
|
||||||
};
|
|
||||||
|
|
||||||
if (self.inputbox.text.items.len == 0) {
|
if (self.inputbox.text.items.len == 0) {
|
||||||
self.total_items = 0;
|
self.total_items = 0;
|
||||||
var pos: usize = 0;
|
var pos: usize = 0;
|
||||||
for (command.commands.items) |cmd_| if (cmd_) |p| {
|
for (self.commands.items) |cmd_| {
|
||||||
defer self.total_items += 1;
|
defer self.total_items += 1;
|
||||||
defer pos += 1;
|
defer pos += 1;
|
||||||
if (pos < self.view_pos) continue;
|
if (pos < self.view_pos) continue;
|
||||||
if (self.items < self.view_rows)
|
if (self.items < self.view_rows)
|
||||||
self.add_item(p.name, null) catch |e| return tp.exit_error(e);
|
self.add_item(cmd_.name, cmd_.id, null) catch |e| return tp.exit_error(e);
|
||||||
};
|
}
|
||||||
} else {
|
} else {
|
||||||
_ = self.query_commands(self.inputbox.text.items) catch |e| return tp.exit_error(e);
|
_ = self.query_commands(self.inputbox.text.items) catch |e| return tp.exit_error(e);
|
||||||
}
|
}
|
||||||
|
@ -285,21 +308,23 @@ fn query_commands(self: *Self, query: []const u8) error{OutOfMemory}!usize {
|
||||||
|
|
||||||
const Match = struct {
|
const Match = struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
|
id: command.ID,
|
||||||
score: i32,
|
score: i32,
|
||||||
matches: []const usize,
|
matches: []const usize,
|
||||||
};
|
};
|
||||||
var matches = std.ArrayList(Match).init(self.a);
|
var matches = std.ArrayList(Match).init(self.a);
|
||||||
|
|
||||||
for (command.commands.items) |cmd_| if (cmd_) |c| {
|
for (self.commands.items) |cmd_| {
|
||||||
const match = searcher.scoreMatches(c.name, query);
|
const match = searcher.scoreMatches(cmd_.name, query);
|
||||||
if (match.score) |score| {
|
if (match.score) |score| {
|
||||||
(try matches.addOne()).* = .{
|
(try matches.addOne()).* = .{
|
||||||
.name = c.name,
|
.name = cmd_.name,
|
||||||
|
.id = cmd_.id,
|
||||||
.score = score,
|
.score = score,
|
||||||
.matches = try self.a.dupe(usize, match.matches),
|
.matches = try self.a.dupe(usize, match.matches),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
if (matches.items.len == 0) return 0;
|
if (matches.items.len == 0) return 0;
|
||||||
|
|
||||||
const less_fn = struct {
|
const less_fn = struct {
|
||||||
|
@ -316,17 +341,18 @@ fn query_commands(self: *Self, query: []const u8) error{OutOfMemory}!usize {
|
||||||
defer pos += 1;
|
defer pos += 1;
|
||||||
if (pos < self.view_pos) continue;
|
if (pos < self.view_pos) continue;
|
||||||
if (self.items < self.view_rows)
|
if (self.items < self.view_rows)
|
||||||
try self.add_item(match.name, match.matches);
|
try self.add_item(match.name, match.id, match.matches);
|
||||||
}
|
}
|
||||||
return matches.items.len;
|
return matches.items.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_item(self: *Self, command_name: []const u8, matches: ?[]const usize) !void {
|
fn add_item(self: *Self, name: []const u8, id: command.ID, matches: ?[]const usize) !void {
|
||||||
var label = std.ArrayList(u8).init(self.a);
|
var label = std.ArrayList(u8).init(self.a);
|
||||||
defer label.deinit();
|
defer label.deinit();
|
||||||
const writer = label.writer();
|
const writer = label.writer();
|
||||||
try cbor.writeValue(writer, command_name);
|
try cbor.writeValue(writer, name);
|
||||||
try cbor.writeValue(writer, if (self.hints) |hints| hints.get(command_name) orelse "" else "");
|
try cbor.writeValue(writer, id);
|
||||||
|
try cbor.writeValue(writer, if (self.hints) |hints| hints.get(name) orelse "" else "");
|
||||||
if (matches) |matches_|
|
if (matches) |matches_|
|
||||||
try cbor.writeValue(writer, matches_);
|
try cbor.writeValue(writer, matches_);
|
||||||
try self.menu.add_item_with_handler(label.items, menu_action_execute_command);
|
try self.menu.add_item_with_handler(label.items, menu_action_execute_command);
|
||||||
|
@ -381,7 +407,29 @@ fn cmd_async(_: *Self, name_: []const u8) tp.result {
|
||||||
return tp.self_pid().send(.{ "cmd", name_ });
|
return tp.self_pid().send(.{ "cmd", name_ });
|
||||||
}
|
}
|
||||||
|
|
||||||
const Commands = command.Collection(cmds);
|
fn sort_by_used_time(self: *Self) void {
|
||||||
|
const less_fn = struct {
|
||||||
|
fn less_fn(_: void, lhs: Command, rhs: Command) bool {
|
||||||
|
return lhs.used_time > rhs.used_time;
|
||||||
|
}
|
||||||
|
}.less_fn;
|
||||||
|
std.mem.sort(Command, self.commands.items, {}, less_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
var mru_list: ?std.ArrayList(i64) = null;
|
||||||
|
|
||||||
|
fn update_used_time(id: command.ID) void {
|
||||||
|
const list = if (mru_list) |*p| p else @panic("missing mru_list");
|
||||||
|
while (list.items.len < id + 1)
|
||||||
|
(list.addOne() catch @panic("oom")).* = 0;
|
||||||
|
list.items[id] = std.time.milliTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_used_time(id: command.ID) i64 {
|
||||||
|
const list = mru_list orelse @panic("missing mru_list");
|
||||||
|
return if (list.items.len < id + 1) 0 else list.items[id];
|
||||||
|
}
|
||||||
|
|
||||||
const cmds = struct {
|
const cmds = struct {
|
||||||
pub const Target = Self;
|
pub const Target = Self;
|
||||||
const Ctx = command.Context;
|
const Ctx = command.Context;
|
||||||
|
|
Loading…
Add table
Reference in a new issue