diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/pardes.zig | 75 |
1 files changed, 50 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; |
