From 35be98f95ca999a112ad3aff0932be766f702e13 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 13 Jan 2026 10:35:39 +0100 Subject: [PATCH 1/3] fix: Make absolute Plane writing functions update column correctly Writing to an absolute plane position should update the plane's cursor position before incrementing it even if it is not actually used. closes #446 --- src/renderer/vaxis/Plane.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/renderer/vaxis/Plane.zig b/src/renderer/vaxis/Plane.zig index 3bf9dab..ac02778 100644 --- a/src/renderer/vaxis/Plane.zig +++ b/src/renderer/vaxis/Plane.zig @@ -138,6 +138,7 @@ pub fn fill(self: *Plane, egc: []const u8) void { for (0..self.dim_y()) |y| for (0..self.dim_x()) |x| self.write_cell(x, y, egc); + if (self.col > 100000) std.log.debug("column: {}", .{self.col}); } pub fn fill_width(self: *Plane, comptime fmt: anytype, args: anytype) !usize { @@ -261,6 +262,8 @@ fn write_cell(self: *Plane, col: usize, row: usize, egc: []const u8) void { cell.style = self.style; } self.window.writeCell(@intCast(col), @intCast(row), cell); + self.row = @intCast(row); + self.col = @intCast(col); self.col += @intCast(w); } From 25d5f80a4c6ca005c1e00632dca80602b3405b3b Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 13 Jan 2026 12:34:46 +0100 Subject: [PATCH 2/3] refactor: drop all c_(u)int usage in Plane The use of c_int and c_uint comes from the days when flow supported building agains notcurses has a c API. --- src/buffer/Buffer.zig | 10 +-- src/renderer/vaxis/Plane.zig | 120 ++++++++++++++++---------------- src/renderer/vaxis/renderer.zig | 4 +- src/renderer/win32/renderer.zig | 8 +-- src/tui/editor.zig | 21 +++--- src/tui/status/tabs.zig | 4 +- test/tests_buffer.zig | 2 +- test/tests_helix.zig | 2 +- 8 files changed, 87 insertions(+), 84 deletions(-) diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index 4733e33..a5b2b16 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -25,7 +25,7 @@ pub const Metrics = struct { egc_chunk_width: egc_chunk_width_func, egc_last: egc_last_func, tab_width: usize, - pub const egc_length_func = *const fn (self: Metrics, egcs: []const u8, colcount: *c_int, abs_col: usize) usize; + pub const egc_length_func = *const fn (self: Metrics, egcs: []const u8, colcount: *usize, abs_col: usize) usize; pub const egc_chunk_width_func = *const fn (self: Metrics, chunk_: []const u8, abs_col_: usize) usize; pub const egc_last_func = *const fn (self: Metrics, egcs: []const u8) []const u8; }; @@ -185,7 +185,7 @@ pub const Leaf = struct { fn pos_to_width(self: *const Leaf, pos: *usize, abs_col_: usize, metrics: Metrics) usize { var col: usize = 0; var abs_col = abs_col_; - var cols: c_int = 0; + var cols: usize = 0; var buf = self.buf; while (buf.len > 0 and pos.* > 0) { if (buf[0] == '\t') { @@ -214,7 +214,7 @@ pub const Leaf = struct { inline fn width_to_pos(self: *const Leaf, col_: usize, abs_col_: usize, metrics: Metrics) !usize { var abs_col = abs_col_; var col = col_; - var cols: c_int = 0; + var cols: usize = 0; var buf = self.buf; return while (buf.len > 0) { if (col == 0) @@ -242,7 +242,7 @@ pub const Leaf = struct { } fn debug_render_chunk(chunk: []const u8, l: *std.Io.Writer, metrics: Metrics) !void { - var cols: c_int = 0; + var cols: usize = 0; var buf = chunk; while (buf.len > 0) { switch (buf[0]) { @@ -511,7 +511,7 @@ const Node = union(enum) { const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); var buf: []const u8 = leaf.buf; while (buf.len > 0) { - var cols: c_int = undefined; + var cols: usize = undefined; const bytes = metrics.egc_length(metrics, buf, &cols, ctx.abs_col); const ret = ctx.walker_f(ctx.walker_ctx, buf[0..bytes], @intCast(cols), metrics); if (ret.err) |e| return .{ .err = e }; diff --git a/src/renderer/vaxis/Plane.zig b/src/renderer/vaxis/Plane.zig index ac02778..5cf7aeb 100644 --- a/src/renderer/vaxis/Plane.zig +++ b/src/renderer/vaxis/Plane.zig @@ -78,23 +78,23 @@ pub fn erase(self: Plane) void { self.window.fill(.{ .style = self.style_base }); } -pub inline fn abs_y(self: Plane) c_int { - return @intCast(self.window.y_off); +pub inline fn abs_y(self: Plane) i32 { + return self.window.y_off; } -pub inline fn abs_x(self: Plane) c_int { - return @intCast(self.window.x_off); +pub inline fn abs_x(self: Plane) i32 { + return self.window.x_off; } -pub inline fn dim_y(self: Plane) c_uint { - return @intCast(self.window.height); +pub inline fn dim_y(self: Plane) u31 { + return self.window.height; } -pub inline fn dim_x(self: Plane) c_uint { - return @intCast(self.window.width); +pub inline fn dim_x(self: Plane) u31 { + return self.window.width; } -pub fn abs_yx_to_rel_nearest_x(self: Plane, y: c_int, x: c_int, xoffset: c_int) struct { c_int, c_int } { +pub fn abs_yx_to_rel_nearest_x(self: Plane, y: i32, x: i32, xoffset: i32) struct { i32, i32 } { if (self.window.screen.width == 0 or self.window.screen.height == 0) return self.abs_yx_to_rel(y, x); const xextra = self.window.screen.width_pix % self.window.screen.width; const xcell = (self.window.screen.width_pix - xextra) / self.window.screen.width; @@ -105,26 +105,26 @@ pub fn abs_yx_to_rel_nearest_x(self: Plane, y: c_int, x: c_int, xoffset: c_int) return self.abs_yx_to_rel(y, x); } -pub fn abs_yx_to_rel(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } { +pub fn abs_yx_to_rel(self: Plane, y: i32, x: i32) struct { i32, i32 } { return .{ y - self.abs_y(), x - self.abs_x() }; } -pub fn abs_y_to_rel(self: Plane, y: c_int) c_int { +pub fn abs_y_to_rel(self: Plane, y: i32) i32 { return y - self.abs_y(); } -pub fn rel_yx_to_abs(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } { +pub fn rel_yx_to_abs(self: Plane, y: i32, x: i32) struct { i32, i32 } { return .{ self.abs_y() + y, self.abs_x() + x }; } pub fn hide(_: Plane) void {} -pub fn move_yx(self: *Plane, y: c_int, x: c_int) !void { +pub fn move_yx(self: *Plane, y: i32, x: i32) !void { self.window.y_off = @intCast(y); self.window.x_off = @intCast(x); } -pub fn resize_simple(self: *Plane, ylen: c_uint, xlen: c_uint) !void { +pub fn resize_simple(self: *Plane, ylen: u32, xlen: u32) !void { self.window.height = @intCast(ylen); self.window.width = @intCast(xlen); } @@ -137,7 +137,7 @@ pub fn home(self: *Plane) void { pub fn fill(self: *Plane, egc: []const u8) void { for (0..self.dim_y()) |y| for (0..self.dim_x()) |x| - self.write_cell(x, y, egc); + self.write_cell(@intCast(x), @intCast(y), egc); if (self.col > 100000) std.log.debug("column: {}", .{self.col}); } @@ -160,12 +160,12 @@ pub fn print(self: *Plane, comptime fmt: anytype, args: anytype) !usize { return self.putstr(text); } -pub fn print_aligned_right(self: *Plane, y: c_int, comptime fmt: anytype, args: anytype) !usize { +pub fn print_aligned_right(self: *Plane, y: i32, comptime fmt: anytype, args: anytype) !usize { var buf: [fmt.len + 4096]u8 = undefined; const width = self.window.width; const text = try std.fmt.bufPrint(&buf, fmt, args); const text_width = self.egc_chunk_width(text, 0, 8); - self.row = @intCast(y); + self.row = y; self.col = @intCast(if (text_width >= width) 0 else width - text_width); return self.putstr(text); } @@ -181,12 +181,12 @@ pub fn print_right(self: *Plane, comptime fmt: anytype, args: anytype) !usize { return self.putstr(text); } -pub fn print_aligned_center(self: *Plane, y: c_int, comptime fmt: anytype, args: anytype) !usize { +pub fn print_aligned_center(self: *Plane, y: i32, comptime fmt: anytype, args: anytype) !usize { var buf: [fmt.len + 4096]u8 = undefined; const width = self.window.width; const text = try std.fmt.bufPrint(&buf, fmt, args); const text_width = self.egc_chunk_width(text, 0, 8); - self.row = @intCast(y); + self.row = y; self.col = @intCast(if (text_width >= width) 0 else (width - text_width) / 2); return self.putstr(text); } @@ -213,7 +213,7 @@ pub fn putstr(self: *Plane, text: []const u8) !usize { self.col = 0; } else return result; } - self.write_cell(@intCast(self.col), @intCast(self.row), s); + self.write_cell(self.col, self.row, s); result += 1; } return result; @@ -228,30 +228,30 @@ pub fn putstr_unicode(self: *Plane, text: []const u8) !usize { 0...31 => |code| Buffer.unicode.control_code_to_unicode(code), else => s_, }; - self.write_cell(@intCast(self.col), @intCast(self.row), s); + self.write_cell(self.col, self.row, s); result += 1; } return result; } pub fn putchar(self: *Plane, ecg: []const u8) void { - self.write_cell(@intCast(self.col), @intCast(self.row), ecg); + self.write_cell(self.col, self.row, ecg); } pub fn putc(self: *Plane, cell: *const Cell) !usize { - return self.putc_yx(@intCast(self.row), @intCast(self.col), cell); + return self.putc_yx(self.row, self.col, cell); } -pub fn putc_yx(self: *Plane, y: c_int, x: c_int, cell: *const Cell) !usize { +pub fn putc_yx(self: *Plane, y: i32, x: i32, cell: *const Cell) !usize { try self.cursor_move_yx(y, x); const w = if (cell.cell.char.width == 0) self.window.gwidth(cell.cell.char.grapheme) else cell.cell.char.width; if (w == 0) return w; self.window.writeCell(@intCast(self.col), @intCast(self.row), cell.cell); - self.col += @intCast(w); + self.col += w; return w; } -fn write_cell(self: *Plane, col: usize, row: usize, egc: []const u8) void { +fn write_cell(self: *Plane, col: i32, row: i32, egc: []const u8) void { var cell: vaxis.Cell = self.window.readCell(@intCast(col), @intCast(row)) orelse .{ .style = self.style }; const w = self.window.gwidth(egc); cell.char.grapheme = self.cache.put(egc); @@ -262,41 +262,39 @@ fn write_cell(self: *Plane, col: usize, row: usize, egc: []const u8) void { cell.style = self.style; } self.window.writeCell(@intCast(col), @intCast(row), cell); - self.row = @intCast(row); - self.col = @intCast(col); - self.col += @intCast(w); + self.row = row; + self.col = col; + self.col += w; } -pub fn cursor_yx(self: Plane, y: *c_uint, x: *c_uint) void { - y.* = @intCast(self.row); - x.* = @intCast(self.col); +pub fn cursor_yx(self: Plane, y: *i32, x: *i32) void { + y.* = self.row; + x.* = self.col; } -pub fn cursor_y(self: Plane) c_uint { - return @intCast(self.row); +pub fn cursor_y(self: Plane) i32 { + return self.row; } -pub fn cursor_x(self: Plane) c_uint { - return @intCast(self.col); +pub fn cursor_x(self: Plane) i32 { + return self.col; } -pub fn cursor_move_yx(self: *Plane, y: c_int, x: c_int) !void { +pub fn cursor_move_yx(self: *Plane, y: i32, x: i32) !void { if (self.window.height == 0 or self.window.width == 0) return; if (self.window.height <= y or self.window.width <= x) return; - if (y >= 0) - self.row = @intCast(y); - if (x >= 0) - self.col = @intCast(x); + self.row = y; + self.col = x; } -pub fn cursor_move_rel(self: *Plane, y: c_int, x: c_int) !void { +pub fn cursor_move_rel(self: *Plane, y: i32, x: i32) !void { if (self.window.height == 0 or self.window.width == 0) return error.OutOfBounds; - const new_y: isize = @as(c_int, @intCast(self.row)) + y; - const new_x: isize = @as(c_int, @intCast(self.col)) + x; + const new_y = self.row + y; + const new_x = self.col + x; if (new_y < 0 or new_x < 0) return error.OutOfBounds; if (self.window.height <= new_y or self.window.width <= new_x) return error.OutOfBounds; - self.row = @intCast(new_y); - self.col = @intCast(new_x); + self.row = new_y; + self.col = new_x; } pub fn cell_init(self: Plane) Cell { @@ -304,7 +302,7 @@ pub fn cell_init(self: Plane) Cell { } pub fn cell_load(self: *Plane, cell: *Cell, gcluster: []const u8) !usize { - var cols: c_int = 0; + var cols: usize = 0; const bytes = self.egc_length(gcluster, &cols, 0, 1); cell.cell.char.grapheme = self.cache.put(gcluster[0..bytes]); cell.cell.char.width = @intCast(cols); @@ -357,12 +355,12 @@ pub fn set_bg_rgb_alpha(self: *Plane, alpha_bg: ThemeColor, col: ThemeColor) !vo self.style.bg = apply_alpha_theme(alpha_bg, col); } -pub fn set_fg_palindex(self: *Plane, idx: c_uint) !void { - self.style.fg = .{ .index = @intCast(idx) }; +pub fn set_fg_palindex(self: *Plane, idx: u8) !void { + self.style.fg = .{ .index = idx }; } -pub fn set_bg_palindex(self: *Plane, idx: c_uint) !void { - self.style.bg = .{ .index = @intCast(idx) }; +pub fn set_bg_palindex(self: *Plane, idx: u8) !void { + self.style.bg = .{ .index = idx }; } pub inline fn set_base_style(self: *Plane, style_: Style) void { @@ -446,7 +444,7 @@ inline fn is_control_code(c: u8) bool { }; } -pub fn egc_length(self: *const Plane, egcs: []const u8, colcount: *c_int, abs_col: usize, tab_width: usize) usize { +pub fn egc_length(self: *const Plane, egcs: []const u8, colcount: *usize, abs_col: usize, tab_width: usize) usize { if (egcs.len == 0) { colcount.* = 0; return 0; @@ -456,7 +454,7 @@ pub fn egc_length(self: *const Plane, egcs: []const u8, colcount: *c_int, abs_co return 1; } if (egcs[0] == '\t') { - colcount.* = @intCast(tab_width - (abs_col % tab_width)); + colcount.* = tab_width - (abs_col % tab_width); return 1; } var iter = vaxis.unicode.graphemeIterator(egcs); @@ -466,7 +464,7 @@ pub fn egc_length(self: *const Plane, egcs: []const u8, colcount: *c_int, abs_co }; const s = grapheme.bytes(egcs); const w = self.window.gwidth(s); - colcount.* = @intCast(w); + colcount.* = w; return s.len; } @@ -474,11 +472,11 @@ pub fn egc_chunk_width(self: *const Plane, chunk_: []const u8, abs_col_: usize, var abs_col = abs_col_; var chunk = chunk_; var colcount: usize = 0; - var cols: c_int = 0; + var cols: usize = 0; while (chunk.len > 0) { const bytes = self.egc_length(chunk, &cols, abs_col, tab_width); - colcount += @intCast(cols); - abs_col += @intCast(cols); + colcount += cols; + abs_col += cols; if (chunk.len < bytes) break; chunk = chunk[bytes..]; } @@ -489,11 +487,11 @@ pub fn egc_chunk_col_pos(self: *const Plane, chunk_: []const u8, abs_col_: usize var abs_col = abs_col_; var chunk = chunk_; var colcount: usize = 0; - var cols: c_int = 0; + var cols: usize = 0; while (chunk.len > 0 and colcount < col) { const bytes = self.egc_length(chunk, &cols, abs_col, tab_width); - colcount += @intCast(cols); - abs_col += @intCast(cols); + colcount += cols; + abs_col += cols; if (chunk.len < bytes) break; chunk = chunk[bytes..]; } @@ -511,7 +509,7 @@ pub fn metrics(self: *const Plane, tab_width: usize) Buffer.Metrics { return .{ .ctx = self, .egc_length = struct { - fn f(self_: Buffer.Metrics, egcs: []const u8, colcount: *c_int, abs_col: usize) usize { + fn f(self_: Buffer.Metrics, egcs: []const u8, colcount: *usize, abs_col: usize) usize { const plane: *const Plane = @ptrCast(@alignCast(self_.ctx)); return plane.egc_length(egcs, colcount, abs_col, self_.tab_width); } diff --git a/src/renderer/vaxis/renderer.zig b/src/renderer/vaxis/renderer.zig index fbf6abf..d687333 100644 --- a/src/renderer/vaxis/renderer.zig +++ b/src/renderer/vaxis/renderer.zig @@ -39,8 +39,8 @@ bracketed_paste_buffer: std.Io.Writer.Allocating, handler_ctx: *anyopaque, dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null, -dispatch_mouse: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null, -dispatch_mouse_drag: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null, +dispatch_mouse: ?*const fn (ctx: *anyopaque, y: i32, x: i32, cbor_msg: []const u8) void = null, +dispatch_mouse_drag: ?*const fn (ctx: *anyopaque, y: i32, x: i32, cbor_msg: []const u8) void = null, dispatch_event: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null, logger: log.Logger, diff --git a/src/renderer/win32/renderer.zig b/src/renderer/win32/renderer.zig index 52acfbc..499e1d1 100644 --- a/src/renderer/win32/renderer.zig +++ b/src/renderer/win32/renderer.zig @@ -83,8 +83,8 @@ event_buffer: std.Io.Writer.Allocating, handler_ctx: *anyopaque, dispatch_initialized: *const fn (ctx: *anyopaque) void, dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null, -dispatch_mouse: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null, -dispatch_mouse_drag: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null, +dispatch_mouse: ?*const fn (ctx: *anyopaque, y: i32, x: i32, cbor_msg: []const u8) void = null, +dispatch_mouse_drag: ?*const fn (ctx: *anyopaque, y: i32, x: i32, cbor_msg: []const u8) void = null, dispatch_event: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null, thread: ?std.Thread = null, @@ -473,7 +473,7 @@ pub fn request_mouse_cursor_default(self: *Self, push_or_pop: bool) void { _ = push_or_pop; //@panic("todo"); } -pub fn cursor_enable(self: *Self, y: c_int, x: c_int, shape: CursorShape) !void { +pub fn cursor_enable(self: *Self, y: i32, x: i32, shape: CursorShape) !void { _ = self; _ = y; _ = x; @@ -488,7 +488,7 @@ pub fn clear_all_multi_cursors(self: *Self) !void { _ = self; //@panic("todo"); } -pub fn show_multi_cursor_yx(self: *Self, y: c_int, x: c_int) !void { +pub fn show_multi_cursor_yx(self: *Self, y: i32, x: i32) !void { _ = self; _ = y; _ = x; diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 005887e..63e34cc 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -1361,8 +1361,8 @@ pub const Editor = struct { } fn render_matches(self: *const Self, last_idx: *usize, theme: *const Widget.Theme, cell: *Cell) void { - var y: c_uint = undefined; - var x: c_uint = undefined; + var y: i32 = undefined; + var x: i32 = undefined; self.plane.cursor_yx(&y, &x); while (true) { if (last_idx.* >= self.matches.items.len) @@ -1380,8 +1380,8 @@ pub const Editor = struct { } fn render_selections(self: *const Self, theme: *const Widget.Theme, cell: *Cell) void { - var y: c_uint = undefined; - var x: c_uint = undefined; + var y: i32 = undefined; + var x: i32 = undefined; self.plane.cursor_yx(&y, &x); for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| @@ -1728,7 +1728,9 @@ pub const Editor = struct { return style_cache_lookup(theme, cache, scope, id); } - inline fn is_point_in_selection(self: *const Self, sel_: anytype, y: c_uint, x: c_uint) bool { + inline fn is_point_in_selection(self: *const Self, sel_: anytype, y_: i32, x_: i32) bool { + const y: u32 = if (y_ < 0) return false else @intCast(y_); + const x: u32 = if (x_ < 0) return false else @intCast(x_); const sel = sel_; const row = self.view.row + y; const col = self.view.col + x; @@ -1737,11 +1739,14 @@ pub const Editor = struct { return sel.begin.row <= row and row <= sel.end.row and b_col <= col and col < e_col; } - inline fn is_point_before_selection(self: *const Self, sel_: anytype, y: c_uint, x: c_uint) bool { + inline fn is_point_before_selection(self: *const Self, sel_: anytype, y_: i32, x_: i32) bool { + const y: u32 = if (y_ < 0) return true else @intCast(y_); const sel = sel_; const row = self.view.row + y; + if (row < sel.begin.row) return true; + const x: u32 = if (x_ < 0) return true else @intCast(x_); const col = self.view.col + x; - return row < sel.begin.row or (row == sel.begin.row and col < sel.begin.col); + return row == sel.begin.row and col < sel.begin.col; } inline fn screen_cursor(self: *const Self, cursor: *const Cursor) ?Cursor { @@ -3958,7 +3963,7 @@ pub const Editor = struct { var text_egcs = text; while (text_egcs.len > 0) { - var colcount: c_int = 1; + var colcount: usize = 1; const egc_len = self.metrics.egc_length(self.metrics, text_egcs, &colcount, 0); switch (text_egcs[0]) { '\t' => underlined.writer.writeAll("\t") catch {}, diff --git a/src/tui/status/tabs.zig b/src/tui/status/tabs.zig index 5a77307..4265eba 100644 --- a/src/tui/status/tabs.zig +++ b/src/tui/status/tabs.zig @@ -439,8 +439,8 @@ const Tab = struct { tabbar: *TabBar, buffer_ref: usize, tab_style: *const Style, - close_pos: ?c_uint = null, - save_pos: ?c_uint = null, + close_pos: ?i32 = null, + save_pos: ?i32 = null, on_event: ?EventHandler = null, const Mode = enum { active, inactive, selected }; diff --git a/test/tests_buffer.zig b/test/tests_buffer.zig index a2a8560..03ecb97 100644 --- a/test/tests_buffer.zig +++ b/test/tests_buffer.zig @@ -8,7 +8,7 @@ fn metrics() Buffer.Metrics { return .{ .ctx = undefined, .egc_length = struct { - fn f(_: Buffer.Metrics, _: []const u8, colcount: *c_int, _: usize) usize { + fn f(_: Buffer.Metrics, _: []const u8, colcount: *usize, _: usize) usize { colcount.* = 1; return 1; } diff --git a/test/tests_helix.zig b/test/tests_helix.zig index d74d511..02c3b6e 100644 --- a/test/tests_helix.zig +++ b/test/tests_helix.zig @@ -49,7 +49,7 @@ fn metrics() Buffer.Metrics { return .{ .ctx = undefined, .egc_length = struct { - fn f(_: Buffer.Metrics, _: []const u8, colcount: *c_int, _: usize) usize { + fn f(_: Buffer.Metrics, _: []const u8, colcount: *usize, _: usize) usize { colcount.* = 1; return 1; } From 1e02d978de36976b9983f7f8cb445a41ca4a8f64 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Tue, 13 Jan 2026 12:42:32 +0100 Subject: [PATCH 3/3] fix: render inserted and changed lines the same in the diff gutter The diffing algo is unstable which causes the diff gutter to change a lot while typing. This is a little annoying and not that useful so we'll just render them the same until we get a stable diff algo. --- src/tui/editor_gutter.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tui/editor_gutter.zig b/src/tui/editor_gutter.zig index ae30356..7e655c1 100644 --- a/src/tui/editor_gutter.zig +++ b/src/tui/editor_gutter.zig @@ -259,7 +259,12 @@ inline fn render_diff_symbols(self: *Self, diff_symbols: *[]Symbol, pos: usize, if ((diff_symbols.*)[0].line > linenum) return; const sym = (diff_symbols.*)[0]; - const char = switch (sym.kind) { + const kind: Kind = switch (sym.kind) { + .insert => .insert, + .modified => .insert, //TODO: we map .modified to .insert here because the diff algo is unstable + .delete => .delete, + }; + const char = switch (kind) { .insert => "┃", .modified => "┋", .delete => "▔", @@ -268,7 +273,7 @@ inline fn render_diff_symbols(self: *Self, diff_symbols: *[]Symbol, pos: usize, self.plane.cursor_move_yx(@intCast(pos), @intCast(self.get_width() - 1)) catch return; var cell = self.plane.cell_init(); _ = self.plane.at_cursor_cell(&cell) catch return; - cell.set_style_fg(switch (sym.kind) { + cell.set_style_fg(switch (kind) { .insert => theme.editor_gutter_added, .modified => theme.editor_gutter_modified, .delete => theme.editor_gutter_deleted,