diff options
| author | Gabriel Schneider <[email protected]> | 2026-07-27 10:20:19 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-01 15:02:07 -0300 |
| commit | c79612b523c1526202335302e1e49c078ca317cf (patch) | |
| tree | b76d7b0e15f3b0e43ba65f9ea9eb57ee4a8ed6e1 /src/pardes.zig | |
| parent | f0e498832a1a4f92d5fdbc3fc643a3dba1dd9df9 (diff) | |
| download | pardes-c79612b523c1526202335302e1e49c078ca317cf.tar.gz pardes-c79612b523c1526202335302e1e49c078ca317cf.zip | |
look resolves against every open pane's directory: clicked pane first, then most-recently-focused, search only when all fail
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 67 |
1 files changed, 40 insertions, 27 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index 017892aa..d3376273 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -2551,20 +2551,17 @@ pub const Pardes = struct { return true; } - /// n/N on a terminal pane (normal mode): select the next/previous output - /// row whose leading file-ish word looks somewhere — resolves to a - /// file/dir/path:NNN exactly as a right click would — and look it. The - /// scan only resolves (realpath, no pane opens, no scrolls); the one - /// chosen row gets the real look. The modal cursor is the position, so - /// the walk continues from wherever j/k left it. The look may focus what - /// it opened; focus comes back here so the next n keeps stepping. /// n/N on a terminal pane: a MOTION over lookable tokens. Select the /// next/prev whitespace-separated token that look.resolve can turn into a /// file/dir (several per line: an ls row hops big.txt -> plain.txt), park /// the cursor at its start, open NOTHING — Enter's normal-mode handler /// looks the selection. Wraps around when nothing lies in the direction /// (fresh out of tty mode the cursor sits below the output, so the first - /// n lands on the first token). + /// n lands on the first token). Deliberately only THIS pane's directory, + /// where the look itself walks every pane's: the motion resolves every + /// token it steps over, so the other directories would cost tokens × + /// panes realpaths on a single keystroke — and a token the motion skips + /// is still openable by looking it, which is all n/N is a shortcut for. fn lookStep(p: *Pardes, pane: *Pane, pl: PaneLines, delta: i32) void { _ = p; // a pure motion now: Enter's normal-mode handler does the look pane.pinCursor(); // fresh out of tty mode the cursor still tracks the shell @@ -4638,32 +4635,48 @@ pub const Pardes = struct { const pane = p.panes[id] orelse return; if (button == .right) { var realbuf: [4096]u8 = undefined; - var cwdbuf: [1024]u8 = undefined; - const cwd: []const u8 = if (pane.file) |f| - (std.fs.path.dirname(f.path) orelse "/") - else blk: { - const c = pane.cwdSlice(); - @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, pl.col)) 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, pl.col)) return; - } + // here and joined onto each directory below. + const pl = look.parsePathLine(std.mem.trim(u8, txt, " \t\r\n")); + if (pl.path.len > 0 and p.focusPaneByPath(pl.path, pl.line, pl.col)) return; + // The word is resolved against the pane DIRECTORIES in access + // order: the pane the click came from FIRST — its answer is the + // one taken, so nothing that resolves today moves — then every + // other live pane, most recently focused first (focus_hist runs + // least-recent -> active, so it is that array backwards; the + // clicked pane is not always `active`, a right click does not + // focus). Only when ALL of them fail does the word fall through to + // the search below: a name you can read in one window is openable + // from any of them. Each attempt is a realpath + an open and + // shells share cwds constantly, so a seen-list holds every + // directory to one try; an absolute word — and `@pN`, which reads + // no directory at all — answers the same everywhere and stops + // after the first pass. + var hit: look.Target = .none; + var seen: [MAX_PANES][]const u8 = undefined; + var nseen: usize = 0; + var n: usize = 0; + cand: while (n <= p.nfocus) : (n += 1) { + const k = if (n == 0) id else p.focus_hist[p.nfocus - n]; + if (n > 0 and k == id) continue; // tried first, skip the rerun + const op = p.panes[k] orelse continue; + const dir = if (op.file) |f| (std.fs.path.dirname(f.path) orelse "/") else op.cwdSlice(); + for (seen[0..nseen]) |s| if (std.mem.eql(u8, s, dir)) continue :cand; + seen[nseen] = dir; + nseen += 1; + if (pl.path.len > 0 and pl.path[0] != '/') { + var joinbuf: [2048]u8 = undefined; + if (std.fmt.bufPrint(&joinbuf, "{s}/{s}", .{ dir, pl.path }) catch null) |j| + if (p.focusPaneByPath(j, pl.line, pl.col)) return; } + hit = look.resolve(txt, dir, &realbuf); + if (hit != .none or pl.path.len == 0 or pl.path[0] == '/') break; } - switch (look.resolve(txt, cwd, &realbuf)) { + switch (hit) { // acme button-3: a word that names no file/dir is a search of // the pane it was clicked in — exactly what `/` runs (n/N then // walk the results). Paths (src/a/b.rs:100) still resolve above |
