feat: add fuzzy matching to command palette
This commit is contained in:
parent
88a036113b
commit
0a3efee633
2 changed files with 54 additions and 10 deletions
|
@ -206,6 +206,7 @@ pub fn build(b: *std.Build) void {
|
||||||
.{ .name = "diff", .module = diff_mod },
|
.{ .name = "diff", .module = diff_mod },
|
||||||
.{ .name = "help.md", .module = help_mod },
|
.{ .name = "help.md", .module = help_mod },
|
||||||
.{ .name = "CaseData", .module = zg_dep.module("CaseData") },
|
.{ .name = "CaseData", .module = zg_dep.module("CaseData") },
|
||||||
|
.{ .name = "fuzzig", .module = fuzzig_dep.module("fuzzig") },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,9 @@ const std = @import("std");
|
||||||
const tp = @import("thespian");
|
const tp = @import("thespian");
|
||||||
const log = @import("log");
|
const log = @import("log");
|
||||||
const cbor = @import("cbor");
|
const cbor = @import("cbor");
|
||||||
|
const fuzzig = @import("fuzzig");
|
||||||
|
|
||||||
const Plane = @import("renderer").Plane;
|
const Plane = @import("renderer").Plane;
|
||||||
const planeutils = @import("renderer").planeutils;
|
|
||||||
const key = @import("renderer").input.key;
|
const key = @import("renderer").input.key;
|
||||||
const mod = @import("renderer").input.modifier;
|
const mod = @import("renderer").input.modifier;
|
||||||
const event_type = @import("renderer").input.event_type;
|
const event_type = @import("renderer").input.event_type;
|
||||||
|
@ -13,20 +13,16 @@ const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8;
|
||||||
const tui = @import("../../tui.zig");
|
const tui = @import("../../tui.zig");
|
||||||
const command = @import("../../command.zig");
|
const command = @import("../../command.zig");
|
||||||
const EventHandler = @import("../../EventHandler.zig");
|
const EventHandler = @import("../../EventHandler.zig");
|
||||||
const MessageFilter = @import("../../MessageFilter.zig");
|
|
||||||
const Button = @import("../../Button.zig");
|
const Button = @import("../../Button.zig");
|
||||||
const InputBox = @import("../../InputBox.zig");
|
const InputBox = @import("../../InputBox.zig");
|
||||||
const Menu = @import("../../Menu.zig");
|
const Menu = @import("../../Menu.zig");
|
||||||
const Widget = @import("../../Widget.zig");
|
const Widget = @import("../../Widget.zig");
|
||||||
const mainview = @import("../../mainview.zig");
|
const mainview = @import("../../mainview.zig");
|
||||||
const project_manager = @import("project_manager");
|
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
const max_recent_files: usize = 25;
|
|
||||||
const max_menu_width = 80;
|
const max_menu_width = 80;
|
||||||
|
|
||||||
a: std.mem.Allocator,
|
a: std.mem.Allocator,
|
||||||
f: usize = 0,
|
|
||||||
menu: *Menu.State(*Self),
|
menu: *Menu.State(*Self),
|
||||||
inputbox: *InputBox.State(*Self),
|
inputbox: *InputBox.State(*Self),
|
||||||
logger: log.Logger,
|
logger: log.Logger,
|
||||||
|
@ -204,20 +200,67 @@ fn mapRelease(self: *Self, keypress: u32, _: u32) tp.result {
|
||||||
fn start_query(self: *Self) tp.result {
|
fn start_query(self: *Self) tp.result {
|
||||||
self.menu.reset_items();
|
self.menu.reset_items();
|
||||||
self.menu.selected = null;
|
self.menu.selected = null;
|
||||||
for (command.commands.items) |cmd_|
|
for (command.commands.items) |cmd_| if (cmd_) |p| {
|
||||||
if (cmd_) |p| {
|
|
||||||
self.add_item(p.name, null) catch |e| return tp.exit_error(e);
|
|
||||||
self.longest = @max(self.longest, p.name.len);
|
self.longest = @max(self.longest, p.name.len);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (self.inputbox.text.items.len == 0) {
|
||||||
|
for (command.commands.items) |cmd_| if (cmd_) |p| {
|
||||||
|
self.add_item(p.name, null) catch |e| return tp.exit_error(e);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
_ = self.query_commands(self.inputbox.text.items) catch |e| return tp.exit_error(e);
|
||||||
|
}
|
||||||
self.do_resize();
|
self.do_resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_item(self: *Self, command_name: []const u8, matches: ?[]const u8) !void {
|
fn query_commands(self: *Self, query: []const u8) error{OutOfMemory}!usize {
|
||||||
|
var searcher = try fuzzig.Ascii.init(
|
||||||
|
self.a,
|
||||||
|
self.longest, // haystack max size
|
||||||
|
self.longest, // needle max size
|
||||||
|
.{ .case_sensitive = false },
|
||||||
|
);
|
||||||
|
defer searcher.deinit();
|
||||||
|
|
||||||
|
const Match = struct {
|
||||||
|
name: []const u8,
|
||||||
|
score: i32,
|
||||||
|
matches: []const usize,
|
||||||
|
};
|
||||||
|
var matches = std.ArrayList(Match).init(self.a);
|
||||||
|
|
||||||
|
for (command.commands.items) |cmd_| if (cmd_) |c| {
|
||||||
|
const match = searcher.scoreMatches(c.name, query);
|
||||||
|
if (match.score) |score| {
|
||||||
|
(try matches.addOne()).* = .{
|
||||||
|
.name = c.name,
|
||||||
|
.score = score,
|
||||||
|
.matches = try self.a.dupe(usize, match.matches),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (matches.items.len == 0) return 0;
|
||||||
|
|
||||||
|
const less_fn = struct {
|
||||||
|
fn less_fn(_: void, lhs: Match, rhs: Match) bool {
|
||||||
|
return lhs.score > rhs.score;
|
||||||
|
}
|
||||||
|
}.less_fn;
|
||||||
|
std.mem.sort(Match, matches.items, {}, less_fn);
|
||||||
|
|
||||||
|
for (matches.items) |match|
|
||||||
|
try self.add_item(match.name, match.matches);
|
||||||
|
return matches.items.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_item(self: *Self, command_name: []const u8, 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, command_name);
|
||||||
if (matches) |cb| _ = try writer.write(cb);
|
if (matches) |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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue