From 182011059d66dd0eb020f6f7f442f91fb7ec791c Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 28 Dec 2025 20:54:24 +0100 Subject: [PATCH 1/7] refactor: add explicit error types to Editor.buf_* functions --- src/tui/editor.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index c41b6d9..9d3a5b0 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -627,7 +627,7 @@ pub const Editor = struct { Widget.need_render(); } - pub fn buf_for_update(self: *Self) !*const Buffer { + pub fn buf_for_update(self: *Self) error{ Stop, OutOfMemory }!*const Buffer { if (!self.pause_undo) { self.cursels_saved.clearAndFree(self.allocator); self.cursels_saved = try self.cursels.clone(self.allocator); @@ -635,19 +635,19 @@ pub const Editor = struct { return self.buffer orelse error.Stop; } - pub fn buf_root(self: *const Self) !Buffer.Root { + pub fn buf_root(self: *const Self) error{Stop}!Buffer.Root { return if (self.buffer) |p| p.root else error.Stop; } - fn buf_eol_mode(self: *const Self) !Buffer.EolMode { + fn buf_eol_mode(self: *const Self) error{Stop}!Buffer.EolMode { return if (self.buffer) |p| p.file_eol_mode else error.Stop; } - fn buf_utf8_sanitized(self: *const Self) !bool { + fn buf_utf8_sanitized(self: *const Self) error{Stop}!bool { return if (self.buffer) |p| p.file_utf8_sanitized else error.Stop; } - fn buf_a(self: *const Self) !Allocator { + fn buf_a(self: *const Self) error{Stop}!Allocator { return if (self.buffer) |p| p.allocator else error.Stop; } From 7f88381eb02f96023c58838b272f018f0f525d33 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 28 Dec 2025 20:55:14 +0100 Subject: [PATCH 2/7] refactor: prefer Selection.is_reversed to Cursor.right_of --- src/tui/editor.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 9d3a5b0..8117c67 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -2652,7 +2652,7 @@ pub const Editor = struct { switch (self.selection_mode) { .char => {}, .word => { - if (sel.begin.right_of(sel.end)) { + if (sel.is_reversed()) { sel.begin = initial.end; with_selection_const(root, move_cursor_word_begin, primary, self.metrics) catch {}; } else { @@ -2661,7 +2661,7 @@ pub const Editor = struct { } }, .line => { - if (sel.begin.right_of(sel.end)) { + if (sel.is_reversed()) { sel.begin = initial.end; with_selection_const(root, move_cursor_begin, primary, self.metrics) catch {}; } else { From 38948cf492807416e85dfd03cfe8bf324cacd3ba Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 28 Dec 2025 20:55:47 +0100 Subject: [PATCH 3/7] fix: prefer "same file" error over "save as would overwrite unsaved changes" in save_as --- src/tui/mainview.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index f77291e..a68e0cd 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -785,10 +785,10 @@ const cmds = struct { var existing = false; if (self.buffer_manager.get_buffer_for_file(file_path)) |new_buffer| { - if (new_buffer.is_dirty()) - return tp.exit("save as would overwrite unsaved changes"); if (buffer == new_buffer) return tp.exit("same file"); + if (new_buffer.is_dirty()) + return tp.exit("save as would overwrite unsaved changes"); existing = true; } try self.create_editor(); From e23d35b8c7d1c60fc420b0cc5b95663f12b2fae7 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 28 Dec 2025 21:17:30 +0100 Subject: [PATCH 4/7] refactor: simplify Editor.select_word_at_cursor --- src/tui/editor.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 8117c67..a144c43 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -2840,8 +2840,8 @@ pub const Editor = struct { fn copy_word_at_cursor(self: *Self, text_allocator: Allocator) ![]const u8 { const root = try self.buf_root(); const primary = self.get_primary(); - const sel = if (primary.selection) |*sel| sel else try self.select_word_at_cursor(primary); - return try copy_selection(root, sel.*, text_allocator, self.metrics); + const sel = if (primary.selection) |sel| sel else try self.select_word_at_cursor(primary); + return try copy_selection(root, sel, text_allocator, self.metrics); } pub fn cut_selection(self: *Self, root: Buffer.Root, cursel: *CurSel, text_allocator: Allocator) !struct { []const u8, Buffer.Root } { @@ -4531,7 +4531,7 @@ pub const Editor = struct { } pub const select_all_meta: Meta = .{ .description = "Select all" }; - fn select_word_at_cursor(self: *Self, cursel: *CurSel) !*Selection { + fn select_word_at_cursor(self: *Self, cursel: *CurSel) error{Stop}!Selection { const root = try self.buf_root(); const sel = cursel.enable_selection(root, self.metrics); defer cursel.check_selection(root, self.metrics); @@ -4539,7 +4539,7 @@ pub const Editor = struct { try move_cursor_word_begin(root, &sel.begin, self.metrics); move_cursor_word_end(root, &sel.end, self.metrics) catch {}; cursel.cursor = sel.end; - return sel; + return sel.*; } pub fn select_line_at_cursor(self: *Self, root: Buffer.Root, cursel: *CurSel, mode: enum { include_eol, exclude_eol, hold_cursor }) !void { From 3de4a471233fc5e4c0801cb4af233d9812b0d9f3 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 28 Dec 2025 21:18:18 +0100 Subject: [PATCH 5/7] fix: don't allow double click to fail if select_word_at_cursor fails --- src/tui/editor.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index a144c43..95fbd82 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -2623,8 +2623,9 @@ pub const Editor = struct { primary.disable_selection(root, self.metrics); self.selection_mode = .word; primary.cursor.move_abs(root, &self.view, @intCast(y), @intCast(x), self.metrics) catch return; - _ = try self.select_word_at_cursor(primary); - self.selection_drag_initial = primary.selection; + self.selection_drag_initial = self.select_word_at_cursor(primary) catch |e| switch (e) { + error.Stop => primary.to_selection_normal(), + }; self.collapse_cursors(); self.clamp_mouse(); } From 1685b3204c3edcc9d9ea146cb06fb173743b4b6c Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 28 Dec 2025 21:18:46 +0100 Subject: [PATCH 6/7] fix: don't allow triple click to fail if select_line_at_cursor fails --- src/tui/editor.zig | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 95fbd82..f4be723 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -2636,8 +2636,15 @@ pub const Editor = struct { primary.disable_selection(root, self.metrics); self.selection_mode = .line; primary.cursor.move_abs(root, &self.view, @intCast(y), @intCast(x), self.metrics) catch return; - try self.select_line_at_cursor(root, primary, .exclude_eol); - self.selection_drag_initial = primary.selection; + blk: { + self.select_line_at_cursor(root, primary, .exclude_eol) catch |e| switch (e) { + error.Stop => { + self.selection_drag_initial = primary.to_selection_normal(); + break :blk; + }, + }; + self.selection_drag_initial = primary.selection; + } self.collapse_cursors(); self.clamp_mouse(); } From 1be41aff8b66c7e69d76b235dcf01d656de8a190 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sun, 28 Dec 2025 21:20:58 +0100 Subject: [PATCH 7/7] fix: don't use with_selection_const in primary_drag with_selection_const destroys the selection on movement errors. closes #406 --- src/tui/editor.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tui/editor.zig b/src/tui/editor.zig index f4be723..305a3e6 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -2662,21 +2662,21 @@ pub const Editor = struct { .word => { if (sel.is_reversed()) { sel.begin = initial.end; - with_selection_const(root, move_cursor_word_begin, primary, self.metrics) catch {}; + move_cursor_word_begin(root, &sel.end, self.metrics) catch {}; } else { sel.begin = initial.begin; - with_selection_const(root, move_cursor_word_end, primary, self.metrics) catch {}; + move_cursor_word_end(root, &sel.end, self.metrics) catch {}; } }, .line => { if (sel.is_reversed()) { sel.begin = initial.end; - with_selection_const(root, move_cursor_begin, primary, self.metrics) catch {}; + move_cursor_begin(root, &sel.end, self.metrics) catch {}; } else { sel.begin = initial.begin; blk: { - with_selection_const(root, move_cursor_end, primary, self.metrics) catch break :blk; - with_selection_const(root, move_cursor_right, primary, self.metrics) catch {}; + move_cursor_end(root, &sel.end, self.metrics) catch break :blk; + move_cursor_right(root, &sel.end, self.metrics) catch {}; } } },