diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-27 23:06:11 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | 95a3a1bb822e583988bfe0f312d0ef219b27a454 (patch) | |
| tree | 95306e8d3f36f8af05b3ea919f6cdef1e94a028b /src/look.zig | |
| parent | c8f0c25a854b8e169db08a84d1fdcd915a70aaa0 (diff) | |
| download | pardes-95a3a1bb822e583988bfe0f312d0ef219b27a454.tar.gz pardes-95a3a1bb822e583988bfe0f312d0ef219b27a454.zip | |
Give a pane's body its own Text holding the cursor, selections, mode and undo
acme keeps what edits a text in its Text (dat.h:171-190) and the window
holds a body and a tag of that type. The cursor, the selections, the modal
state and the edit-buffer undo move off Pane into Text.zig, Pane holds
them as its body, and the edit and normal-mode operations take the Text
they edit. Nothing changes in behaviour; this is the step that lets the
tag become a second Text.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/look.zig')
| -rw-r--r-- | src/look.zig | 106 |
1 files changed, 53 insertions, 53 deletions
diff --git a/src/look.zig b/src/look.zig index 1534d332..efc3f312 100644 --- a/src/look.zig +++ b/src/look.zig @@ -482,15 +482,15 @@ pub fn expandedSel(p: *Pardes, pane: *Pane, at: Pane.Sel) ?Pane.Sel { /// the word under the modal cursor as a pane-local selection (paneText /// coords: row 0 is the tag; file panes carry the line-number prefix) pub fn cursorWordSel(p: *Pardes, pane: *Pane) Pane.Sel { - const w = pane.wrapRow(pane.cur_row, pane.cur_col); + const w = pane.wrapRow(pane.body.cur_row, pane.body.cur_col); const vrow = w.row + @as(i32, BOX_H); const vcol = if (pane.file != null) - panes.File.displayOffset(pane, pane.cur_row, w.at, pane.cur_col) + @as(i32, panes.File.gutterWidth(pane)) + panes.File.displayOffset(pane, pane.body.cur_row, w.at, pane.body.cur_col) + @as(i32, panes.File.gutterWidth(pane)) else blk: { - const lines = edit.paneCursorLines(p, pane) catch break :blk pane.cur_col; - const local = pane.cur_row; - if (local < 0 or @as(usize, @intCast(local)) >= lines.len) break :blk pane.cur_col; - break :blk panes.File.lineDisplayOffset(lines[@intCast(local)], @intCast(@max(0, w.at)), @intCast(@max(0, pane.cur_col))); + const lines = edit.paneCursorLines(p, &pane.body) catch break :blk pane.body.cur_col; + const local = pane.body.cur_row; + if (local < 0 or @as(usize, @intCast(local)) >= lines.len) break :blk pane.body.cur_col; + break :blk panes.File.lineDisplayOffset(lines[@intCast(local)], @intCast(@max(0, w.at)), @intCast(@max(0, pane.body.cur_col))); }; return .{ .state = .done, .c0 = vcol, .c1 = vcol, .r0 = vrow, .r1 = vrow }; } @@ -526,9 +526,9 @@ fn lookFirstHit(p: *Pardes, id: usize) void { if (p.active != rid) return; // walked out of the list; leave it there const txt = edit.currentSelText(p, rp) orelse return; // spent, exactly as the look chord spends it (handleKey) - rp.vsel.active = false; - rp.msel.active = false; - rp.select = false; + rp.body.vsel.active = false; + rp.body.msel.active = false; + rp.body.select = false; exec.runBuiltin(p, config.look_cmd, rid, "", txt); } @@ -587,7 +587,7 @@ pub fn runSearch(p: *Pardes, id: usize, pat_raw: []const u8, kind: Search, start anchor = found.anchor; } } else { - const lines = try edit.paneCursorLines(p, pane); + const lines = try edit.paneCursorLines(p, &pane.body); // a real file names itself; a terminal or an output buffer has no path const has_path = if (pane.file) |f| f.output == null else false; var idbuf: [16]u8 = undefined; @@ -597,8 +597,8 @@ pub fn runSearch(p: *Pardes, id: usize, pat_raw: []const u8, kind: Search, start std.fs.path.basename(pane.file.?.path) else std.fmt.bufPrint(&idbuf, config.pane_addr ++ "{d}", .{id}) catch return error.PathTooLong; - const cl: usize = @intCast(@max(0, pane.cur_row)); - const cc: usize = @intCast(@max(0, pane.cur_col)); + const cl: usize = @intCast(@max(0, pane.body.cur_row)); + const cc: usize = @intCast(@max(0, pane.body.cur_col)); for (lines, 0..) |ln, i| { const at = std.ascii.indexOfIgnoreCase(ln, pat) orelse continue; const row = try std.fmt.allocPrint(arena, "{s}:{d}:{d}{c}{d} {s}\n", .{ @@ -656,11 +656,11 @@ pub fn jumpResult(p: *Pardes, id: usize, row: usize) bool { if (ln.len == 0) return false; const r: i32 = @intCast(row); pane.search_row = row; - rp.msel = .{ .active = true, .r0 = r, .r1 = r }; - rp.vsel.active = false; - rp.nsel = 0; - rp.cur_row = r; - rp.cur_pinned = true; + rp.body.msel = .{ .active = true, .r0 = r, .r1 = r }; + rp.body.vsel.active = false; + rp.body.nsel = 0; + rp.body.cur_row = r; + rp.body.cur_pinned = true; const off = rp.scroll(); const last = off + @as(i32, rp.rows) - 1; if (r < off) rp.scrollBy(r - off) else if (r > last) rp.scrollBy(r - last); @@ -676,14 +676,14 @@ pub fn jumpResult(p: *Pardes, id: usize, row: usize) bool { null; }; if (span) |selected| { - rp.cur_col = @intCast(selected.start); + rp.body.cur_col = @intCast(selected.start); rp.look_at = .{ .row = r, .col0 = @intCast(selected.start), .col1 = @intCast(selected.end - 1), }; } else { - rp.cur_col = 0; + rp.body.cur_col = 0; rp.look_at = null; } if (tr.commands) { @@ -692,7 +692,7 @@ pub fn jumpResult(p: *Pardes, id: usize, row: usize) bool { lookAt(p, rid, ln[selected.start..selected.end]); // This is the newly visited result, not a selection carried into // the destination. Look cleared the previous source selection. - rp.msel = .{ .active = true, .r0 = r, .r1 = r }; + rp.body.msel = .{ .active = true, .r0 = r, .r1 = r }; } armLookWalk(p, rid); return true; @@ -705,9 +705,9 @@ fn lookEdge(delta: i32) LookFrom { } fn lookStand(pane: *Pane) LookFrom { - if (pane.look_at) |s| if (s.row == pane.cur_row and s.col0 == pane.cur_col) + if (pane.look_at) |s| if (s.row == pane.body.cur_row and s.col0 == pane.body.cur_col) return .{ .row = s.row, .col = s.col0, .strict = true }; - return .{ .row = pane.cur_row, .col = pane.cur_col }; + return .{ .row = pane.body.cur_row, .col = pane.body.cur_col }; } fn lookWalkPanes(p: *Pardes, out: *[MAX_PANES]usize) []const usize { @@ -759,7 +759,7 @@ fn wholeRowSpan(ln: []const u8) ?Span { } fn lookSpanIn(p: *Pardes, pane: *Pane, from: LookFrom, delta: i32, budget: *usize) ?Pane.LookSpot { - const lines = edit.paneCursorLines(p, pane) catch return null; + const lines = edit.paneCursorLines(p, &pane.body) catch return null; const nrows: i32 = @intCast(lines.len); if (nrows == 0) return null; const grain: panes.Output.Grain = if (pane.file) |*f| panes.Output.grain(f.output) else .word; @@ -835,13 +835,13 @@ pub fn lookWalk(p: *Pardes, delta: i32) void { } fn landLookSpot(p: *Pardes, id: usize, pane: *Pane, spot: Pane.LookSpot) void { - edit.pinPaneCursor(p, pane); // fresh out of tty mode the cursor still tracks the shell - pane.vsel = .{ .active = true, .row = spot.row, .col = spot.col1, .explicit = true }; - pane.msel.active = false; - pane.nsel = 0; - pane.cur_row = spot.row; - pane.cur_col = spot.col0; - pane.cur_pinned = true; + edit.pinPaneCursor(p, &pane.body); // fresh out of tty mode the cursor still tracks the shell + pane.body.vsel = .{ .active = true, .row = spot.row, .col = spot.col1, .explicit = true }; + pane.body.msel.active = false; + pane.body.nsel = 0; + pane.body.cur_row = spot.row; + pane.body.cur_col = spot.col0; + pane.body.cur_pinned = true; pane.look_at = spot; p.look_walk_owner = pane.serial; p.active = id; @@ -944,7 +944,7 @@ pub fn focusPaneLine(p: *Pardes, id: usize, at: Spot, landing: enum { center, ke if (id >= MAX_PANES) return; const pane = p.panes[id] orelse return; p.active = id; - pane.normal.clear(); + pane.body.normal.clear(); if (pane.hasPdf()) { if (landing == .keep) return; if (comptime pdf_enabled) { @@ -964,21 +964,21 @@ pub fn focusPaneLine(p: *Pardes, id: usize, at: Spot, landing: enum { center, ke } } } - pane.cur_row = @intCast(at.line - 1); - pane.cur_col = if (at.col > 0) @intCast(at.col - 1) else 0; - pane.cur_pinned = true; + pane.body.cur_row = @intCast(at.line - 1); + pane.body.cur_col = if (at.col > 0) @intCast(at.col - 1) else 0; + pane.body.cur_pinned = true; if (at.end_line != 0) return selectSpan(p, pane, at); pane.ensureCursorVisible(); } fn selectSpan(p: *Pardes, pane: *Pane, at: Spot) void { - const text = edit.flatSurface(p, pane) catch return; + const text = edit.flatSurface(p, &pane.body) catch return; // saturating, not `- 1`: `f.zig:0-5` is a legal thing to type and a // 1-based zero is the same nothing an absent number is const acol: i32 = @intCast(at.col -| 1); const ecol: i32 = if (at.end_col > 0) @intCast(at.end_col - 1) else std.math.maxInt(i32); - const r = Pane.cellRange(text, @intCast(at.line -| 1), acol, @intCast(at.end_line -| 1), ecol); - pane.setRange(text, 0, r, true); + const r = panes.Text.cellRange(text, @intCast(at.line -| 1), acol, @intCast(at.end_line -| 1), ecol); + pane.body.setRange(text, 0, r, true); } fn openPaneTarget(p: *const Pardes, path: []const u8, at: Spot) ?Target { @@ -1006,13 +1006,13 @@ fn focusPaneByPath(p: *Pardes, path: []const u8, at: Spot) bool { /// Navigation starts a new selection at the destination. pub fn clearNavigationSelection(p: *Pardes, pane: *Pane) void { - pane.vsel = .{}; - pane.msel = .{}; - pane.nsel = 0; - pane.select = false; - pane.normal.clear(); - pane.append_at = null; - pane.sticky_col = -1; + pane.body.vsel = .{}; + pane.body.msel = .{}; + pane.body.nsel = 0; + pane.body.select = false; + pane.body.normal.clear(); + pane.body.append_at = null; + pane.body.sticky_col = -1; for (0..pane.sel.len) |slot| pane.clearPointerSelection(slot); pane.sel = @splat(.{}); if (comptime pdf_enabled) if (pane.pdf) |*state| { @@ -1150,7 +1150,7 @@ pub fn lookAt(p: *Pardes, id: usize, operand: []const u8) void { const found = resolveLookTarget(p, id, txt, &realbuf); switch (found) { .none => { - const bmode = if (pane.tag_edit) pane.tag_mode else pane.mode; + const bmode = if (pane.tag_edit) pane.tag_mode else pane.body.mode; if (pane.image != null or bmode == .tty) return; runSearch(p, id, trimmed, .text, .cursor) catch |err| { p.reportError(id, "search", err); @@ -1203,7 +1203,7 @@ pub fn lookAt(p: *Pardes, id: usize, operand: []const u8) void { const free = p.freeSlot() orelse return p.reportError(id, "look", error.NoPaneSlots); const nt = panes.File.open(p, free, target.path, target.at.line) catch |err| return p.reportError(id, "look", err); - if (target.at.col > 0) nt.cur_col = @intCast(target.at.col - 1); + if (target.at.col > 0) nt.body.cur_col = @intCast(target.at.col - 1); exec.placeDoc(p, id, free, nt); layout.compute(p); nt.file.?.scroll -|= @max(1, p.rects[free].h -| BOX_H) / 2; @@ -1249,7 +1249,7 @@ test "Look hover waits without mutating the pane and input cancels it" { const selections_before = pane.sel; const active_before = p.active; - const cursor_before = .{ pane.cur_row, pane.cur_col }; + const cursor_before = .{ pane.body.cur_row, pane.body.cur_col }; const jumps_before = .{ p.njumps, p.jcur, p.n_look_src }; const effects_before = p.effects_len; @@ -1276,7 +1276,7 @@ test "Look hover waits without mutating the pane and input cancels it" { // Resolving and displaying the operand is observational only. try std.testing.expect(std.meta.eql(selections_before, pane.sel)); try std.testing.expectEqual(active_before, p.active); - try std.testing.expectEqual(cursor_before, .{ pane.cur_row, pane.cur_col }); + try std.testing.expectEqual(cursor_before, .{ pane.body.cur_row, pane.body.cur_col }); try std.testing.expectEqual(jumps_before, .{ p.njumps, p.jcur, p.n_look_src }); try std.testing.expectEqual(effects_before, p.effects_len); @@ -1436,8 +1436,8 @@ test "Look copies its operand before releasing pointer selection storage" { }; p.drag = .{ .select = .{ .id = 0, .button = config.select_button } }; lookAt(p, 0, operand); - try std.testing.expectEqual(@as(i32, 2), pane.cur_row); - try std.testing.expectEqual(@as(i32, 1), pane.cur_col); + try std.testing.expectEqual(@as(i32, 2), pane.body.cur_row); + try std.testing.expectEqual(@as(i32, 1), pane.body.cur_col); try std.testing.expect(pane.pointer_selections[0] == null); try std.testing.expectEqual(.none, pane.sel[0].state); try std.testing.expectEqual(.none, p.drag); @@ -1448,10 +1448,10 @@ test "Look ignores missing and out of bounds pane addresses" { defer p.deinit(); const pane = try p.setTestFile("alpha\nbravo\n"); for ([_][]const u8{ config.pane_addr ++ "15:2", config.pane_addr ++ "999999:2" }) |operand| { - pane.setRange(pane.file.?.content, 0, .{ .anchor = 0, .head = 3 }, true); + pane.body.setRange(pane.file.?.content, 0, .{ .anchor = 0, .head = 3 }, true); lookAt(p, 0, operand); try std.testing.expectEqual(@as(usize, 0), p.active); - try std.testing.expectEqual(@as(i32, 0), pane.cur_row); - try std.testing.expect(!pane.vsel.active); + try std.testing.expectEqual(@as(i32, 0), pane.body.cur_row); + try std.testing.expect(!pane.body.vsel.active); } } |
