diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-09 23:43:06 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-10 09:17:07 -0300 |
| commit | 0d66575a3c888498c7929e2ec345133628c5d63d (patch) | |
| tree | 3be80051fd505f9675b36f9b570c46a955ca3445 /src/file_pane.zig | |
| parent | 4b5e793f48f6e721c39ab6ddcf755604df73b643 (diff) | |
| download | pardes-0d66575a3c888498c7929e2ec345133628c5d63d.tar.gz pardes-0d66575a3c888498c7929e2ec345133628c5d63d.zip | |
fix tab rendering - waybe wreckless
Diffstat (limited to 'src/file_pane.zig')
| -rw-r--r-- | src/file_pane.zig | 69 |
1 files changed, 56 insertions, 13 deletions
diff --git a/src/file_pane.zig b/src/file_pane.zig index c0a2350d..b0921bd0 100644 --- a/src/file_pane.zig +++ b/src/file_pane.zig @@ -17,6 +17,39 @@ const tracy = @import("tracy.zig"); const SYNTAX_CONTEXT_AFTER_ROWS: usize = 2; +pub fn byteDisplayWidth(byte: u8) usize { + return if (byte == '\t') config.tab_width else 1; +} + +pub fn displayWidth(text: []const u8) usize { + var width: usize = 0; + for (text) |byte| width +|= byteDisplayWidth(byte); + return width; +} + +/// Source byte at a zero-based display column. Every cell occupied by a tab +/// maps back to that one tab byte. +pub fn byteAtDisplay(text: []const u8, display_col: usize) usize { + var col: usize = 0; + for (text, 0..) |byte, i| { + const next = col +| byteDisplayWidth(byte); + if (display_col < next) return i; + col = next; + } + return text.len; +} + +fn fitEnd(text: []const u8, start: usize, width: usize) usize { + var end = start; + var used: usize = 0; + while (end < text.len) : (end += 1) { + const next = used +| byteDisplayWidth(text[end]); + if (next > width) return if (end == start) start + 1 else end; + used = next; + } + return end; +} + pub fn lineCount(content: []const u8) usize { return std.mem.count(u8, content, "\n") + 1; } @@ -258,16 +291,17 @@ fn fillBody(dst: ?[]u8, pane: *Pane, f: *File, width: usize, record_wrap: bool) // panicking — see test/snapshots/badutf.snap. It cannot overflow // the pane either: a UTF-8 sequence is never fewer bytes than the // cells it draws in. - const take = if (width == 0) text.len - at else @min(width, text.len - at); + const end = if (width == 0) text.len else fitEnd(text, at, width); + const take = end - at; const cut = if (pane.hscroll > 0 and width == 0) @min(@as(usize, @intCast(pane.hscroll)), take) else 0; - const shown = text[at + cut .. at + take]; + const shown = text[at + cut .. end]; if (dst) |out| @memcpy(out[written..][0..shown.len], shown); written += shown.len; - if (width > 0 and at + take < text.len) { - at += take; + if (width > 0 and end < text.len) { + at = end; } else { abs += 1; at = 0; @@ -364,16 +398,25 @@ pub fn recolorSyntax(p: *Pardes, pane: *Pane, f: *File, r: pardes.Rect, tx: u16, line.len; } var c: usize = 0; - while (hs + c < limit and config.PREFIX_W + c < tw) : (c += 1) { + var screen_c: usize = 0; + while (hs + c < limit and config.PREFIX_W + screen_c < tw) : (c += 1) { + const cells = byteDisplayWidth(line[hs + c]); const idx = base + hs + c; - if (idx < f.highlight_start) continue; - const hidx = idx - f.highlight_start; - if (hidx >= f.highlights.len) continue; - const ss = synStyle(p, @enumFromInt(f.highlights[hidx])) orelse continue; - const cell = s.at(tx + @as(u16, @intCast(config.PREFIX_W + c)), body_y + vr); - if (cell.default) continue; - cell.style.fg = .{ .rgb = ss.fg }; - cell.style.bold = ss.bold; + if (idx >= f.highlight_start) { + const hidx = idx - f.highlight_start; + if (hidx < f.highlights.len) { + if (synStyle(p, @enumFromInt(f.highlights[hidx]))) |ss| { + var fill: usize = 0; + while (fill < cells and config.PREFIX_W + screen_c + fill < tw) : (fill += 1) { + const cell = s.at(tx + @as(u16, @intCast(config.PREFIX_W + screen_c + fill)), body_y + vr); + if (cell.default) continue; + cell.style.fg = .{ .rgb = ss.fg }; + cell.style.bold = ss.bold; + } + } + } + } + screen_c += cells; } } } |
