diff options
| -rw-r--r-- | src/pardes.zig | 75 | ||||
| -rw-r--r-- | test/snapshots/lookloaded.golden | 93 | ||||
| -rw-r--r-- | test/snapshots/lookloaded.snap | 39 |
3 files changed, 182 insertions, 25 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index 9397f445..4750fa0b 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4107,6 +4107,37 @@ pub const Pardes = struct { // ---- the ONE dispatcher: look (right/Enter) and execute (middle/Tab) ---- + /// focus the pane already loaded on `path` (exact match), if any: file + /// panes recenter on a :NN line like the look dedup always has, image + /// panes just focus. Returns false when no pane holds that path. + fn focusPaneByPath(p: *Pardes, path: []const u8, line: usize) bool { + for (p.panes, 0..) |slot, i| { + const tt = slot orelse continue; + if (tt.image) |iv| if (std.mem.eql(u8, iv.path, path)) { + p.active = i; + return true; + }; + const ff = if (tt.file) |*f| f else continue; + if (!std.mem.eql(u8, ff.path, path)) continue; + p.active = i; + if (line > 0 and line <= fileLineCount(ff.content)) { + const next = (line - 1) -| tt.rows / 2; // center, clamp at top + if (next != ff.scroll) { + ff.scroll = next; + ff.syntax_dirty = true; + } + // land the modal cursor on the target line (and keep + // ensureCursorVisible agreeing with the recenter — + // a stale cursor would yank the view right back) + tt.cur_row = @intCast(line - 1); + tt.cur_col = 0; + tt.cur_pinned = true; + } + return true; + } + return false; + } + fn actOnSelection(p: *Pardes, button: Mouse.Button, id: usize, txt: []const u8, arg: ?[]const u8) void { const pane = p.panes[id] orelse return; // image tagline toggles (execute), before the builtin/run dispatch @@ -4135,6 +4166,23 @@ pub const Pardes = struct { @memcpy(cwdbuf[0..c.len], c); break :blk cwdbuf[0..c.len]; }; + // an already-loaded pane wins BEFORE any filesystem resolve: the + // web build has no fs (a look would otherwise be inert even for + // panes sitting in the session), and native gets the same dedup + // it always did, just without touching disk. Pane paths are + // canonical (realpath'd or dump-given), so match the word as-is + // and cwd-joined; anything fancier falls through to resolve. + { + const pl = look.parsePathLine(std.mem.trim(u8, txt, " \t\r\n")); + if (pl.path.len > 0) { + if (p.focusPaneByPath(pl.path, pl.line)) return; + if (pl.path[0] != '/') { + var joinbuf: [2048]u8 = undefined; + if (std.fmt.bufPrint(&joinbuf, "{s}/{s}", .{ cwd, pl.path }) catch null) |j| + if (p.focusPaneByPath(j, pl.line)) return; + } + } + } switch (look.resolve(txt, cwd, &realbuf)) { .none => {}, .url => |u| if (u.len <= 256) p.emit(.{ .open_link = .from(u) }), @@ -4157,25 +4205,7 @@ pub const Pardes = struct { }, .file => |target| { // focus an existing pane on this path (rescrolled), else open - for (p.panes, 0..) |slot, i| { - if (slot) |tt| if (tt.file) |*ff| if (std.mem.eql(u8, ff.path, target.path)) { - p.active = i; - if (target.line > 0 and target.line <= fileLineCount(ff.content)) { - const next = (target.line - 1) -| tt.rows / 2; // center, clamp at top - if (next != ff.scroll) { - ff.scroll = next; - ff.syntax_dirty = true; - } - // land the modal cursor on the target line (and keep - // ensureCursorVisible agreeing with the recenter — - // a stale cursor would yank the view right back) - tt.cur_row = @intCast(target.line - 1); - tt.cur_col = 0; - tt.cur_pinned = true; - } - return; - }; - } + if (p.focusPaneByPath(target.path, target.line)) return; const free = p.freeSlot() orelse return; const nt = p.openFileView(free, target.path, target.line) catch return; p.placeDoc(id, free, nt); @@ -4186,12 +4216,7 @@ pub const Pardes = struct { nt.file.?.scroll -|= @max(1, p.rects[free].h -| BOX_H) / 2; }, .image => |target| { - for (p.panes, 0..) |slot, i| { - if (slot) |tt| if (tt.image) |iv| if (std.mem.eql(u8, iv.path, target.path)) { - p.active = i; - return; - }; - } + if (p.focusPaneByPath(target.path, 0)) return; const free = p.freeSlot() orelse return; // petscii by default when the host has no kitty graphics const nt = p.openImageView(free, target.path, !p.kitty_ok) catch return; diff --git a/test/snapshots/lookloaded.golden b/test/snapshots/lookloaded.golden new file mode 100644 index 00000000..af90b0ef --- /dev/null +++ b/test/snapshots/lookloaded.golden @@ -0,0 +1,93 @@ +== snap file-open grid=100x30 cursor=7,5 +|Kill Newcol Tutor Debug Colors NextColor Dump +| NOR /tmp/pardes-snap/lookloaded/cwd/big.txt Save NOR /tmp/pardes-snap/lookloaded/cwd Del +| 37 line 37 +| 38 line 38 big.txt +| 39 line 39 +| 40 line 40 +| 41 line 41 +|╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ +| NOR /tmp/pardes-snap/lookloaded/cwd Del +| +| big.txt +| +| +| +| big.txt:40 big.txt:10 +| +| NOR /tmp/pardes-snap/lookloaded/cwd Del +| +| big.txt +| +| +| +| +| +| +| +| +| +| +| +== snap removed grid=100x30 cursor=4,10 +|Kill Newcol Tutor Debug Colors NextColor Dump +| NOR /tmp/pardes-snap/lookloaded/cwd/big.txt Save NOR /tmp/pardes-snap/lookloaded/cwd Del +| 37 line 37 +| 38 line 38 big.txt +| 39 line 39 +| 40 line 40 +| 41 line 41 +| 42 line 42 +| NOR /tmp/pardes-snap/lookloaded/cwd Del +| +| big.txt +| +| GONE +| +| big.txt:40 big.txt:10 +| +| NOR /tmp/pardes-snap/lookloaded/cwd Del +| +| big.txt +| +| +| +| +| +| +| +| +| +| +| +== snap look-noFS grid=100x30 cursor=7,5 +|Kill Newcol Tutor Debug Colors NextColor Dump +| NOR /tmp/pardes-snap/lookloaded/cwd/big.txt Save NOR /tmp/pardes-snap/lookloaded/cwd Del +| 7 line 7 +| 8 line 8 big.txt +| 9 line 9 +| 10 line 10 +| 11 line 11 +| 12 line 12 +| NOR /tmp/pardes-snap/lookloaded/cwd Del +| +| big.txt +| +| GONE +| +| big.txt:40 big.txt:10 +| +| NOR /tmp/pardes-snap/lookloaded/cwd Del +| +| big.txt +| +| +| +| +| +| +| +| +| +| +| diff --git a/test/snapshots/lookloaded.snap b/test/snapshots/lookloaded.snap new file mode 100644 index 00000000..03874925 --- /dev/null +++ b/test/snapshots/lookloaded.snap @@ -0,0 +1,39 @@ +# a look matches an already-LOADED pane before touching the filesystem: open +# big.txt:40 from a look, rm the file on disk, then look big.txt:10 — the +# loaded pane focuses and recenters even though realpath now fails. (This +# fs-last ordering is what lets the fs-less web build focus dump panes.) +lines big.txt 60 +start 30 100 -n 3 +wait 8000 big.txt +stable 700 20000 +press left 5 8 +release left 5 8 +stable 400 5000 +key i +text big.txt:40 big.txt:10 +key esc +settle 700 +stable 400 5000 +press right 7 8 +release right 7 8 +stable 700 15000 +snap file-open +# back to the shell: rm the file from disk in tty mode +press left 5 11 +release left 5 11 +stable 400 5000 +key c-b +stable 600 8000 +text rm big.txt; echo GO''NE +key enter +wait 8000 GONE +stable 700 15000 +key c-b +stable 600 8000 +snap removed +# look big.txt:10: nothing on disk, but the pane is loaded — it focuses and +# recenters on line 10 (before this change: realpath fails, look no-ops) +press right 18 15 +release right 18 15 +stable 700 15000 +snap look-noFS |
