From 1d947ab49967943b04a1c4ae37c048d657b52ca9 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 23 Jan 2025 16:45:04 +0100 Subject: [PATCH] refactor: improve capsulation and safety of tui module public api --- src/tui/Button.zig | 2 +- src/tui/InputBox.zig | 7 +- src/tui/ModalBackground.zig | 4 +- src/tui/editor.zig | 47 ++-- src/tui/editor_gutter.zig | 14 +- src/tui/home.zig | 2 +- src/tui/inputview.zig | 4 +- src/tui/inspector_view.zig | 2 +- src/tui/mainview.zig | 53 +++-- src/tui/mode/helix.zig | 3 +- src/tui/mode/mini/file_browser.zig | 10 +- src/tui/mode/mini/find.zig | 2 +- src/tui/mode/mini/find_in_files.zig | 2 +- src/tui/mode/mini/goto.zig | 2 +- src/tui/mode/mini/save_as.zig | 1 - src/tui/mode/overlay/command_palette.zig | 2 +- src/tui/mode/overlay/fontface_palette.zig | 8 +- .../overlay/list_all_commands_palette.zig | 2 +- src/tui/mode/overlay/open_recent.zig | 15 +- src/tui/mode/overlay/palette.zig | 15 +- src/tui/mode/overlay/theme_palette.zig | 4 +- src/tui/status/clock.zig | 4 +- src/tui/status/filestate.zig | 11 +- src/tui/status/keystate.zig | 6 +- src/tui/status/minilog.zig | 4 +- src/tui/status/modestate.zig | 4 +- src/tui/status/modstate.zig | 4 +- src/tui/tui.zig | 203 +++++++++++------- 28 files changed, 239 insertions(+), 198 deletions(-) diff --git a/src/tui/Button.zig b/src/tui/Button.zig index 54089f8..d356ebe 100644 --- a/src/tui/Button.zig +++ b/src/tui/Button.zig @@ -125,7 +125,7 @@ pub fn State(ctx_type: type) type { tui.need_render(); return true; } else if (try m.match(.{ "H", tp.extract(&self.hover) })) { - tui.current().rdr.request_mouse_cursor_pointer(self.hover); + tui.rdr().request_mouse_cursor_pointer(self.hover); tui.need_render(); return true; } diff --git a/src/tui/InputBox.zig b/src/tui/InputBox.zig index 10f41e5..71d79b7 100644 --- a/src/tui/InputBox.zig +++ b/src/tui/InputBox.zig @@ -36,10 +36,9 @@ pub fn Options(context: type) type { } if (self.cursor) |cursor| { const pos: c_int = @intCast(cursor); - const tui_ = tui.current(); - if (tui_.config.enable_terminal_cursor) { + if (tui.config().enable_terminal_cursor) { const y, const x = self.plane.rel_yx_to_abs(0, pos + 1); - tui_.rdr.cursor_enable(y, x, tui_.get_cursor_shape()) catch {}; + tui.rdr().cursor_enable(y, x, tui.get_cursor_shape()) catch {}; } else { self.plane.cursor_move_yx(0, pos + 1) catch return false; var cell = self.plane.cell_init(); @@ -119,7 +118,7 @@ pub fn State(ctx_type: type) type { tui.need_render(); return true; } else if (try m.match(.{ "H", tp.extract(&self.hover) })) { - tui.current().rdr.request_mouse_cursor_pointer(self.hover); + tui.rdr().request_mouse_cursor_pointer(self.hover); tui.need_render(); return true; } diff --git a/src/tui/ModalBackground.zig b/src/tui/ModalBackground.zig index c6c8ac9..63444c0 100644 --- a/src/tui/ModalBackground.zig +++ b/src/tui/ModalBackground.zig @@ -63,7 +63,7 @@ pub fn create(ctx_type: type, allocator: std.mem.Allocator, parent: Widget, opts .allocator = allocator, .plane = parent.plane.*, .opts = opts, - .fade_time_ms = tui.current().config.animation_max_lag, + .fade_time_ms = tui.config().animation_max_lag, .frame_rate = @intCast(tp.env.get().num("frame-rate")), }; return self; @@ -106,7 +106,7 @@ pub fn State(ctx_type: type) type { self.call_click_handler(@enumFromInt(btn)); return true; } else if (try m.match(.{ "H", tp.extract(&self.hover) })) { - tui.current().rdr.request_mouse_cursor_default(self.hover); + tui.rdr().request_mouse_cursor_default(self.hover); return true; } return false; diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 0fe48aa..5ab0100 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -98,7 +98,7 @@ pub const CurSel = struct { } fn enable_selection(self: *Self, root: Buffer.Root, metrics: Buffer.Metrics) !*Selection { - return switch (tui.current().get_selection_style()) { + return switch (tui.get_selection_style()) { .normal => self.enable_selection_normal(), .inclusive => try self.enable_selection_inclusive(root, metrics), }; @@ -131,7 +131,7 @@ pub const CurSel = struct { } fn disable_selection(self: *Self, root: Buffer.Root, metrics: Buffer.Metrics) void { - switch (tui.current().get_selection_style()) { + switch (tui.get_selection_style()) { .normal => self.disable_selection_normal(), .inclusive => self.disable_selection_inclusive(root, metrics), } @@ -404,8 +404,8 @@ pub const Editor = struct { fn init(self: *Self, allocator: Allocator, n: Plane, buffer_manager: *Buffer.Manager) void { const logger = log.logger("editor"); const frame_rate = tp.env.get().num("frame-rate"); - const indent_size = tui.current().config.indent_size; - const tab_width = tui.current().config.tab_width; + const indent_size = tui.config().indent_size; + const tab_width = tui.config().tab_width; self.* = Self{ .allocator = allocator, .plane = n, @@ -423,8 +423,8 @@ pub const Editor = struct { .cursels = CurSel.List.init(allocator), .cursels_saved = CurSel.List.init(allocator), .matches = Match.List.init(allocator), - .enable_terminal_cursor = tui.current().config.enable_terminal_cursor, - .render_whitespace = from_whitespace_mode(tui.current().config.whitespace_mode), + .enable_terminal_cursor = tui.config().enable_terminal_cursor, + .render_whitespace = from_whitespace_mode(tui.config().whitespace_mode), .diagnostics = std.ArrayList(Diagnostic).init(allocator), }; } @@ -555,7 +555,7 @@ pub const Editor = struct { self.buffer = null; self.plane.erase(); self.plane.home(); - tui.current().rdr.cursor_disable(); + tui.rdr().cursor_disable(); _ = try self.handlers.msg(.{ "E", "close" }); if (self.syntax) |_| if (self.file_path) |file_path| project_manager.did_close(file_path) catch {}; @@ -933,7 +933,7 @@ pub const Editor = struct { return Buffer.Walker.keep_walking; } }; - const hl_row: ?usize = if (tui.current().config.highlight_current_line) blk: { + const hl_row: ?usize = if (tui.config().highlight_current_line) blk: { if (self.get_primary().selection) |_| if (theme.editor_selection.bg) |sel_bg| if (theme.editor_line_highlight.bg) |hl_bg| @@ -969,7 +969,7 @@ pub const Editor = struct { } fn render_cursors(self: *Self, theme: *const Widget.Theme, cell_map: CellMap) !void { - const style = tui.current().get_selection_style(); + const style = tui.get_selection_style(); const frame = tracy.initZone(@src(), .{ .name = "editor render cursors" }); defer frame.deinit(); for (self.cursels.items[0 .. self.cursels.items.len - 1]) |*cursel_| if (cursel_.*) |*cursel| { @@ -988,19 +988,18 @@ pub const Editor = struct { } fn render_cursor_primary(self: *Self, cursor: *const Cursor, theme: *const Widget.Theme, cell_map: CellMap) !void { - const tui_ = tui.current(); - if (!tui_.is_mainview_focused() or !self.enable_terminal_cursor) { + if (!tui.is_mainview_focused() or !self.enable_terminal_cursor) { if (self.screen_cursor(cursor)) |pos| { set_cell_map_cursor(cell_map, pos.row, pos.col); self.plane.cursor_move_yx(@intCast(pos.row), @intCast(pos.col)) catch return; - const style = if (tui_.is_mainview_focused()) theme.editor_cursor else theme.editor_cursor_secondary; + const style = if (tui.is_mainview_focused()) theme.editor_cursor else theme.editor_cursor_secondary; self.render_cursor_cell(style); } } else { if (self.screen_cursor(cursor)) |pos| { set_cell_map_cursor(cell_map, pos.row, pos.col); const y, const x = self.plane.rel_yx_to_abs(@intCast(pos.row), @intCast(pos.col)); - const configured_shape = tui_.get_cursor_shape(); + const configured_shape = tui.get_cursor_shape(); const cursor_shape = if (self.cursels.items.len > 1) switch (configured_shape) { .beam => .block, .beam_blink => .block_blink, @@ -1008,9 +1007,9 @@ pub const Editor = struct { .underline_blink => .block_blink, else => configured_shape, } else configured_shape; - tui_.rdr.cursor_enable(y, x, cursor_shape) catch {}; + tui.rdr().cursor_enable(y, x, cursor_shape) catch {}; } else { - tui_.rdr.cursor_disable(); + tui.rdr().cursor_disable(); } } } @@ -2112,12 +2111,12 @@ pub const Editor = struct { } fn get_animation_min_lag() f64 { - const ms: f64 = @floatFromInt(tui.current().config.animation_min_lag); + const ms: f64 = @floatFromInt(tui.config().animation_min_lag); return @max(ms * 0.001, 0.001); // to seconds } fn get_animation_max_lag() f64 { - const ms: f64 = @floatFromInt(tui.current().config.animation_max_lag); + const ms: f64 = @floatFromInt(tui.config().animation_max_lag); return @max(ms * 0.001, 0.001); // to seconds } @@ -2249,7 +2248,7 @@ pub const Editor = struct { @import("renderer").copy_to_windows_clipboard(text) catch |e| self.logger.print_err("clipboard", "failed to set clipboard: {any}", .{e}); } else { - tui.current().rdr.copy_to_system_clipboard(text); + tui.rdr().copy_to_system_clipboard(text); } } @@ -3566,13 +3565,13 @@ pub const Editor = struct { pub fn enable_jump_mode(self: *Self, _: Context) Result { self.jump_mode = true; - tui.current().rdr.request_mouse_cursor_pointer(true); + tui.rdr().request_mouse_cursor_pointer(true); } pub const enable_jump_mode_meta = .{ .description = "Enable jump/hover mode" }; pub fn disable_jump_mode(self: *Self, _: Context) Result { self.jump_mode = false; - tui.current().rdr.request_mouse_cursor_text(true); + tui.rdr().request_mouse_cursor_text(true); } pub const disable_jump_mode_meta = .{}; @@ -3762,7 +3761,7 @@ pub const Editor = struct { if (ctx.args.match(.{ "then", .{ tp.extract(&cmd), tp.extract_cbor(&args) } }) catch false) { then = true; } - if (tui.current().config.enable_format_on_save) if (self.get_formatter()) |_| { + if (tui.config().enable_format_on_save) if (self.get_formatter()) |_| { self.need_save_after_filter = .{ .then = if (then) .{ .cmd = cmd, .args = args } else null }; const primary = self.get_primary(); const sel = primary.selection; @@ -4888,10 +4887,10 @@ pub const EditorWidget = struct { } else if (try m.match(.{ "H", tp.extract(&self.hover) })) { if (self.editor.jump_mode) { self.update_hover_timer(.init); - tui.current().rdr.request_mouse_cursor_pointer(self.hover); + tui.rdr().request_mouse_cursor_pointer(self.hover); } else { self.update_hover_timer(.cancel); - tui.current().rdr.request_mouse_cursor_text(self.hover); + tui.rdr().request_mouse_cursor_text(self.hover); } } else if (try m.match(.{"HOVER"})) { self.update_hover_timer(.fired); @@ -4947,7 +4946,7 @@ pub const EditorWidget = struct { } fn mouse_pos_abs(self: *Self, y: c_int, x: c_int, xoffset: c_int) struct { c_int, c_int } { - return if (tui.current().is_cursor_beam()) + return if (tui.is_cursor_beam()) self.editor.plane.abs_yx_to_rel_nearest_x(y, x, xoffset) else self.editor.plane.abs_yx_to_rel(y, x); diff --git a/src/tui/editor_gutter.zig b/src/tui/editor_gutter.zig index 5e2d829..26994d4 100644 --- a/src/tui/editor_gutter.zig +++ b/src/tui/editor_gutter.zig @@ -45,15 +45,15 @@ pub fn create(allocator: Allocator, parent: Widget, event_source: Widget, editor .allocator = allocator, .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent.plane.*), .parent = parent, - .linenum = tui.current().config.gutter_line_numbers, - .relative = tui.current().config.gutter_line_numbers_relative, - .highlight = tui.current().config.highlight_current_line_gutter, - .symbols = tui.current().config.gutter_symbols, + .linenum = tui.config().gutter_line_numbers, + .relative = tui.config().gutter_line_numbers_relative, + .highlight = tui.config().highlight_current_line_gutter, + .symbols = tui.config().gutter_symbols, .editor = editor, .diff = try diff.create(), .diff_symbols = std.ArrayList(Symbol).init(allocator), }; - try tui.current().message_filters.add(MessageFilter.bind(self, filter_receive)); + try tui.message_filters().add(MessageFilter.bind(self, filter_receive)); try event_source.subscribe(EventHandler.bind(self, handle_event)); return self.widget(); } @@ -65,7 +65,7 @@ pub fn widget(self: *Self) Widget { pub fn deinit(self: *Self, allocator: Allocator) void { self.diff_symbols_clear(); self.diff_symbols.deinit(); - tui.current().message_filters.remove_ptr(self); + tui.message_filters().remove_ptr(self); self.plane.deinit(); allocator.destroy(self); } @@ -130,7 +130,7 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { self.plane.set_style(theme.editor_gutter); _ = self.plane.fill(" "); if (self.linenum) { - const relative = self.relative or if (tui.current().input_mode) |mode| mode.line_numbers == .relative else false; + const relative = self.relative or if (tui.input_mode()) |mode| mode.line_numbers == .relative else false; if (relative) self.render_relative(theme) else diff --git a/src/tui/home.zig b/src/tui/home.zig index ca843fe..ad576cb 100644 --- a/src/tui/home.zig +++ b/src/tui/home.zig @@ -126,7 +126,7 @@ pub fn walk(self: *Self, walk_ctx: *anyopaque, f: Widget.WalkFn, w: *Widget) boo pub fn receive(_: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { var hover: bool = false; if (try m.match(.{ "H", tp.extract(&hover) })) { - tui.current().rdr.request_mouse_cursor_default(hover); + tui.rdr().request_mouse_cursor_default(hover); tui.need_render(); return true; } diff --git a/src/tui/inputview.zig b/src/tui/inputview.zig index 50a9546..a8be882 100644 --- a/src/tui/inputview.zig +++ b/src/tui/inputview.zig @@ -39,12 +39,12 @@ pub fn create(allocator: Allocator, parent: Plane) !Widget { .plane = n, .buffer = Buffer.init(allocator), }; - try tui.current().input_listeners.add(EventHandler.bind(self, listen)); + try tui.input_listeners().add(EventHandler.bind(self, listen)); return Widget.to(self); } pub fn deinit(self: *Self, allocator: Allocator) void { - tui.current().input_listeners.remove_ptr(self); + tui.input_listeners().remove_ptr(self); for (self.buffer.items) |item| self.buffer.allocator.free(item.json); self.buffer.deinit(); diff --git a/src/tui/inspector_view.zig b/src/tui/inspector_view.zig index bc2f819..9af41da 100644 --- a/src/tui/inspector_view.zig +++ b/src/tui/inspector_view.zig @@ -37,7 +37,7 @@ pub fn create(allocator: Allocator, parent: Plane) !Widget { pub fn deinit(self: *Self, allocator: Allocator) void { self.editor.handlers.remove_ptr(self); - tui.current().message_filters.remove_ptr(self); + tui.message_filters().remove_ptr(self); self.plane.deinit(); allocator.destroy(self); } diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 70aca79..c6398d7 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -80,8 +80,8 @@ pub fn create(allocator: std.mem.Allocator) !Widget { const widgets = try WidgetList.createV(allocator, self.plane, @typeName(Self), .dynamic); self.widgets = widgets; self.widgets_widget = widgets.widget(); - if (tui.current().config.top_bar.len > 0) - self.top_bar = try widgets.addP(try @import("status/bar.zig").create(allocator, self.plane, tui.current().config.top_bar, .none, null)); + if (tui.config().top_bar.len > 0) + self.top_bar = try widgets.addP(try @import("status/bar.zig").create(allocator, self.plane, tui.config().top_bar, .none, null)); const views = try WidgetList.createH(allocator, self.plane, @typeName(Self), .dynamic); self.views = views; @@ -90,8 +90,8 @@ pub fn create(allocator: std.mem.Allocator) !Widget { try widgets.add(self.views_widget); - if (tui.current().config.bottom_bar.len > 0) { - self.bottom_bar = try widgets.addP(try @import("status/bar.zig").create(allocator, self.plane, tui.current().config.bottom_bar, .grip, EventHandler.bind(self, handle_bottom_bar_event))); + if (tui.config().bottom_bar.len > 0) { + self.bottom_bar = try widgets.addP(try @import("status/bar.zig").create(allocator, self.plane, tui.config().bottom_bar, .grip, EventHandler.bind(self, handle_bottom_bar_event))); } if (tp.env.get().is("show-input")) self.toggle_inputview_async(); @@ -142,9 +142,6 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { .end = .{ .row = end_line, .col = end_pos }, }); 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); } @@ -212,7 +209,7 @@ fn toggle_panel_view(self: *Self, view: anytype, enable_only: bool) !void { try panels.add(try view.create(self.allocator, self.widgets.plane)); self.panels = panels; } - tui.current().resize(); + tui.resize(); } fn get_panel_view(self: *Self, comptime view: type) ?*view { @@ -228,7 +225,7 @@ fn close_all_panel_views(self: *Self) void { self.widgets.remove(panels.widget()); self.panels = null; } - tui.current().resize(); + tui.resize(); } fn toggle_view(self: *Self, view: anytype) !void { @@ -237,7 +234,7 @@ fn toggle_view(self: *Self, view: anytype) !void { } else { try self.widgets.add(try view.create(self.allocator, self.plane)); } - tui.current().resize(); + tui.resize(); } fn check_all_not_dirty(self: *const Self) command.Result { @@ -274,7 +271,7 @@ const cmds = struct { return; try project_manager.open(project_dir); const project = tp.env.get().str("project"); - tui.current().rdr.set_terminal_working_directory(project); + tui.rdr().set_terminal_working_directory(project); if (self.top_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" }); if (self.bottom_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" }); } @@ -297,7 +294,7 @@ const cmds = struct { self.buffer_manager = BufferManager.init(self.allocator); try project_manager.open(project_dir); const project = tp.env.get().str("project"); - tui.current().rdr.set_terminal_working_directory(project); + tui.rdr().set_terminal_working_directory(project); if (self.top_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" }); if (self.bottom_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" }); if (try project_manager.request_most_recent_file(self.allocator)) |file_path| @@ -487,9 +484,9 @@ const cmds = struct { pub const add_split_meta = .{}; pub fn gutter_mode_next(self: *Self, _: Ctx) Result { - const tui_ = tui.current(); - var ln = tui_.config.gutter_line_numbers; - var lnr = tui_.config.gutter_line_numbers_relative; + const config = tui.config_mut(); + var ln = config.gutter_line_numbers; + var lnr = config.gutter_line_numbers_relative; if (ln and !lnr) { ln = true; lnr = true; @@ -500,9 +497,9 @@ const cmds = struct { ln = true; lnr = false; } - tui_.config.gutter_line_numbers = ln; - tui_.config.gutter_line_numbers_relative = lnr; - try tui_.save_config(); + config.gutter_line_numbers = ln; + config.gutter_line_numbers_relative = lnr; + try tui.save_config(); if (self.widgets.get("editor_gutter")) |gutter_widget| { const gutter = gutter_widget.dynamic_cast(@import("editor_gutter.zig")) orelse return; gutter.linenum = ln; @@ -658,7 +655,7 @@ const cmds = struct { defer self.allocator.free(text); return command.executeName("paste", command.fmt(.{text})) catch {}; } - tui.current().rdr.request_system_clipboard(); + tui.rdr().request_system_clipboard(); } pub const system_paste_meta = .{ .description = "Paste from system clipboard" }; @@ -719,7 +716,7 @@ const cmds = struct { if (!try ctx.args.match(.{tp.extract(&amount)})) return error.InvalidArgument; if (build_options.gui) - tui.current().rdr.adjust_fontsize(amount); + tui.rdr().adjust_fontsize(amount); } pub const adjust_fontsize_meta = .{ .arguments = &.{.float} }; @@ -728,13 +725,13 @@ const cmds = struct { if (!try ctx.args.match(.{tp.extract(&fontsize)})) return error.InvalidArgument; if (build_options.gui) - tui.current().rdr.set_fontsize(fontsize); + tui.rdr().set_fontsize(fontsize); } pub const set_fontsize_meta = .{ .arguments = &.{.float} }; pub fn reset_fontsize(_: *Self, _: Ctx) Result { if (build_options.gui) - tui.current().rdr.reset_fontsize(); + tui.rdr().reset_fontsize(); } pub const reset_fontsize_meta = .{ .description = "Reset font to configured size" }; @@ -743,13 +740,13 @@ const cmds = struct { if (!try ctx.args.match(.{tp.extract(&fontface)})) return error.InvalidArgument; if (build_options.gui) - tui.current().rdr.set_fontface(fontface); + tui.rdr().set_fontface(fontface); } pub const set_fontface_meta = .{ .arguments = &.{.float} }; pub fn reset_fontface(_: *Self, _: Ctx) Result { if (build_options.gui) - tui.current().rdr.reset_fontface(); + tui.rdr().reset_fontface(); } pub const reset_fontface_meta = .{ .description = "Reset font to configured face" }; }; @@ -888,7 +885,7 @@ fn create_editor(self: *Self) !void { try self.replace_active_view(editor_widget); if (editor.dynamic_cast(ed.EditorWidget)) |p| try self.add_editor(&p.editor); - tui.current().resize(); + tui.resize(); } fn toggle_logview_async(_: *Self) void { @@ -913,16 +910,16 @@ fn create_home(self: *Self) !void { if (self.active_editor) |_| return; try self.delete_active_view(); try self.replace_active_view(try home.create(self.allocator, Widget.to(self))); - tui.current().resize(); + tui.resize(); } fn create_home_split(self: *Self) !void { tui.reset_drag_context(); try self.add_view(try home.create(self.allocator, Widget.to(self))); - tui.current().resize(); + tui.resize(); } -fn write_restore_info(self: *Self) void { +pub fn write_restore_info(self: *Self) void { const editor = self.get_active_editor() orelse return; var sfa = std.heap.stackFallback(512, self.allocator); const a = sfa.get(); diff --git a/src/tui/mode/helix.zig b/src/tui/mode/helix.zig index 7003ec7..8c6b161 100644 --- a/src/tui/mode/helix.zig +++ b/src/tui/mode/helix.zig @@ -5,7 +5,6 @@ const command = @import("command"); const cmd = command.executeName; const tui = @import("../tui.zig"); -const mainview = @import("../mainview.zig"); var commands: Commands = undefined; @@ -59,7 +58,7 @@ const cmds_ = struct { const logger = log.logger("helix-mode"); defer logger.deinit(); logger.print("saved location", .{}); - const mv = tui.current().mainview.dynamic_cast(mainview) orelse return; + const mv = tui.mainview() orelse return; const file_path = mv.get_active_file_path() orelse return; const primary = (mv.get_active_editor() orelse return).get_primary(); const sel: ?location_history.Selection = if (primary.selection) |sel| .{ diff --git a/src/tui/mode/mini/file_browser.zig b/src/tui/mode/mini/file_browser.zig index 025253d..1fe07de 100644 --- a/src/tui/mode/mini/file_browser.zig +++ b/src/tui/mode/mini/file_browser.zig @@ -44,7 +44,7 @@ pub fn Create(options: type) type { .entries = std.ArrayList(Entry).init(allocator), }; try self.commands.init(self); - try tui.current().message_filters.add(MessageFilter.bind(self, receive_path_entry)); + try tui.message_filters().add(MessageFilter.bind(self, receive_path_entry)); try options.load_entries(self); if (@hasDecl(options, "restore_state")) options.restore_state(self) catch {}; @@ -57,7 +57,7 @@ pub fn Create(options: type) type { pub fn deinit(self: *Self) void { self.commands.deinit(); - tui.current().message_filters.remove_ptr(self); + tui.message_filters().remove_ptr(self); self.clear_entries(); self.entries.deinit(); self.match.deinit(); @@ -113,7 +113,7 @@ pub fn Create(options: type) type { } else { try self.file_path.appendSlice(self.query.items); } - if (tui.current().mini_mode) |*mini_mode| { + if (tui.mini_mode()) |mini_mode| { mini_mode.text = self.file_path.items; mini_mode.cursor = tui.egc_chunk_width(self.file_path.items, 0, 8); } @@ -137,7 +137,7 @@ pub fn Create(options: type) type { fn process_project_manager(self: *Self, m: tp.message) MessageFilter.Error!void { defer { - if (tui.current().mini_mode) |*mini_mode| { + if (tui.mini_mode()) |mini_mode| { mini_mode.text = self.file_path.items; mini_mode.cursor = tui.egc_chunk_width(self.file_path.items, 0, 8); } @@ -241,7 +241,7 @@ pub fn Create(options: type) type { } fn update_mini_mode_text(self: *Self) void { - if (tui.current().mini_mode) |*mini_mode| { + if (tui.mini_mode()) |mini_mode| { mini_mode.text = self.file_path.items; mini_mode.cursor = tui.egc_chunk_width(self.file_path.items, 0, 8); } diff --git a/src/tui/mode/mini/find.zig b/src/tui/mode/mini/find.zig index a662ab6..0cea98b 100644 --- a/src/tui/mode/mini/find.zig +++ b/src/tui/mode/mini/find.zig @@ -140,7 +140,7 @@ fn load_history(self: *Self, pos: usize) void { } fn update_mini_mode_text(self: *Self) void { - if (tui.current().mini_mode) |*mini_mode| { + if (tui.mini_mode()) |mini_mode| { mini_mode.text = self.input.items; mini_mode.cursor = tui.egc_chunk_width(self.input.items, 0, 8); } diff --git a/src/tui/mode/mini/find_in_files.zig b/src/tui/mode/mini/find_in_files.zig index caac1c7..c1369e0 100644 --- a/src/tui/mode/mini/find_in_files.zig +++ b/src/tui/mode/mini/find_in_files.zig @@ -81,7 +81,7 @@ fn start_query(self: *Self) !void { } fn update_mini_mode_text(self: *Self) void { - if (tui.current().mini_mode) |*mini_mode| { + if (tui.mini_mode()) |mini_mode| { mini_mode.text = self.input; mini_mode.cursor = tui.egc_chunk_width(self.input, 0, 8); } diff --git a/src/tui/mode/mini/goto.zig b/src/tui/mode/mini/goto.zig index 473c11f..56890a7 100644 --- a/src/tui/mode/mini/goto.zig +++ b/src/tui/mode/mini/goto.zig @@ -49,7 +49,7 @@ pub fn receive(self: *Self, _: tp.pid_ref, _: tp.message) error{Exit}!bool { } fn update_mini_mode_text(self: *Self) void { - if (tui.current().mini_mode) |*mini_mode| { + if (tui.mini_mode()) |mini_mode| { mini_mode.text = if (self.input) |linenum| (fmt.bufPrint(&self.buf, "{d}", .{linenum}) catch "") else diff --git a/src/tui/mode/mini/save_as.zig b/src/tui/mode/mini/save_as.zig index 42c5835..9287ed6 100644 --- a/src/tui/mode/mini/save_as.zig +++ b/src/tui/mode/mini/save_as.zig @@ -4,7 +4,6 @@ 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()); diff --git a/src/tui/mode/overlay/command_palette.zig b/src/tui/mode/overlay/command_palette.zig index 75e0e54..1f0ba2b 100644 --- a/src/tui/mode/overlay/command_palette.zig +++ b/src/tui/mode/overlay/command_palette.zig @@ -20,7 +20,7 @@ pub const Entry = struct { }; pub fn load_entries(palette: *Type) !usize { - const hints = if (tui.current().input_mode) |m| m.keybind_hints else @panic("no keybind hints"); + const hints = if (tui.input_mode()) |m| m.keybind_hints else @panic("no keybind hints"); var longest_hint: usize = 0; for (command.commands.items) |cmd_| if (cmd_) |p| { if (p.meta.description.len > 0) { diff --git a/src/tui/mode/overlay/fontface_palette.zig b/src/tui/mode/overlay/fontface_palette.zig index dce743a..b405b7e 100644 --- a/src/tui/mode/overlay/fontface_palette.zig +++ b/src/tui/mode/overlay/fontface_palette.zig @@ -33,10 +33,10 @@ pub fn deinit(palette: *Type) void { pub fn load_entries(palette: *Type) !usize { var idx: usize = 0; - previous_fontface = try palette.allocator.dupe(u8, tui.current().fontface); - const fontfaces = tui.current().fontfaces orelse return 0; - tui.current().fontfaces = null; - for (fontfaces.items) |fontface| { + previous_fontface = try palette.allocator.dupe(u8, tui.fontface()); + const fontfaces = try tui.fontfaces(palette.allocator); + defer palette.allocator.free(fontfaces); + for (fontfaces) |fontface| { idx += 1; (try palette.entries.addOne()).* = .{ .label = fontface }; if (previous_fontface) |previous_fontface_| if (std.mem.eql(u8, fontface, previous_fontface_)) { diff --git a/src/tui/mode/overlay/list_all_commands_palette.zig b/src/tui/mode/overlay/list_all_commands_palette.zig index 188bfba..f85e009 100644 --- a/src/tui/mode/overlay/list_all_commands_palette.zig +++ b/src/tui/mode/overlay/list_all_commands_palette.zig @@ -23,7 +23,7 @@ pub fn deinit(palette: *Type) void { } pub fn load_entries(palette: *Type) !usize { - const hints = if (tui.current().input_mode) |m| m.keybind_hints else @panic("no keybind hints"); + const hints = if (tui.input_mode()) |m| m.keybind_hints else @panic("no keybind hints"); var longest_hint: usize = 0; for (command.commands.items) |cmd_| if (cmd_) |p| { var label_ = std.ArrayList(u8).init(palette.allocator); diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index 5df659f..87764a0 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -18,7 +18,6 @@ const Button = @import("../../Button.zig"); const InputBox = @import("../../InputBox.zig"); const Menu = @import("../../Menu.zig"); const Widget = @import("../../Widget.zig"); -const mainview = @import("../../mainview.zig"); const ModalBackground = @import("../../ModalBackground.zig"); const Self = @This(); @@ -38,11 +37,11 @@ commands: Commands = undefined, buffer_manager: ?*BufferManager, pub fn create(allocator: std.mem.Allocator) !tui.Mode { - const mv = tui.current().mainview.dynamic_cast(mainview) orelse return error.NotFound; + const mv = tui.mainview() orelse return error.NotFound; const self: *Self = try allocator.create(Self); self.* = .{ .allocator = allocator, - .modal = try ModalBackground.create(*Self, allocator, tui.current().mainview, .{ .ctx = self }), + .modal = try ModalBackground.create(*Self, allocator, tui.mainview_widget(), .{ .ctx = self }), .menu = try Menu.create(*Self, allocator, tui.plane(), .{ .ctx = self, .on_render = on_render_menu, @@ -56,7 +55,7 @@ pub fn create(allocator: std.mem.Allocator) !tui.Mode { .buffer_manager = tui.get_buffer_manager(), }; try self.commands.init(self); - try tui.current().message_filters.add(MessageFilter.bind(self, receive_project_manager)); + try tui.message_filters().add(MessageFilter.bind(self, receive_project_manager)); self.query_pending = true; try project_manager.request_recent_files(max_recent_files); self.menu.resize(.{ .y = 0, .x = self.menu_pos_x(), .w = max_menu_width() + 2 }); @@ -72,8 +71,8 @@ pub fn create(allocator: std.mem.Allocator) !tui.Mode { pub fn deinit(self: *Self) void { self.commands.deinit(); - tui.current().message_filters.remove_ptr(self); - if (tui.current().mainview.dynamic_cast(mainview)) |mv| { + tui.message_filters().remove_ptr(self); + if (tui.mainview()) |mv| { mv.floating_views.remove(self.menu.container_widget); mv.floating_views.remove(self.modal.widget()); } @@ -86,13 +85,13 @@ inline fn menu_width(self: *Self) usize { } inline fn menu_pos_x(self: *Self) usize { - const screen_width = tui.current().screen().w; + const screen_width = tui.screen().w; const width = self.menu_width(); return if (screen_width <= width) 0 else (screen_width - width) / 2; } inline fn max_menu_width() usize { - const width = tui.current().screen().w; + const width = tui.screen().w; return @max(15, width - (width / 5)); } diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index 7f40087..83553f2 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -14,7 +14,6 @@ const tui = @import("../../tui.zig"); const Button = @import("../../Button.zig"); const InputBox = @import("../../InputBox.zig"); const Widget = @import("../../Widget.zig"); -const mainview = @import("../../mainview.zig"); const scrollbar_v = @import("../../scrollbar_v.zig"); const ModalBackground = @import("../../ModalBackground.zig"); @@ -47,11 +46,11 @@ pub fn Create(options: type) type { pub const ButtonState = Button.State(*Menu.State(*Self)); pub fn create(allocator: std.mem.Allocator) !tui.Mode { - const mv = tui.current().mainview.dynamic_cast(mainview) orelse return error.NotFound; + const mv = tui.mainview() orelse return error.NotFound; const self: *Self = try allocator.create(Self); self.* = .{ .allocator = allocator, - .modal = try ModalBackground.create(*Self, allocator, tui.current().mainview, .{ + .modal = try ModalBackground.create(*Self, allocator, tui.mainview_widget(), .{ .ctx = self, .on_click = mouse_palette_menu_cancel, }), @@ -68,7 +67,7 @@ pub fn Create(options: type) type { .ctx = self, .label = options.label, }))).dynamic_cast(InputBox.State(*Self)) orelse unreachable, - .view_rows = get_view_rows(tui.current().screen()), + .view_rows = get_view_rows(tui.screen()), .entries = std.ArrayList(Entry).init(allocator), }; self.menu.scrollbar.?.style_factory = scrollbar_style; @@ -92,8 +91,8 @@ pub fn Create(options: type) type { if (@hasDecl(options, "deinit")) options.deinit(self); self.entries.deinit(); - tui.current().message_filters.remove_ptr(self); - if (tui.current().mainview.dynamic_cast(mainview)) |mv| { + tui.message_filters().remove_ptr(self); + if (tui.mainview()) |mv| { mv.floating_views.remove(self.menu.container_widget); mv.floating_views.remove(self.modal.widget()); } @@ -160,7 +159,7 @@ pub fn Create(options: type) type { } fn do_resize(self: *Self) void { - const screen = tui.current().screen(); + const screen = tui.screen(); const w = @min(self.longest, max_menu_width) + 2 + 1 + self.longest_hint; const x = if (screen.w > w) (screen.w - w) / 2 else 0; self.view_rows = get_view_rows(screen); @@ -246,7 +245,7 @@ pub fn Create(options: type) type { while (i > 0) : (i -= 1) self.menu.select_down(); self.do_resize(); - tui.current().refresh_hover(); + tui.refresh_hover(); self.selection_updated(); } } diff --git a/src/tui/mode/overlay/theme_palette.zig b/src/tui/mode/overlay/theme_palette.zig index e3af4a6..5c482bc 100644 --- a/src/tui/mode/overlay/theme_palette.zig +++ b/src/tui/mode/overlay/theme_palette.zig @@ -27,7 +27,7 @@ var previous_theme: ?[]const u8 = null; pub fn load_entries(palette: *Type) !usize { var longest_hint: usize = 0; var idx: usize = 0; - previous_theme = tui.current().theme.name; + previous_theme = tui.theme().name; for (Widget.themes) |theme| { idx += 1; (try palette.entries.addOne()).* = .{ @@ -76,7 +76,7 @@ pub fn updated(palette: *Type, button_: ?*Type.ButtonState) !void { } pub fn cancel(palette: *Type) !void { - if (previous_theme) |name_| if (!std.mem.eql(u8, name_, tui.current().theme.name)) { + if (previous_theme) |name_| if (!std.mem.eql(u8, name_, tui.theme().name)) { previous_theme = null; tp.self_pid().send(.{ "cmd", "set_theme", .{name_} }) catch |e| palette.logger.err("theme_palette cancel", e); }; diff --git a/src/tui/status/clock.zig b/src/tui/status/clock.zig index 127109c..4ef96b6 100644 --- a/src/tui/status/clock.zig +++ b/src/tui/status/clock.zig @@ -28,13 +28,13 @@ pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?Event .on_event = event_handler, .tz = zeit.local(allocator, &env) catch |e| return tp.exit_error(e, @errorReturnTrace()), }; - try tui.current().message_filters.add(MessageFilter.bind(self, receive_tick)); + try tui.message_filters().add(MessageFilter.bind(self, receive_tick)); self.update_tick_timer(.init); return Widget.to(self); } pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { - tui.current().message_filters.remove_ptr(self); + tui.message_filters().remove_ptr(self); if (self.tick_timer) |*t| { t.cancel() catch {}; t.deinit(); diff --git a/src/tui/status/filestate.zig b/src/tui/status/filestate.zig index 008f6ae..c9f2e10 100644 --- a/src/tui/status/filestate.zig +++ b/src/tui/status/filestate.zig @@ -90,7 +90,7 @@ pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) btn.plane.fill(" "); btn.plane.home(); } - if (tui.current().mini_mode) |_| + if (tui.mini_mode()) |_| render_mini_mode(&btn.plane, theme) else if (self.detailed) self.render_detailed(&btn.plane, theme) @@ -102,14 +102,13 @@ pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme) fn render_mini_mode(plane: *Plane, theme: *const Widget.Theme) void { plane.off_styles(style.italic); - const tui_ = tui.current(); - const mini_mode = tui_.mini_mode orelse return; + const mini_mode = tui.mini_mode() orelse return; _ = plane.print(" {s}", .{mini_mode.text}) catch {}; if (mini_mode.cursor) |cursor| { const pos: c_int = @intCast(cursor); - if (tui_.config.enable_terminal_cursor) { + if (tui.config().enable_terminal_cursor) { const y, const x = plane.rel_yx_to_abs(0, pos + 1); - tui_.rdr.cursor_enable(y, x, tui_.get_cursor_shape()) catch {}; + tui.rdr().cursor_enable(y, x, tui.get_cursor_shape()) catch {}; } else { plane.cursor_move_yx(0, pos + 1) catch return; var cell = plane.cell_init(); @@ -189,7 +188,7 @@ fn render_terminal_title(self: *Self) void { if (std.mem.eql(u8, self.previous_title, new_title)) return; @memcpy(self.previous_title_buf[0..new_title.len], new_title); self.previous_title = self.previous_title_buf[0..new_title.len]; - tui.current().rdr.set_terminal_title(new_title); + tui.rdr().set_terminal_title(new_title); } pub fn receive(self: *Self, _: *Button.State(Self), _: tp.pid_ref, m: tp.message) error{Exit}!bool { diff --git a/src/tui/status/keystate.zig b/src/tui/status/keystate.zig index ee854e3..56b818f 100644 --- a/src/tui/status/keystate.zig +++ b/src/tui/status/keystate.zig @@ -36,7 +36,7 @@ pub fn create(allocator: Allocator, parent: Plane, _: ?EventHandler) @import("wi .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .wipe_after_frames = @divTrunc(frame_rate, 2), }; - try tui.current().input_listeners.add(EventHandler.bind(self, listen)); + try tui.input_listeners().add(EventHandler.bind(self, listen)); return self.widget(); } @@ -45,7 +45,7 @@ pub fn widget(self: *Self) Widget { } pub fn deinit(self: *Self, allocator: Allocator) void { - tui.current().input_listeners.remove_ptr(self); + tui.input_listeners().remove_ptr(self); self.plane.deinit(); allocator.destroy(self); } @@ -171,7 +171,7 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { return true; } if (try m.match(.{ "H", tp.extract(&self.hover) })) { - tui.current().rdr.request_mouse_cursor_pointer(self.hover); + tui.rdr().request_mouse_cursor_pointer(self.hover); return true; } diff --git a/src/tui/status/minilog.zig b/src/tui/status/minilog.zig index 56a9c03..340c241 100644 --- a/src/tui/status/minilog.zig +++ b/src/tui/status/minilog.zig @@ -34,7 +34,7 @@ pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?Event .on_event = event_handler, }; logview.init(allocator); - try tui.current().message_filters.add(MessageFilter.bind(self, receive_log)); + try tui.message_filters().add(MessageFilter.bind(self, receive_log)); try log.subscribe(); return Widget.to(self); } @@ -47,7 +47,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { } self.msg.deinit(); log.unsubscribe() catch {}; - tui.current().message_filters.remove_ptr(self); + tui.message_filters().remove_ptr(self); self.plane.deinit(); allocator.destroy(self); } diff --git a/src/tui/status/modestate.zig b/src/tui/status/modestate.zig index d89c68d..7dce3c8 100644 --- a/src/tui/status/modestate.zig +++ b/src/tui/status/modestate.zig @@ -33,11 +33,11 @@ pub fn layout(_: *void, btn: *Button.State(void)) Widget.Layout { } fn is_mini_mode() bool { - return tui.current().mini_mode != null; + return tui.mini_mode() != null; } fn is_overlay_mode() bool { - return tui.current().input_mode_outer != null; + return tui.input_mode_outer() != null; } pub fn render(_: *void, self: *Button.State(void), theme: *const Widget.Theme) bool { diff --git a/src/tui/status/modstate.zig b/src/tui/status/modstate.zig index 9e01881..351a373 100644 --- a/src/tui/status/modstate.zig +++ b/src/tui/status/modstate.zig @@ -24,7 +24,7 @@ pub fn create(allocator: Allocator, parent: Plane, _: ?EventHandler) @import("wi self.* = .{ .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), }; - try tui.current().input_listeners.add(EventHandler.bind(self, listen)); + try tui.input_listeners().add(EventHandler.bind(self, listen)); return self.widget(); } @@ -33,7 +33,7 @@ pub fn widget(self: *Self) Widget { } pub fn deinit(self: *Self, allocator: Allocator) void { - tui.current().input_listeners.remove_ptr(self); + tui.input_listeners().remove_ptr(self); self.plane.deinit(); allocator.destroy(self); } diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 2004fb7..d41144f 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -3,7 +3,6 @@ const build_options = @import("build_options"); const tp = @import("thespian"); const cbor = @import("cbor"); const log = @import("log"); -const config = @import("config"); const project_manager = @import("project_manager"); const root = @import("root"); const tracy = @import("tracy"); @@ -16,19 +15,19 @@ const keybind = @import("keybind"); const Widget = @import("Widget.zig"); const MessageFilter = @import("MessageFilter.zig"); -const mainview = @import("mainview.zig"); +const MainView = @import("mainview.zig"); const Allocator = std.mem.Allocator; allocator: Allocator, rdr: renderer, -config: config, +config: @import("config"), frame_time: usize, // in microseconds frame_clock: tp.metronome, frame_clock_running: bool = false, frame_last_time: i64 = 0, receiver: Receiver, -mainview: Widget, +mainview: ?Widget = null, message_filters: MessageFilter.List, input_mode: ?Mode = null, delayed_init_done: bool = false, @@ -55,7 +54,7 @@ keepalive_timer: ?tp.Cancellable = null, mouse_idle_timer: ?tp.Cancellable = null, default_cursor: keybind.CursorShape = .default, fontface: []const u8 = "", -fontfaces: ?std.ArrayList([]const u8) = null, +fontfaces: std.ArrayListUnmanaged([]const u8) = .{}, enable_mouse_idle_timer: bool = false, const keepalive = std.time.us_per_day * 365; // one year @@ -84,13 +83,11 @@ fn start(args: StartArgs) tp.result { } fn init(allocator: Allocator) !*Self { - var self = try allocator.create(Self); - - var conf, const conf_bufs = root.read_config(config, allocator); + var conf, const conf_bufs = root.read_config(@import("config"), allocator); defer root.free_config(allocator, conf_bufs); - const theme = get_theme_by_name(conf.theme) orelse get_theme_by_name("dark_modern") orelse return tp.exit("unknown theme"); - conf.theme = theme.name; + const theme_ = get_theme_by_name(conf.theme) orelse get_theme_by_name("dark_modern") orelse return tp.exit("unknown theme"); + conf.theme = theme_.name; conf.whitespace_mode = try allocator.dupe(u8, conf.whitespace_mode); conf.input_mode = try allocator.dupe(u8, conf.input_mode); conf.top_bar = try allocator.dupe(u8, conf.top_bar); @@ -106,6 +103,7 @@ fn init(allocator: Allocator) !*Self { const frame_time = std.time.us_per_s / conf.frame_rate; const frame_clock = try tp.metronome.init(frame_time); + var self = try allocator.create(Self); self.* = .{ .allocator = allocator, .config = conf, @@ -114,14 +112,13 @@ fn init(allocator: Allocator) !*Self { .frame_clock = frame_clock, .frame_clock_running = true, .receiver = Receiver.init(receive, self), - .mainview = undefined, .message_filters = MessageFilter.List.init(allocator), .input_listeners = EventHandler.List.init(allocator), .logger = log.logger("tui"), .init_timer = if (build_options.gui) null else try tp.timeout.init_ms(init_delay, tp.message.fmt( .{"init"}, )), - .theme = theme, + .theme = theme_, .no_sleep = tp.env.get().is("no-sleep"), }; instance_ = self; @@ -147,11 +144,11 @@ fn init(allocator: Allocator) !*Self { try self.listen_sigwinch(); }, } - self.mainview = try mainview.create(allocator); - self.resize(); + self.mainview = try MainView.create(allocator); + resize(); self.set_terminal_style(); try self.rdr.render(); - try self.save_config(); + try save_config(); try self.init_input_namespace(); if (tp.env.get().is("restore-session")) { command.executeName("restore_session", .{}) catch |e| self.logger.err("restore_session", e); @@ -168,7 +165,7 @@ fn init_input_namespace(self: *Self) !void { self.logger.print_err("keybind", "unknown mode {s}", .{namespace_name}); try keybind.set_namespace("flow"); self.config.input_mode = "flow"; - try self.save_config(); + try save_config(); }; } @@ -204,7 +201,7 @@ fn deinit(self: *Self) void { self.delayed_init_input_mode = null; } self.commands.deinit(); - self.mainview.deinit(self.allocator); + if (self.mainview) |*mv| mv.deinit(self.allocator); self.message_filters.deinit(); self.input_listeners.deinit(); if (self.frame_clock_running) @@ -296,7 +293,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { } if (try m.match(.{"restart"})) { - _ = try self.mainview.msg(.{"write_restore_info"}); + if (mainview()) |mv| mv.write_restore_info(); project_manager.shutdown(); self.final_exit = "restart"; return; @@ -310,8 +307,8 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { }; if (try m.match(.{"resize"})) { - self.resize(); - const box = self.screen(); + resize(); + const box = screen(); message("{d}x{d}", .{ box.w, box.h }); return; } @@ -391,20 +388,16 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { return self.enter_overlay_mode(@import("mode/overlay/fontface_palette.zig").Type); } - var fontface: []const u8 = undefined; - if (try m.match(.{ "fontface", "current", tp.extract(&fontface) })) { + var fontface_: []const u8 = undefined; + if (try m.match(.{ "fontface", "current", tp.extract(&fontface_) })) { if (self.fontface.len > 0) self.allocator.free(self.fontface); self.fontface = ""; - self.fontface = try self.allocator.dupe(u8, fontface); + self.fontface = try self.allocator.dupe(u8, fontface_); return; } - if (try m.match(.{ "fontface", tp.extract(&fontface) })) { - var fontfaces = if (self.fontfaces) |*p| p else blk: { - self.fontfaces = std.ArrayList([]const u8).init(self.allocator); - break :blk &self.fontfaces.?; - }; - try fontfaces.append(try self.allocator.dupe(u8, fontface)); + if (try m.match(.{ "fontface", tp.extract(&fontface_) })) { + try self.fontfaces.append(self.allocator, try self.allocator.dupe(u8, fontface_)); return; } @@ -427,14 +420,14 @@ fn render(self: *Self) void { { const frame = tracy.initZone(@src(), .{ .name = "tui update" }); defer frame.deinit(); - self.mainview.update(); + if (self.mainview) |mv| mv.update(); } const more = ret: { const frame = tracy.initZone(@src(), .{ .name = "tui render" }); defer frame.deinit(); self.rdr.stdplane().erase(); - break :ret self.mainview.render(&self.theme); + break :ret if (self.mainview) |mv| mv.render(&self.theme) else false; }; { @@ -543,7 +536,7 @@ fn find_coord_widget(self: *Self, y: usize, x: usize) ?*Widget { } }; var ctx: Ctx = .{ .y = y, .x = x }; - _ = self.mainview.walk(&ctx, Ctx.find); + if (self.mainview) |*mv| _ = mv.walk(&ctx, Ctx.find); return ctx.widget; } @@ -560,7 +553,7 @@ fn is_live_widget_ptr(self: *Self, w_: *Widget) bool { } }; var ctx: Ctx = .{ .w = w_ }; - return self.mainview.walk(&ctx, Ctx.find); + return if (self.mainview) |*mv| mv.walk(&ctx, Ctx.find) else false; } fn send_widgets(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool { @@ -569,8 +562,10 @@ fn send_widgets(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool { tp.trace(tp.channel.widget, m); return if (self.keyboard_focus) |w| w.send(from, m) + else if (self.mainview) |mv| + mv.send(from, m) else - self.mainview.send(from, m); + false; } fn send_mouse(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message) tp.result { @@ -623,16 +618,19 @@ fn clear_hover_focus(self: *Self) tp.result { self.hover_focus = null; } -pub fn refresh_hover(self: *Self) void { +pub fn refresh_hover() void { + const self = current(); self.clear_hover_focus() catch return; _ = self.update_hover(self.last_hover_y, self.last_hover_x) catch {}; } -pub fn save_config(self: *const Self) !void { +pub fn save_config() !void { + const self = current(); try root.write_config(self.config, self.allocator); } -pub fn is_mainview_focused(self: *const Self) bool { +pub fn is_mainview_focused() bool { + const self = current(); return self.mini_mode == null and self.input_mode_outer == null; } @@ -643,7 +641,7 @@ fn enter_overlay_mode(self: *Self, mode: type) command.Result { if (self.input_mode_outer) |_| try cmds.exit_overlay_mode(self, .{}); self.input_mode_outer = self.input_mode; self.input_mode = try mode.create(self.allocator); - self.refresh_hover(); + refresh_hover(); } fn get_input_mode(self: *Self, mode_name: []const u8) !Mode { @@ -703,7 +701,7 @@ const cmds = struct { self.config.theme = self.theme.name; self.set_terminal_style(); self.logger.print("theme: {s}", .{self.theme.description}); - try self.save_config(); + try save_config(); } pub const set_theme_meta = .{ .arguments = &.{.string} }; @@ -712,7 +710,7 @@ const cmds = struct { self.config.theme = self.theme.name; self.set_terminal_style(); self.logger.print("theme: {s}", .{self.theme.description}); - try self.save_config(); + try save_config(); } pub const theme_next_meta = .{ .description = "Switch to next color theme" }; @@ -721,7 +719,7 @@ const cmds = struct { self.config.theme = self.theme.name; self.set_terminal_style(); self.logger.print("theme: {s}", .{self.theme.description}); - try self.save_config(); + try save_config(); } pub const theme_prev_meta = .{ .description = "Switch to previous color theme" }; @@ -740,7 +738,7 @@ const cmds = struct { "full" else "none"; - try self.save_config(); + try save_config(); var buf: [32]u8 = undefined; const m = try tp.message.fmtbuf(&buf, .{ "whitespace_mode", self.config.whitespace_mode }); _ = try self.send_widgets(tp.self_pid(), m); @@ -764,7 +762,7 @@ const cmds = struct { found = true; } else try self.allocator.dupe(u8, namespaces[0]); - try self.save_config(); + try save_config(); self.logger.print("input mode {s}", .{self.config.input_mode}); try keybind.set_namespace(self.config.input_mode); return self.refresh_input_mode(); @@ -842,7 +840,7 @@ const cmds = struct { if (self.input_mode) |*mode| mode.deinit(); self.input_mode = self.input_mode_outer; self.input_mode_outer = null; - self.refresh_hover(); + refresh_hover(); } pub const exit_overlay_mode_meta = .{}; @@ -879,13 +877,13 @@ const cmds = struct { fn enter_mini_mode(self: *Self, comptime mode: anytype, ctx: Ctx) !void { command.executeName("disable_fast_scroll", .{}) catch {}; command.executeName("disable_jump_mode", .{}) catch {}; - const input_mode, const mini_mode = try mode.create(self.allocator, ctx); + const input_mode_, const mini_mode_ = try mode.create(self.allocator, ctx); if (self.mini_mode) |_| try exit_mini_mode(self, .{}); if (self.input_mode_outer) |_| try exit_overlay_mode(self, .{}); if (self.input_mode_outer != null) @panic("exit_overlay_mode failed"); self.input_mode_outer = self.input_mode; - self.input_mode = input_mode; - self.mini_mode = mini_mode; + self.input_mode = input_mode_; + self.mini_mode = mini_mode_; } pub fn exit_mini_mode(self: *Self, _: Ctx) Result { @@ -980,12 +978,52 @@ pub const KeybindHints = keybind.KeybindHints; threadlocal var instance_: ?*Self = null; -pub fn current() *Self { +fn current() *Self { return instance_ orelse @panic("tui call out of context"); } +pub fn rdr() *renderer { + return ¤t().rdr; +} + +pub fn message_filters() *MessageFilter.List { + return ¤t().message_filters; +} + +pub fn input_listeners() *EventHandler.List { + return ¤t().input_listeners; +} + +pub fn input_mode() ?*Mode { + return if (current().input_mode) |*p| p else null; +} + +pub fn input_mode_outer() ?*Mode { + return if (current().input_mode_outer) |*p| p else null; +} + +pub fn mini_mode() ?*MiniMode { + return if (current().mini_mode) |*p| p else null; +} + +pub fn config() *const @import("config") { + return ¤t().config; +} + +pub fn config_mut() *@import("config") { + return ¤t().config; +} + +pub fn mainview() ?*MainView { + return if (current().mainview) |*mv| mv.dynamic_cast(MainView) else null; +} + +pub fn mainview_widget() Widget { + return current().mainview orelse @panic("tui main view not found"); +} + pub fn get_active_editor() ?*@import("editor.zig").Editor { - if (current().mainview.dynamic_cast(mainview)) |mv_| if (mv_.get_active_editor()) |editor| + if (mainview()) |mv_| if (mv_.get_active_editor()) |editor| return editor; return null; } @@ -997,7 +1035,7 @@ pub fn get_active_selection(allocator: std.mem.Allocator) ?[]u8 { } pub fn get_buffer_manager() ?*@import("Buffer").Manager { - return if (current().mainview.dynamic_cast(mainview)) |mv_| &mv_.buffer_manager else null; + return if (mainview()) |mv| &mv.buffer_manager else null; } fn context_check() void { @@ -1031,9 +1069,9 @@ pub fn need_render() void { } } -pub fn resize(self: *Self) void { - self.mainview.resize(self.screen()); - self.refresh_hover(); +pub fn resize() void { + mainview_widget().resize(screen()); + refresh_hover(); need_render(); } @@ -1053,24 +1091,36 @@ pub fn egc_last(egcs: []const u8) []const u8 { return plane().egc_last(egcs); } -pub fn screen(self: *Self) Widget.Box { - return Widget.Box.from(self.rdr.stdplane()); +pub fn screen() Widget.Box { + return Widget.Box.from(plane()); +} + +pub fn fontface() []const u8 { + return current().fontface; +} + +pub fn fontfaces(allocator: std.mem.Allocator) error{OutOfMemory}![][]const u8 { + return current().fontfaces.toOwnedSlice(allocator); +} + +pub fn theme() *const Widget.Theme { + return ¤t().theme; } pub fn get_theme_by_name(name: []const u8) ?Widget.Theme { - for (Widget.themes) |theme| { - if (std.mem.eql(u8, theme.name, name)) - return theme; + for (Widget.themes) |theme_| { + if (std.mem.eql(u8, theme_.name, name)) + return theme_; } return null; } pub fn get_next_theme_by_name(name: []const u8) Widget.Theme { var next = false; - for (Widget.themes) |theme| { + for (Widget.themes) |theme_| { if (next) - return theme; - if (std.mem.eql(u8, theme.name, name)) + return theme_; + if (std.mem.eql(u8, theme_.name, name)) next = true; } return Widget.themes[0]; @@ -1078,24 +1128,24 @@ pub fn get_next_theme_by_name(name: []const u8) Widget.Theme { pub fn get_prev_theme_by_name(name: []const u8) Widget.Theme { var prev: ?Widget.Theme = null; - for (Widget.themes) |theme| { - if (std.mem.eql(u8, theme.name, name)) + for (Widget.themes) |theme_| { + if (std.mem.eql(u8, theme_.name, name)) return prev orelse Widget.themes[Widget.themes.len - 1]; - prev = theme; + prev = theme_; } return Widget.themes[Widget.themes.len - 1]; } -pub fn find_scope_style(theme: *const Widget.Theme, scope: []const u8) ?Widget.Theme.Token { +pub fn find_scope_style(theme_: *const Widget.Theme, scope: []const u8) ?Widget.Theme.Token { return if (find_scope_fallback(scope)) |tm_scope| - scope_to_theme_token(theme, tm_scope) orelse - scope_to_theme_token(theme, scope) + scope_to_theme_token(theme_, tm_scope) orelse + scope_to_theme_token(theme_, scope) else - scope_to_theme_token(theme, scope); + scope_to_theme_token(theme_, scope); } -fn scope_to_theme_token(theme: *const Widget.Theme, document_scope: []const u8) ?Widget.Theme.Token { - var idx = theme.tokens.len - 1; +fn scope_to_theme_token(theme_: *const Widget.Theme, document_scope: []const u8) ?Widget.Theme.Token { + var idx = theme_.tokens.len - 1; var matched: ?Widget.Theme.Token = null; var done = false; while (!done) : (if (idx == 0) { @@ -1103,7 +1153,7 @@ fn scope_to_theme_token(theme: *const Widget.Theme, document_scope: []const u8) } else { idx -= 1; }) { - const token = theme.tokens[idx]; + const token = theme_.tokens[idx]; const theme_scope = Widget.scopes[token.id]; const last_matched_scope = if (matched) |tok| Widget.scopes[tok.id] else ""; if (theme_scope.len < last_matched_scope.len) continue; @@ -1178,7 +1228,8 @@ fn set_terminal_style(self: *Self) void { } } -pub fn get_cursor_shape(self: *Self) renderer.CursorShape { +pub fn get_cursor_shape() renderer.CursorShape { + const self = current(); const shape = if (self.input_mode) |mode| mode.cursor_shape orelse self.default_cursor else self.default_cursor; return switch (shape) { .default => .default, @@ -1191,15 +1242,15 @@ pub fn get_cursor_shape(self: *Self) renderer.CursorShape { }; } -pub fn is_cursor_beam(self: *Self) bool { - return switch (self.get_cursor_shape()) { +pub fn is_cursor_beam() bool { + return switch (get_cursor_shape()) { .beam, .beam_blink => true, else => false, }; } -pub fn get_selection_style(self: *Self) @import("Buffer").Selection.Style { - return if (self.input_mode) |mode| mode.selection_style else .normal; +pub fn get_selection_style() @import("Buffer").Selection.Style { + return if (current().input_mode) |mode| mode.selection_style else .normal; } pub fn message(comptime fmt: anytype, args: anytype) void {