From 038ed4da2b86c8c5104565f19f737c9bc2073367 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 11 Dec 2024 20:54:53 +0100 Subject: [PATCH] refactor: simplify getting the active editor and selection --- src/tui/inspector_view.zig | 20 ++--- src/tui/mainview.zig | 97 ++++++++++++---------- src/tui/mode/mini/find.zig | 43 +++++----- src/tui/mode/mini/find_in_files.zig | 31 +++---- src/tui/mode/mini/goto.zig | 23 +++-- src/tui/mode/mini/move_to_char.zig | 3 +- src/tui/mode/mini/open_file.zig | 26 +++--- src/tui/mode/mini/save_as.zig | 19 ++--- src/tui/mode/overlay/file_type_palette.zig | 7 +- src/tui/tui.zig | 12 +++ 10 files changed, 138 insertions(+), 143 deletions(-) diff --git a/src/tui/inspector_view.zig b/src/tui/inspector_view.zig index 248d3cc..bc2f819 100644 --- a/src/tui/inspector_view.zig +++ b/src/tui/inspector_view.zig @@ -11,7 +11,6 @@ const EventHandler = @import("EventHandler"); const tui = @import("tui.zig"); const Widget = @import("Widget.zig"); -const mainview = @import("mainview.zig"); const ed = @import("editor.zig"); pub const name = @typeName(Self); @@ -25,18 +24,15 @@ last_node: usize = 0, const Self = @This(); pub fn create(allocator: Allocator, parent: Plane) !Widget { - if (tui.current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_editor()) |editor| { - const self: *Self = try allocator.create(Self); - self.* = .{ - .plane = try Plane.init(&(Widget.Box{}).opts_vscroll(name), parent), - .editor = editor, - .pos_cache = try ed.PosToWidthCache.init(allocator), - }; - - try editor.handlers.add(EventHandler.bind(self, ed_receive)); - return Widget.to(self); + const editor = tui.get_active_editor() orelse return error.NotFound; + const self: *Self = try allocator.create(Self); + self.* = .{ + .plane = try Plane.init(&(Widget.Box{}).opts_vscroll(name), parent), + .editor = editor, + .pos_cache = try ed.PosToWidthCache.init(allocator), }; - return error.NotFound; + try editor.handlers.add(EventHandler.bind(self, ed_receive)); + return Widget.to(self); } pub fn deinit(self: *Self, allocator: Allocator) void { diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 82c6041..1a4d428 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -37,8 +37,8 @@ floating_views: WidgetStack, commands: Commands = undefined, top_bar: ?*Widget = null, bottom_bar: ?*Widget = null, -editor: ?*ed.Editor = null, -view_widget_idx: usize, +active_editor: ?usize = null, +editors: std.ArrayListUnmanaged(*ed.Editor) = .{}, panels: ?*WidgetList = null, last_match_text: ?[]const u8 = null, location_history: location_history, @@ -215,14 +215,25 @@ fn toggle_view(self: *Self, view: anytype) !void { tui.current().resize(); } +fn check_all_not_dirty(self: *const Self) command.Result { + for (self.editors.items) |editor| + if (editor.is_dirty()) + return tp.exit("unsaved changes"); +} + +fn check_active_not_dirty(self: *const Self) command.Result { + if (self.active_editor) |idx| + if (self.editors.items[idx].is_dirty()) + return tp.exit("unsaved changes"); +} + const cmds = struct { pub const Target = Self; const Ctx = command.Context; const Result = command.Result; pub fn quit(self: *Self, _: Ctx) Result { - if (self.editor) |editor| if (editor.is_dirty()) - return tp.exit("unsaved changes"); + try self.check_all_not_dirty(); try tp.self_pid().send("quit"); } pub const quit_meta = .{ .description = "Quit (exit) Flow Control" }; @@ -255,15 +266,12 @@ const cmds = struct { var project_dir: []const u8 = undefined; if (!try ctx.args.match(.{tp.extract(&project_dir)})) return; - if (self.editor) |editor| { - if (editor.is_dirty()) - return tp.exit("unsaved changes"); - self.clear_file_stack(); + try self.check_all_not_dirty(); + for (self.editors.items) |editor| { editor.clear_diagnostics(); try editor.close_file(.{}); - } else { - self.clear_file_stack(); } + self.clear_file_stack(); self.clear_find_in_files_results(.diagnostics); if (self.file_list_type == .diagnostics and self.is_panel_view_showing(filelist_view)) try self.toggle_panel_view(filelist_view, false); @@ -319,14 +327,11 @@ const cmds = struct { } const f = project_manager.normalize_file_path(file orelse return); - const same_file = if (self.editor) |editor| if (editor.file_path) |fp| - std.mem.eql(u8, fp, f) - else - false else false; + const same_file = if (self.get_active_file_path()) |fp| std.mem.eql(u8, fp, f) else false; if (!same_file) { - if (self.editor) |editor| { - if (editor.is_dirty()) return tp.exit("unsaved changes"); + if (self.get_active_editor()) |editor| { + try self.check_active_not_dirty(); editor.send_editor_jump_source() catch {}; } try self.create_editor(); @@ -492,7 +497,7 @@ const cmds = struct { tp.extract(&sel.end.col), })) return error.InvalidArgument; file_path = project_manager.normalize_file_path(file_path); - if (self.editor) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) + if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) try editor.add_diagnostic(file_path, source, code, message, severity, sel) else try self.add_find_in_files_result( @@ -512,7 +517,7 @@ const cmds = struct { var file_path: []const u8 = undefined; if (!try ctx.args.match(.{tp.extract(&file_path)})) return error.InvalidArgument; file_path = project_manager.normalize_file_path(file_path); - if (self.editor) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) + if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse "")) editor.clear_diagnostics(); self.clear_find_in_files_results(.diagnostics); @@ -522,7 +527,7 @@ const cmds = struct { pub const clear_diagnostics_meta = .{ .arguments = &.{.string} }; pub fn show_diagnostics(self: *Self, _: Ctx) Result { - const editor = self.editor orelse return; + const editor = self.get_active_editor() orelse return; self.clear_find_in_files_results(.diagnostics); for (editor.diagnostics.items) |diagnostic| { try self.add_find_in_files_result( @@ -565,7 +570,7 @@ const cmds = struct { }; pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result { - const editor = if (self.editor) |editor_| editor_ else return; + const editor = self.get_active_editor() orelse return; var sel: ed.Selection = undefined; if (try m.match(.{ "E", "location", tp.more })) @@ -576,7 +581,7 @@ pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result self.show_file_async_and_free(file_path) else self.show_home_async(); - self.editor = null; + self.active_editor = null; return; } @@ -601,7 +606,7 @@ pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result pub fn location_update(self: *Self, m: tp.message) tp.result { var row: usize = 0; var col: usize = 0; - const file_path = (self.editor orelse return).file_path orelse return; + const file_path = self.get_active_file_path() orelse return; if (try m.match(.{ tp.any, tp.any, tp.any, tp.extract(&row), tp.extract(&col) })) { if (row == 0 and col == 0) return; @@ -646,8 +651,12 @@ fn store_last_match_text(self: *Self, text: ?[]const u8) void { self.last_match_text = text; } -pub fn get_editor(self: *Self) ?*ed.Editor { - return self.editor; +pub fn get_active_editor(self: *Self) ?*ed.Editor { + return self.editors.items[self.active_editor orelse return null]; +} + +pub fn get_active_file_path(self: *Self) ?[]const u8 { + return if (self.get_active_editor()) |editor| editor.file_path orelse null else null; } pub fn walk(self: *Self, ctx: *anyopaque, f: Widget.WalkFn, w: *Widget) bool { @@ -702,29 +711,27 @@ fn create_home(self: *Self) !void { } fn write_restore_info(self: *Self) void { - if (self.editor) |editor| { - var sfa = std.heap.stackFallback(512, self.allocator); - const a = sfa.get(); - var meta = std.ArrayList(u8).init(a); - editor.write_state(meta.writer()) catch return; - const file_name = root.get_restore_file_name() catch return; - var file = std.fs.createFileAbsolute(file_name, .{ .truncate = true }) catch return; - defer file.close(); - file.writeAll(meta.items) catch return; - } + const editor = self.get_active_editor() orelse return; + var sfa = std.heap.stackFallback(512, self.allocator); + const a = sfa.get(); + var meta = std.ArrayList(u8).init(a); + editor.write_state(meta.writer()) catch return; + const file_name = root.get_restore_file_name() catch return; + var file = std.fs.createFileAbsolute(file_name, .{ .truncate = true }) catch return; + defer file.close(); + file.writeAll(meta.items) catch return; } fn read_restore_info(self: *Self) !void { - if (self.editor) |editor| { - const file_name = try root.get_restore_file_name(); - const file = try std.fs.cwd().openFile(file_name, .{ .mode = .read_only }); - defer file.close(); - const stat = try file.stat(); - var buf = try self.allocator.alloc(u8, @intCast(stat.size)); - defer self.allocator.free(buf); - const size = try file.readAll(buf); - try editor.extract_state(buf[0..size]); - } + const editor = self.get_active_editor() orelse return; + const file_name = try root.get_restore_file_name(); + const file = try std.fs.cwd().openFile(file_name, .{ .mode = .read_only }); + defer file.close(); + const stat = try file.stat(); + var buf = try self.allocator.alloc(u8, @intCast(stat.size)); + defer self.allocator.free(buf); + const size = try file.readAll(buf); + try editor.extract_state(buf[0..size]); } fn push_file_stack(self: *Self, file_path: []const u8) !void { @@ -800,7 +807,7 @@ fn add_info_content( info.set_content(content) catch |e| return tp.exit_error(e, @errorReturnTrace()); const match: ed.Match = .{ .begin = .{ .row = begin_line, .col = begin_pos }, .end = .{ .row = end_line, .col = end_pos } }; - if (self.editor) |editor| + if (self.get_active_editor()) |editor| switch (editor.matches.items.len) { 0 => { (editor.matches.addOne() catch return).* = match; diff --git a/src/tui/mode/mini/find.zig b/src/tui/mode/mini/find.zig index 88913d0..09fe83b 100644 --- a/src/tui/mode/mini/find.zig +++ b/src/tui/mode/mini/find.zig @@ -6,7 +6,6 @@ const command = @import("command"); const EventHandler = @import("EventHandler"); const tui = @import("../../tui.zig"); -const mainview = @import("../../mainview.zig"); const ed = @import("../../editor.zig"); const Allocator = @import("std").mem.Allocator; @@ -28,29 +27,27 @@ history_pos: ?usize = null, commands: Commands = undefined, pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.MiniMode } { - if (tui.current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_editor()) |editor| { - const self: *Self = try allocator.create(Self); - self.* = .{ - .allocator = allocator, - .input = ArrayList(u8).init(allocator), - .last_input = ArrayList(u8).init(allocator), - .start_view = editor.view, - .start_cursor = editor.get_primary().cursor, - .editor = editor, - }; - try self.commands.init(self); - if (editor.get_primary().selection) |sel| ret: { - const text = editor.get_selection(sel, self.allocator) catch break :ret; - defer self.allocator.free(text); - try self.input.appendSlice(text); - } - var mode = try keybind.mode("mini/find", allocator, .{ - .insert_command = "mini_mode_insert_bytes", - }); - mode.event_handler = EventHandler.to_owned(self); - return .{ mode, .{ .name = name } }; + const editor = tui.get_active_editor() orelse return error.NotFound; + const self: *Self = try allocator.create(Self); + self.* = .{ + .allocator = allocator, + .input = ArrayList(u8).init(allocator), + .last_input = ArrayList(u8).init(allocator), + .start_view = editor.view, + .start_cursor = editor.get_primary().cursor, + .editor = editor, }; - return error.NotFound; + try self.commands.init(self); + if (editor.get_primary().selection) |sel| ret: { + const text = editor.get_selection(sel, self.allocator) catch break :ret; + defer self.allocator.free(text); + try self.input.appendSlice(text); + } + var mode = try keybind.mode("mini/find", allocator, .{ + .insert_command = "mini_mode_insert_bytes", + }); + mode.event_handler = EventHandler.to_owned(self); + return .{ mode, .{ .name = name } }; } pub fn deinit(self: *Self) void { diff --git a/src/tui/mode/mini/find_in_files.zig b/src/tui/mode/mini/find_in_files.zig index a628c94..28e01a4 100644 --- a/src/tui/mode/mini/find_in_files.zig +++ b/src/tui/mode/mini/find_in_files.zig @@ -6,7 +6,6 @@ const command = @import("command"); const EventHandler = @import("EventHandler"); const tui = @import("../../tui.zig"); -const mainview = @import("../../mainview.zig"); const Allocator = @import("std").mem.Allocator; const eql = @import("std").mem.eql; @@ -21,30 +20,22 @@ buf: [1024]u8 = undefined, input: []u8 = "", last_buf: [1024]u8 = undefined, last_input: []u8 = "", -mainview: *mainview, commands: Commands = undefined, pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.MiniMode } { const self: *Self = try allocator.create(Self); - if (tui.current().mainview.dynamic_cast(mainview)) |mv| { - self.* = .{ - .allocator = allocator, - .mainview = mv, - }; - try self.commands.init(self); - if (mv.get_editor()) |editor| if (editor.get_primary().selection) |sel| ret: { - const text = editor.get_selection(sel, self.allocator) catch break :ret; - defer self.allocator.free(text); - @memcpy(self.buf[0..text.len], text); - self.input = self.buf[0..text.len]; - }; - var mode = try keybind.mode("mini/find_in_files", allocator, .{ - .insert_command = "mini_mode_insert_bytes", - }); - mode.event_handler = EventHandler.to_owned(self); - return .{ mode, .{ .name = name } }; + self.* = .{ .allocator = allocator }; + try self.commands.init(self); + if (tui.get_active_selection(self.allocator)) |text| { + defer self.allocator.free(text); + @memcpy(self.buf[0..text.len], text); + self.input = self.buf[0..text.len]; } - return error.NotFound; + var mode = try keybind.mode("mini/find_in_files", allocator, .{ + .insert_command = "mini_mode_insert_bytes", + }); + mode.event_handler = EventHandler.to_owned(self); + return .{ mode, .{ .name = name } }; } pub fn deinit(self: *Self) void { diff --git a/src/tui/mode/mini/goto.zig b/src/tui/mode/mini/goto.zig index 71a7606..56257e1 100644 --- a/src/tui/mode/mini/goto.zig +++ b/src/tui/mode/mini/goto.zig @@ -8,7 +8,6 @@ const command = @import("command"); const EventHandler = @import("EventHandler"); const tui = @import("../../tui.zig"); -const mainview = @import("../../mainview.zig"); const Allocator = @import("std").mem.Allocator; const fmt = @import("std").fmt; @@ -26,19 +25,17 @@ commands: Commands = undefined, pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.MiniMode } { const self: *Self = try allocator.create(Self); - if (tui.current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_editor()) |editor| { - self.* = .{ - .allocator = allocator, - .start = editor.get_primary().cursor.row + 1, - }; - try self.commands.init(self); - var mode = try keybind.mode("mini/goto", allocator, .{ - .insert_command = "mini_mode_insert_bytes", - }); - mode.event_handler = EventHandler.to_owned(self); - return .{ mode, .{ .name = name } }; + const editor = tui.get_active_editor() orelse return error.NotFound; + self.* = .{ + .allocator = allocator, + .start = editor.get_primary().cursor.row + 1, }; - return error.NotFound; + try self.commands.init(self); + var mode = try keybind.mode("mini/goto", allocator, .{ + .insert_command = "mini_mode_insert_bytes", + }); + mode.event_handler = EventHandler.to_owned(self); + return .{ mode, .{ .name = name } }; } pub fn deinit(self: *Self) void { diff --git a/src/tui/mode/mini/move_to_char.zig b/src/tui/mode/mini/move_to_char.zig index 2c21cb3..c725e07 100644 --- a/src/tui/mode/mini/move_to_char.zig +++ b/src/tui/mode/mini/move_to_char.zig @@ -6,7 +6,6 @@ const command = @import("command"); const EventHandler = @import("EventHandler"); const tui = @import("../../tui.zig"); -const mainview = @import("../../mainview.zig"); const Allocator = @import("std").mem.Allocator; @@ -32,7 +31,7 @@ const Operation = enum { pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tui.MiniMode } { var right: bool = true; - const select = if (tui.current().mainview.dynamic_cast(mainview)) |mv| if (mv.get_editor()) |editor| if (editor.get_primary().selection) |_| true else false else false else false; + const select = if (tui.get_active_editor()) |editor| if (editor.get_primary().selection) |_| true else false else false; _ = ctx.args.match(.{tp.extract(&right)}) catch return error.InvalidArgument; const self: *Self = try allocator.create(Self); self.* = .{ diff --git a/src/tui/mode/mini/open_file.zig b/src/tui/mode/mini/open_file.zig index 955cde7..407ffeb 100644 --- a/src/tui/mode/mini/open_file.zig +++ b/src/tui/mode/mini/open_file.zig @@ -4,26 +4,24 @@ const root = @import("root"); const command = @import("command"); const tui = @import("../../tui.zig"); -const mainview = @import("../../mainview.zig"); pub const Type = @import("file_browser.zig").Create(@This()); pub const create = Type.create; pub fn load_entries(self: *Type) error{ Exit, OutOfMemory }!void { - if (tui.current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_editor()) |editor| { - if (editor.is_dirty()) return tp.exit("unsaved changes"); - if (editor.file_path) |old_path| - if (std.mem.lastIndexOf(u8, old_path, "/")) |pos| - try self.file_path.appendSlice(old_path[0 .. pos + 1]); - if (editor.get_primary().selection) |sel| ret: { - const text = editor.get_selection(sel, self.allocator) catch break :ret; - defer self.allocator.free(text); - if (!(text.len > 2 and std.mem.eql(u8, text[0..2], ".."))) - self.file_path.clearRetainingCapacity(); - try self.file_path.appendSlice(text); - } - }; + const editor = tui.get_active_editor() orelse return; + if (editor.is_dirty()) return tp.exit("unsaved changes"); + if (editor.file_path) |old_path| + if (std.mem.lastIndexOf(u8, old_path, "/")) |pos| + try self.file_path.appendSlice(old_path[0 .. pos + 1]); + if (editor.get_primary().selection) |sel| ret: { + const text = editor.get_selection(sel, self.allocator) catch break :ret; + defer self.allocator.free(text); + if (!(text.len > 2 and std.mem.eql(u8, text[0..2], ".."))) + self.file_path.clearRetainingCapacity(); + try self.file_path.appendSlice(text); + } } pub fn name(_: *Type) []const u8 { diff --git a/src/tui/mode/mini/save_as.zig b/src/tui/mode/mini/save_as.zig index 0650f09..42c5835 100644 --- a/src/tui/mode/mini/save_as.zig +++ b/src/tui/mode/mini/save_as.zig @@ -11,16 +11,15 @@ pub const Type = @import("file_browser.zig").Create(@This()); pub const create = Type.create; pub fn load_entries(self: *Type) !void { - if (tui.current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_editor()) |editor| { - try self.file_path.appendSlice(editor.file_path orelse ""); - if (editor.get_primary().selection) |sel| ret: { - const text = editor.get_selection(sel, self.allocator) catch break :ret; - defer self.allocator.free(text); - if (!(text.len > 2 and std.mem.eql(u8, text[0..2], ".."))) - self.file_path.clearRetainingCapacity(); - try self.file_path.appendSlice(text); - } - }; + const editor = tui.get_active_editor() orelse return; + try self.file_path.appendSlice(editor.file_path orelse ""); + if (editor.get_primary().selection) |sel| ret: { + const text = editor.get_selection(sel, self.allocator) catch break :ret; + defer self.allocator.free(text); + if (!(text.len > 2 and std.mem.eql(u8, text[0..2], ".."))) + self.file_path.clearRetainingCapacity(); + try self.file_path.appendSlice(text); + } } pub fn name(_: *Type) []const u8 { diff --git a/src/tui/mode/overlay/file_type_palette.zig b/src/tui/mode/overlay/file_type_palette.zig index 3464cbd..ae7e3d8 100644 --- a/src/tui/mode/overlay/file_type_palette.zig +++ b/src/tui/mode/overlay/file_type_palette.zig @@ -5,7 +5,6 @@ const syntax = @import("syntax"); const Widget = @import("../../Widget.zig"); const tui = @import("../../tui.zig"); -const mainview = @import("../../mainview.zig"); pub const Type = @import("palette.zig").Create(@This()); @@ -32,9 +31,9 @@ pub fn load_entries(palette: *Type) !usize { var longest_hint: usize = 0; var idx: usize = 0; previous_file_type = blk: { - if (tui.current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_editor()) |editor| { - if (editor.syntax) |editor_syntax| break :blk editor_syntax.file_type.name; - }; + if (tui.get_active_editor()) |editor| + if (editor.syntax) |editor_syntax| + break :blk editor_syntax.file_type.name; break :blk null; }; diff --git a/src/tui/tui.zig b/src/tui/tui.zig index fac0dc9..a62c098 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -908,6 +908,18 @@ pub fn current() *Self { return instance_ orelse @panic("tui call out of context"); } +pub fn get_active_editor() ?*@import("editor.zig").Editor { + if (current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_active_editor()) |editor| + return editor; + return null; +} + +pub fn get_active_selection(allocator: std.mem.Allocator) ?[]u8 { + const editor = get_active_editor() orelse return null; + const sel = editor.get_primary().selection orelse return null; + return editor.get_selection(sel, allocator) catch null; +} + fn context_check() void { if (instance_ == null) @panic("tui call out of context"); }