diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 09:16:36 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | a9de2b51ab76a43250a4d6c41a0ddd54e6970049 (patch) | |
| tree | a2138ead27f434026c224fd8b926f66b7fd7fe0c /src/pardes.zig | |
| parent | 0d04bc9214cccce42d74862b31376d49bdda3956 (diff) | |
| download | pardes-a9de2b51ab76a43250a4d6c41a0ddd54e6970049.tar.gz pardes-a9de2b51ab76a43250a4d6c41a0ddd54e6970049.zip | |
Let the keyboard reach a tag's path, read-only
The tag's computed path, dirty marker and page were out of the keyboard's
reach: its cursor lived in the text after them. It now lives in the tag as
shown, as in acme where the name is ordinary text at the start of the tag, so
`0` goes to the line's start and motions select and yank the path. The path
stays read-only: an edit that would change it is refused and leaves the
cursor where it was, and typing into a file's path drafts a new name as a
click on it does. Sync moves the tag's positions when the prefix changes
length, so a rename or the dirty marker still never moves the cursor off the
text after it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 35 |
1 files changed, 29 insertions, 6 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index 65f3aebb..321a6b3c 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4976,16 +4976,14 @@ pub const Pardes = struct { if (t.what != .body) { // An explicit selection, else the word under the cursor in the // text as it is shown: a tag's prefix is part of what a word is. - const arena = p.scratch.allocator(); - const own = arena.dupe(u8, edit.flatSurface(p, t) catch return) catch return; - const shown = if (t.what == .tag) tagline.tagText(p, arena, pane) catch return else own; - const range = t.primaryRange(own, 0); + const shown = p.scratch.allocator().dupe(u8, edit.flatSurface(p, t) catch return) catch return; + const range = t.primaryRange(shown, 0); const lo = @min(range.anchor, range.head); const hi = @max(range.anchor, range.head); const txt = if ((t.vsel.active and t.vsel.explicit) or t.msel.active) - own[lo..hi] + shown[lo..hi] else word: { - const b = config.wordBounds(shown, shown.len - own.len + modal.selectionCursor(own, range)); + const b = config.wordBounds(shown, modal.selectionCursor(shown, range)); break :word shown[b.lo..b.hi]; }; // Out of the tag or header FIRST: Del frees the pane, Kill @@ -5013,6 +5011,14 @@ pub const Pardes = struct { return; } if (t.mode == .normal and t.what == .body and pane.hasPdf()) return normal.handlePdfNormal(p, pane, key); + if (t.what == .tag and t.mode == .insert and tagline.typedIntoName(p, pane, key)) return; + // A tag's prefix is read-only: a key whose edit reached into it + // changes nothing, not even where the cursor is. + const kept = .{ t.cur_row, t.cur_col, t.vsel, t.msel, t.sels, t.nsel }; + t.refused = false; + defer if (t.refused) { + t.cur_row, t.cur_col, t.vsel, t.msel, t.sels, t.nsel = kept; + }; switch (t.mode) { .normal => normal.handleNormal(p, t, key), .insert => { @@ -5597,6 +5603,23 @@ pub const Pardes = struct { const r = p.rects[id]; // The tag takes a row per line, leaving the body one row if it // had any; a folded pane shows its first line only. + // Measured on the stack: this runs on every event, and not + // every event may take from the scratch arena. + var room: [2 * limits.host_path_cap + 256]u8 = undefined; + var measure = std.heap.FixedBufferAllocator.init(&room); + const lead = if (tagline.prefixIn(measure.allocator(), pane)) |prefix| prefix.len else |_| pane.tag_lead; + if (lead != pane.tag_lead) { + const t = &pane.tag; + const old: i32 = @intCast(pane.tag_lead); + const new: i32 = @intCast(lead); + if (t.cur_row == 0 and t.cur_col >= old) t.cur_col += new - old; + if (t.vsel.row == 0 and t.vsel.col >= old) t.vsel.col += new - old; + for (t.sels[0..t.nsel]) |*sel| { + if (sel.row == 0 and sel.col >= old) sel.col += new - old; + if (sel.arow == 0 and sel.acol >= old) sel.acol += new - old; + } + pane.tag_lead = lead; + } const lines = std.mem.count(u8, tagline.curTail(pane), "\n") + 1; pane.tag_rows = @intCast(@min(lines, MAX_TAG_ROWS, @max(BOX_H, r.h -| 1))); if (pane.collapsed) pane.tag_rows = BOX_H; |
