diff options
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 54 |
1 files changed, 46 insertions, 8 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index ebc54426..32a58f89 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -284,7 +284,7 @@ pub const Surface = struct { pub const Mouse = struct { // sel indexing relies on left/middle/right being 0/1/2 - pub const Button = enum { left, middle, right, wheel_up, wheel_down, none }; + pub const Button = enum { left, middle, right, wheel_up, wheel_down, wheel_left, wheel_right, none }; pub const Kind = enum { press, release, motion, drag }; button: Button, kind: Kind, @@ -498,6 +498,12 @@ pub const Pane = struct { cur_pinned: bool = false, cur_row: i32 = 0, cur_col: i32 = 0, + /// horizontal scroll, file panes only (terminals wrap at pty width, they + /// never have wider lines): content columns hidden left of the gutter. + /// No scrollbar — the wheel and cursor movement (with scrolloff) drive it, + /// the goal is just being able to read long lines. Byte columns, like the + /// rest of the file-pane code. + hscroll: i32 = 0, cur_off: u16 = 0, sel: [3]Sel = @splat(.{}), edits: [MAX_EDITS]Edit = @splat(.{}), @@ -552,6 +558,17 @@ pub const Pane = struct { if (pane.file) |f| to = @min(to, @as(i32, @intCast(modal.lineCount(f.content) -| 1))); pane.scrollBy(@max(0, to - last)); } + // the horizontal mirror, files only: keep scroll_off columns of + // context around the cursor (wheel-driven hscroll is exempt — it + // never moves the cursor, and a cursor move pulls the view back) + if (pane.file != null) { + const w: i32 = @max(1, @as(i32, pane.cols) - @as(i32, PREFIX_W)); + const hmargin: i32 = @min(scroll_off, @divTrunc(w - 1, 2)); + if (pane.cur_col < pane.hscroll + hmargin) + pane.hscroll = @max(0, pane.cur_col - hmargin) + else if (pane.cur_col > pane.hscroll + w - 1 - hmargin) + pane.hscroll = pane.cur_col - (w - 1 - hmargin); + } } /// the last navigable row: file -> last content line; terminal -> viewport bottom @@ -1172,7 +1189,7 @@ pub const Pardes = struct { /// coords: row 0 is the tag; file panes carry the line-number prefix) fn cursorWordSel(pane: *Pane) Sel { const vrow = (pane.cur_row - pane.scroll()) + @as(i32, BOX_H); - const vcol = if (pane.file != null) pane.cur_col + @as(i32, PREFIX_W) else pane.cur_col; + const vcol = if (pane.file != null) pane.cur_col - pane.hscroll + @as(i32, PREFIX_W) else pane.cur_col; return .{ .state = .done, .c0 = vcol, .c1 = vcol, .r0 = vrow, .r1 = vrow }; } @@ -2368,6 +2385,15 @@ pub const Pardes = struct { const id = hovered orelse return; p.panes[id].?.scrollBy(if (m.button == .wheel_up) -1 else 1); }, + .wheel_left, .wheel_right => { + if (m.kind != .press) return; + const id = hovered orelse return; + const pane = p.panes[id].?; + // ponytail: no right clamp — overscroll shows blank and the + // next cursor move or left wheel pulls it back + if (pane.file != null) + pane.hscroll = @max(0, pane.hscroll + @as(i32, if (m.button == .wheel_right) 4 else -4)); + }, .left => switch (m.kind) { .press => { // middle+left chord: a left press during a middle select- @@ -2560,7 +2586,7 @@ pub const Pardes = struct { const body_vis = sl.r1 - @as(i32, BOX_H); if (body_vis >= 0 and pane.mode != .tty) { pane.cur_row = body_vis + pane.scroll(); - pane.cur_col = if (pane.file != null) @max(0, sl.c1 - @as(i32, PREFIX_W)) else sl.c1; + pane.cur_col = if (pane.file != null) @max(0, sl.c1 - @as(i32, PREFIX_W)) + pane.hscroll else sl.c1; pane.cur_off = if (pane.runAt(pane.cur_row, pane.cur_col)) |i| pane.edits[i].len else 0; pane.cur_pinned = true; pane.mode = .normal; @@ -3602,13 +3628,14 @@ pub const Pardes = struct { while (skip < f.scroll) : (skip += 1) { if (flines.next() == null) break; } + const hs: usize = @intCast(@max(0, pane.hscroll)); var vr: u16 = 0; while (vr < body_h) : (vr += 1) { const line = flines.next() orelse break; const base = @intFromPtr(line.ptr) - @intFromPtr(f.content.ptr); var c: usize = 0; - while (c < line.len and PREFIX_W + c < tw) : (c += 1) { - const idx = base + c; + while (hs + c < line.len and PREFIX_W + c < tw) : (c += 1) { + const idx = base + hs + c; if (idx < f.highlight_start) continue; const hidx = idx - f.highlight_start; if (hidx >= f.highlights.len) continue; @@ -3697,12 +3724,13 @@ pub const Pardes = struct { if (pane.mode == .normal and pane.vsel.active) { const bnd = vselBounds(pane); const vpfx: i32 = if (pane.file != null) PREFIX_W else 0; + const vhs: i32 = if (pane.file != null) pane.hscroll else 0; var ar: i32 = bnd.lo_row; while (ar <= bnd.hi_row) : (ar += 1) { const prow = ar - off + @as(i32, BOX_H); if (prow < 0 or prow >= @as(i32, r.h)) continue; - const cstart: i32 = if (ar == bnd.lo_row) bnd.lo_col + vpfx else vpfx; - const cend: i32 = if (ar == bnd.hi_row) bnd.hi_col + vpfx else @as(i32, tw) - 1; + const cstart: i32 = if (ar == bnd.lo_row) bnd.lo_col - vhs + vpfx else vpfx; + const cend: i32 = if (ar == bnd.hi_row) bnd.hi_col - vhs + vpfx else @as(i32, tw) - 1; var col: i32 = @max(cstart, vpfx); while (col <= cend and col < tw) : (col += 1) { const cell = s.at(tx + @as(u16, @intCast(col)), r.y + @as(u16, @intCast(prow))); @@ -3726,7 +3754,7 @@ pub const Pardes = struct { // insert mode maps the run anchor to its visual column; normal // mode keeps cur_col already-visual. const cx = if (pane.file != null) - @as(i32, PREFIX_W) + ccol + @as(i32, coff) + @as(i32, PREFIX_W) + ccol + @as(i32, coff) - pane.hscroll else if (pane.mode == .insert) pane.renderedCol(crow, ccol, coff) else @@ -3861,6 +3889,7 @@ pub const Pardes = struct { while (i < pane.rows) : (i += 1) { if (i > 0) try out.append(arena, '\n'); const abs: i32 = @as(i32, @intCast(f.scroll)) + @as(i32, @intCast(i)); + const row_start = out.items.len; if (flines.next()) |text| { var lbuf: [16]u8 = undefined; // unsigned: {d} prints a leading '+' for signed ints @@ -3871,6 +3900,15 @@ pub const Pardes = struct { try base.appendSlice(arena, text); try spliceRow(arena, &out, pane, abs, base.items); } else try spliceRow(arena, &out, pane, abs, ""); + // horizontal scroll: cut hscroll columns after the gutter, + // from the fully composed row (edit splices shift with it) + if (pane.hscroll > 0) { + const cut_at = row_start + PREFIX_W; + if (out.items.len > cut_at) { + const cut = @min(@as(usize, @intCast(pane.hscroll)), out.items.len - cut_at); + out.replaceRangeAssumeCapacity(cut_at, cut, &.{}); + } + } } return out.items; } |
