From f535fc02bf0e534119c021b0e6b884d75b864c81 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sat, 27 Jul 2024 23:57:56 +0200 Subject: [PATCH] feat: more work on new find in files results table --- src/tui/Widget.zig | 2 +- src/tui/editor.zig | 8 ---- src/tui/filelist_view.zig | 49 +++++++++++++++-------- src/tui/mainview.zig | 62 ++++++++++++++++++++++++++++- src/tui/mode/input/flow.zig | 4 +- src/tui/mode/mini/find_in_files.zig | 23 ++++------- 6 files changed, 105 insertions(+), 43 deletions(-) diff --git a/src/tui/Widget.zig b/src/tui/Widget.zig index bb8f86a..e37bbe9 100644 --- a/src/tui/Widget.zig +++ b/src/tui/Widget.zig @@ -155,7 +155,7 @@ pub fn need_reflow() void { tp.self_pid().send(.{"reflow"}) catch {}; } -pub fn name(self: Self, buf: []u8) []u8 { +pub fn name(self: Self, buf: []u8) []const u8 { return self.plane.name(buf); } diff --git a/src/tui/editor.zig b/src/tui/editor.zig index fed3de5..afec546 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -3129,17 +3129,11 @@ pub const Editor = struct { return self.find_in(query, ripgrep.find_in_stdin, true); } - pub fn find_in_files(self: *Self, query: []const u8) !void { - return self.find_in(query, ripgrep.find_in_files, false); - } - pub fn add_match(self: *Self, m: tp.message) !void { - var path: []const u8 = undefined; var begin_line: usize = undefined; var begin_pos: usize = undefined; var end_line: usize = undefined; var end_pos: usize = undefined; - var lines: []const u8 = undefined; var batch_cbor: []const u8 = undefined; if (try m.match(.{ "A", "done", self.match_token })) { self.add_match_done(); @@ -3149,8 +3143,6 @@ pub const Editor = struct { self.add_match_internal(begin_line, begin_pos, end_line, end_pos); } else if (try m.match(.{ tp.any, tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos) })) { self.add_match_internal(begin_line, begin_pos, end_line, end_pos); - } else if (try m.match(.{ tp.any, tp.extract(&path), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos), tp.extract(&lines) })) { - self.logger.print("match: {s}:{d}:{d}:{d}:{d} {s}", .{ path, begin_line, begin_pos + 1, end_line, end_pos + 1, std.fmt.fmtSliceEscapeLower(lines) }); } } diff --git a/src/tui/filelist_view.zig b/src/tui/filelist_view.zig index 1f0261e..5c999ca 100644 --- a/src/tui/filelist_view.zig +++ b/src/tui/filelist_view.zig @@ -57,12 +57,6 @@ pub fn create(allocator: Allocator, parent: Plane) !Widget { .on_scroll = EventHandler.bind(self, Self.handle_scroll), }), }; - (try self.entries.addOne()).* = .{ .path = "file_path_1.zig", .begin_line = 1, .begin_pos = 1, .end_line = 1, .end_pos = 10, .lines = "matching text" }; - (try self.entries.addOne()).* = .{ .path = "file_path_2.zig", .begin_line = 1, .begin_pos = 1, .end_line = 1, .end_pos = 10, .lines = "matching text" }; - (try self.entries.addOne()).* = .{ .path = "file_path_3.zig", .begin_line = 1, .begin_pos = 1, .end_line = 1, .end_pos = 10, .lines = "matching text" }; - try self.add_item(0); - try self.add_item(1); - try self.add_item(2); return Widget.to(self); } @@ -72,23 +66,47 @@ pub fn deinit(self: *Self, a: Allocator) void { } pub fn handle_resize(self: *Self, pos: Widget.Box) void { + self.plane.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return; + self.plane.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return; self.menu.resize(pos); } -fn add_item(self: *Self, idx: usize) !void { +pub fn walk(self: *Self, walk_ctx: *anyopaque, f: Widget.WalkFn, w: *Widget) bool { + return self.menu.walk(walk_ctx, f) or f(walk_ctx, w); +} + +pub fn add_item(self: *Self, entry_: Entry) !void { + const idx = self.entries.items.len; + const entry = (try self.entries.addOne()); + entry.* = entry_; + entry.path = try self.allocator.dupe(u8, entry_.path); + entry.lines = try self.allocator.dupe(u8, entry_.lines); var label = std.ArrayList(u8).init(self.allocator); defer label.deinit(); const writer = label.writer(); - try cbor.writeValue(writer, idx); - try self.menu.add_item_with_handler(label.items, handle_menu_action); + cbor.writeValue(writer, idx) catch return; + self.menu.add_item_with_handler(label.items, handle_menu_action) catch return; + self.menu.resize(Widget.Box.from(self.plane)); +} + +pub fn reset(self: *Self) void { + for (self.entries.items) |entry| { + self.allocator.free(entry.path); + self.allocator.free(entry.lines); + } + self.entries.clearRetainingCapacity(); + self.menu.reset_items(); } pub fn render(self: *Self, theme: *const Widget.Theme) bool { + self.plane.set_base_style(" ", theme.panel); + self.plane.erase(); + self.plane.home(); return self.menu.render(theme); } fn handle_render_menu(self: *Self, button: *Button.State(*Menu.State(*Self)), theme: *const Widget.Theme, selected: bool) bool { - const style_base = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.editor_widget; + const style_base = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.panel; // const style_keybind = if (tui.find_scope_style(theme, "entity.name")) |sty| sty.style else style_base; button.plane.set_base_style(" ", style_base); button.plane.erase(); @@ -102,14 +120,13 @@ fn handle_render_menu(self: *Self, button: *Button.State(*Menu.State(*Self)), th return false; } if (idx >= self.entries.items.len) { - self.logger.print_err(name, "table entry index out of range: {d}/{d}", .{ idx, self.entries.items.len }); return false; } const entry = &self.entries.items[idx]; const pointer = if (selected) "⏵" else " "; _ = button.plane.print("{s} ", .{pointer}) catch {}; button.plane.set_style(style_base); - _ = button.plane.print("{s} ", .{entry.path}) catch {}; + _ = button.plane.print("{s}:{d} {s}", .{entry.path, entry.begin_line + 1, entry.lines}) catch {}; return false; } @@ -143,12 +160,12 @@ fn handle_menu_action(menu: **Menu.State(*Self), button: *Button.State(*Menu.Sta tp.self_pid().send(.{ "cmd", "navigate", .{ .file = entry.path, .goto = .{ + entry.end_line + 1, + entry.end_pos + 2, entry.begin_line, - entry.begin_pos, - entry.begin_line, - entry.begin_pos, + entry.begin_pos + 1, entry.end_line, - entry.end_pos, + entry.end_pos + 1, }, } }) catch |e| self.logger.err("navigate", e); } diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index c21d4f3..e65a2ba 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -2,6 +2,7 @@ const std = @import("std"); const tp = @import("thespian"); const cbor = @import("cbor"); const tracy = @import("tracy"); +const ripgrep = @import("ripgrep"); const root = @import("root"); const location_history = @import("location_history"); const project_manager = @import("project_manager"); @@ -33,6 +34,7 @@ panels: ?*WidgetList = null, last_match_text: ?[]const u8 = null, location_history: location_history, file_stack: std.ArrayList([]const u8), +find_in_files_done: bool = false, const NavState = struct { time: i64 = 0, @@ -80,13 +82,45 @@ pub fn deinit(self: *Self, a: std.mem.Allocator) void { } pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { - if (try m.match(.{"write_restore_info"})) { + var path: []const u8 = undefined; + var begin_line: usize = undefined; + var begin_pos: usize = undefined; + var end_line: usize = undefined; + var end_pos: usize = undefined; + var lines: []const u8 = undefined; + if (try m.match(.{ "FIF", tp.extract(&path), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos), tp.extract(&lines) })) { + try self.add_find_in_files_result(path, begin_line, begin_pos, end_line, end_pos, lines); + return true; + } else if (try m.match(.{ "FIF", "done" })) { + self.find_in_files_done = true; + return true; + } else if (try m.match(.{"write_restore_info"})) { self.write_restore_info(); return true; } return if (try self.floating_views.send(from_, m)) true else self.widgets.send(from_, m); } +fn add_find_in_files_result(self: *Self, path: []const u8, begin_line: usize, begin_pos: usize, end_line: usize, end_pos: usize, lines: []const u8) tp.result { + const filelist_view = @import("filelist_view.zig"); + if (!self.is_panel_view_showing(filelist_view)) + _ = self.toggle_panel_view(filelist_view, false) catch |e| return tp.exit_error(e, @errorReturnTrace()); + const fl = self.get_panel_view(filelist_view) orelse @panic("filelist_view missing"); + if (self.find_in_files_done) { + self.find_in_files_done = false; + fl.reset(); + } + @import("log").logger("mainview").print("match: {s}:{d}:{d}:{d}:{d} {s}", .{ path, begin_line, begin_pos + 1, end_line, end_pos + 1, std.fmt.fmtSliceEscapeLower(lines) }); + fl.add_item(.{ + .path = path, + .begin_line = begin_line - 1, + .begin_pos = begin_pos - 1, + .end_line = end_line - 1, + .end_pos = end_pos - 1, + .lines = lines + }) catch |e| return tp.exit_error(e, @errorReturnTrace()); +} + pub fn update(self: *Self) void { self.widgets.update(); self.floating_views.update(); @@ -132,6 +166,14 @@ fn toggle_panel_view(self: *Self, view: anytype, enable_only: bool) !bool { return enabled; } +fn get_panel_view(self: *Self, comptime view: type) ?*view { + return if (self.panels) |panels| if (panels.get(@typeName(view))) |w| w.dynamic_cast(view) else null else null; +} + +fn is_panel_view_showing(self: *Self, comptime view: type) bool { + return self.get_panel_view(view) != null; +} + fn close_all_panel_views(self: *Self) void { if (self.panels) |panels| { self.widgets.remove(panels.widget()); @@ -265,6 +307,13 @@ const cmds = struct { tui.need_render(); } + pub fn toggle_panel(self: *Self, _: Ctx) Result { + if (self.is_panel_view_showing(@import("filelist_view.zig"))) + _ = try self.toggle_panel_view(@import("filelist_view.zig"), false) + else + _ = try self.toggle_panel_view(@import("logview.zig"), false); + } + pub fn toggle_logview(self: *Self, _: Ctx) Result { _ = try self.toggle_panel_view(@import("logview.zig"), false); } @@ -289,6 +338,10 @@ const cmds = struct { _ = try self.toggle_panel_view(@import("filelist_view.zig"), false); } + pub fn show_filelist_view(self: *Self, _: Ctx) Result { + _ = try self.toggle_panel_view(@import("filelist_view.zig"), true); + } + pub fn jump_back(self: *Self, _: Ctx) Result { try self.location_history.back(location_jump); } @@ -360,6 +413,13 @@ pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result } } +pub fn find_in_files(self: *Self, query: []const u8) !void { + const find_f = ripgrep.find_in_files; + if (std.mem.indexOfScalar(u8, query, '\n')) |_| return; + var rg = try find_f(self.a, query, "FIF"); + defer rg.deinit(); +} + pub fn location_update(self: *Self, m: tp.message) tp.result { var row: usize = 0; var col: usize = 0; diff --git a/src/tui/mode/input/flow.zig b/src/tui/mode/input/flow.zig index 3b52946..d2f7799 100644 --- a/src/tui/mode/input/flow.zig +++ b/src/tui/mode/input/flow.zig @@ -81,7 +81,7 @@ fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) !void { return switch (modifiers) { mod.CTRL => switch (keynormal) { 'E' => self.cmd("open_recent", .{}), - 'J' => self.cmd("toggle_logview", .{}), + 'J' => self.cmd("toggle_panel", .{}), 'Z' => self.cmd("undo", .{}), 'Y' => self.cmd("redo", .{}), 'Q' => self.cmd("quit", .{}), @@ -199,7 +199,7 @@ fn mapPress(self: *Self, keypress: u32, egc: u32, modifiers: u32) !void { key.F07 => self.cmd("dump_current_line", .{}), key.F09 => self.cmd("theme_prev", .{}), key.F10 => self.cmd("theme_next", .{}), - key.F11 => self.cmd("toggle_logview", .{}), + key.F11 => self.cmd("toggle_panel", .{}), key.F12 => self.cmd("goto_definition", .{}), key.F34 => self.cmd("toggle_whitespace", .{}), // C-F10 key.F58 => self.cmd("gutter_mode_next", .{}), // A-F10 diff --git a/src/tui/mode/mini/find_in_files.zig b/src/tui/mode/mini/find_in_files.zig index 09d41d8..8dbb3e5 100644 --- a/src/tui/mode/mini/find_in_files.zig +++ b/src/tui/mode/mini/find_in_files.zig @@ -22,27 +22,23 @@ buf: [1024]u8 = undefined, input: []u8 = "", last_buf: [1024]u8 = undefined, last_input: []u8 = "", -start_view: ed.View, -start_cursor: ed.Cursor, -editor: *ed.Editor, +mainview: *mainview, pub fn create(a: Allocator, _: command.Context) !*Self { const self: *Self = try a.create(Self); - if (tui.current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_editor()) |editor| { + if (tui.current().mainview.dynamic_cast(mainview)) |mv| { self.* = .{ .a = a, - .start_view = editor.view, - .start_cursor = editor.get_primary().cursor, - .editor = editor, + .mainview = mv, }; - if (editor.get_primary().selection) |sel| ret: { + if (mv.get_editor()) |editor| if (editor.get_primary().selection) |sel| ret: { const text = editor.get_selection(sel, self.a) catch break :ret; defer self.a.free(text); @memcpy(self.buf[0..text.len], text); self.input = self.buf[0..text.len]; - } + }; return self; - }; + } return error.NotFound; } @@ -178,8 +174,7 @@ fn flush_input(self: *Self) !void { return; @memcpy(self.last_buf[0..self.input.len], self.input); self.last_input = self.last_buf[0..self.input.len]; - command.executeName("show_logview", .{}) catch {}; - try self.editor.find_in_files(self.input); + try self.mainview.find_in_files(self.input); } } @@ -188,8 +183,6 @@ fn cmd(self: *Self, name_: []const u8, ctx: command.Context) tp.result { return command.executeName(name_, ctx); } -fn cancel(self: *Self) void { - self.editor.get_primary().cursor = self.start_cursor; - self.editor.scroll_to(self.start_view.row); +fn cancel(_: *Self) void { command.executeName("exit_mini_mode", .{}) catch {}; }