summaryrefslogtreecommitdiff
path: root/src/look.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-27 23:06:11 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:14 -0300
commit95a3a1bb822e583988bfe0f312d0ef219b27a454 (patch)
tree95306e8d3f36f8af05b3ea919f6cdef1e94a028b /src/look.zig
parentc8f0c25a854b8e169db08a84d1fdcd915a70aaa0 (diff)
downloadpardes-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.zig106
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);
}
}