summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-07-27 10:20:19 -0300
committerGabriel Schneider <[email protected]>2026-08-01 15:02:07 -0300
commitc79612b523c1526202335302e1e49c078ca317cf (patch)
treeb76d7b0e15f3b0e43ba65f9ea9eb57ee4a8ed6e1
parentf0e498832a1a4f92d5fdbc3fc643a3dba1dd9df9 (diff)
downloadpardes-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
-rw-r--r--docs/design.typ16
-rw-r--r--src/pardes.zig67
-rw-r--r--src/tutor.txt7
-rw-r--r--test/snapshots/lookpanes.golden306
-rw-r--r--test/snapshots/lookpanes.snap79
5 files changed, 440 insertions, 35 deletions
diff --git a/docs/design.typ b/docs/design.typ
index 61015e17..c7284049 100644
--- a/docs/design.typ
+++ b/docs/design.typ
@@ -204,12 +204,16 @@ pane to new column; Ctrl-w h/j/k/l directional focus; `--tty` single-pane mode.
enters normal), click clears, tag-row click enters tag edit, scrollbar
click scrolls up-to-row (right button: down), border/move drags. Middle:
execute — no-drag expands to file-ish word (alnum `.-+/:@_~`); builtin or send
-to shell; does not focus. Right: look — peel `:NNN`, resolve against pane cwd
-(`/proc/pid/cwd`) or file's dir; dir → shell + `ls` in source column (dedup by
-cwd), file → file pane at line (dedup by path, first doc opens left column and
-may evict a lone pristine shell; later docs split below the existing doc),
-image ext → image pane. Middle+left chord: kept left selection appended as
-trailing CLI argument. Wheel: scroll hovered pane, batched.
+to shell; does not focus. Right: look — peel `:NNN`, resolve against the
+clicked pane's dir (`/proc/pid/cwd`, or a file's dirname) and, if that fails,
+against every other live pane's, most-recently-focused first (focus_hist
+backwards, duplicate dirs skipped; absolute words try one dir and stop) so a
+relative name is openable from any window that can see it; dir → shell + `ls`
+in source column (dedup by cwd), file → file pane at line (dedup by path,
+first doc opens left column and may evict a lone pristine shell; later docs
+split below the existing doc), image ext → image pane. Middle+left chord: kept
+left selection appended as trailing CLI argument. Wheel: scroll hovered pane,
+batched.
*Modes.* normal: helix motions (`h j k l w b e W B E 0 $ ^ gg ge gh gl G`,
`Ctrl-d/u/f/b`, `zt zz zb`), insert entries (`i a I A o O`), `v`/`x`
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
diff --git a/src/tutor.txt b/src/tutor.txt
index 80a67c57..195173c6 100644
--- a/src/tutor.txt
+++ b/src/tutor.txt
@@ -65,6 +65,9 @@
R look open the word under the cursor as a path: a directory
opens/focuses a terminal there and ls's it; a file opens
a file pane (scrolled to a ":line" suffix if present).
+ A bare name is tried in THIS pane's directory first, then
+ in every other open pane's, most recently used first — a
+ name none of them holds is what becomes a search.
WEB TOUCH (one finger): a tap is LOOK, the same action as R; a drag
past a small movement threshold is natural scrolling.
@@ -137,8 +140,8 @@
That is an OUTPUT BUFFER: a file pane with no file behind it, so it has
no Save, but everything else about it is an ordinary buffer you can read,
edit, select and look in. `n`/`N` step the rows and look each one, so the
- view follows along. Right-clicking a word that names no file searches for
- it the same way — acme's button 3 — everywhere except a shell in tty mode,
+ view follows along. Right-clicking a word that names no file in ANY open
+ pane's directory searches for it — acme's button 3 — except in tty mode,
where the click belongs to the program on the other end. On a shell with no
search armed, n/N instead step the lookable tokens in its output.
A hit in a file reads `path:LINE:COL`, the ordinary look target. A hit in
diff --git a/test/snapshots/lookpanes.golden b/test/snapshots/lookpanes.golden
new file mode 100644
index 00000000..e2f72d13
--- /dev/null
+++ b/test/snapshots/lookpanes.golden
@@ -0,0 +1,306 @@
+== snap shells grid=130x50 cursor=122,3
+|Kill Newcol Tutor Debug NextColor Dump
+| NOR /tmp/pardes-snap/lookpanes/cwd/beta/bmark.txt Save Del NOR /tmp/pardes-snap/lookpanes/cwd Del
+| 1 both.txt
+| 2 b-marker alpha beta zzboot.txt zqonly.txt pick.txt twin.txt
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha Del
+|
+| amark.txt both.txt pick.txt twin.txt zqonly.txt
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== snap other-dir grid=130x50 cursor=7,27
+|Kill Newcol Tutor Debug NextColor Dump
+| NOR /tmp/pardes-snap/lookpanes/cwd/beta/bmark.txt Save Del NOR /tmp/pardes-snap/lookpanes/cwd Del
+| 1 both.txt
+| 2 b-marker alpha beta zzboot.txt zqonly.txt pick.txt twin.txt
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha Del
+|
+| amark.txt both.txt pick.txt twin.txt zqonly.txt
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha/zqonly.txt Save Del
+| 1 ONLY-IN-ALPHA
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== snap clicked-wins grid=130x50 cursor=7,15
+|Kill Newcol Tutor Debug NextColor Dump
+| NOR /tmp/pardes-snap/lookpanes/cwd/beta/bmark.txt Save Del NOR /tmp/pardes-snap/lookpanes/cwd Del
+| 1 both.txt
+| 2 b-marker alpha beta zzboot.txt zqonly.txt pick.txt twin.txt
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha Del
+|
+| amark.txt both.txt pick.txt twin.txt zqonly.txt
+|
+|
+|
+|
+|
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/beta/both.txt Save Del
+| 1 BOTH-FROM-BETA
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha/zqonly.txt Save Del
+| 1 ONLY-IN-ALPHA
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== snap mru-alpha grid=130x50 cursor=7,9
+|Kill Newcol Tutor Debug NextColor Dump
+| NOR /tmp/pardes-snap/lookpanes/cwd/beta/bmark.txt Save Del NOR /tmp/pardes-snap/lookpanes/cwd Del
+| 1 both.txt
+| 2 b-marker alpha beta zzboot.txt zqonly.txt pick.txt twin.txt
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha Del
+|
+| amark.txt both.txt pick.txt twin.txt zqonly.txt
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha/pick.txt Save Del
+| 1 PICK-FROM-ALPHA
+|
+|
+|
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/beta/both.txt Save Del
+| 1 BOTH-FROM-BETA
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha/zqonly.txt Save Del
+| 1 ONLY-IN-ALPHA
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== snap mru-beta grid=130x50 cursor=7,5
+|Kill Newcol Tutor Debug NextColor Dump
+| NOR /tmp/pardes-snap/lookpanes/cwd/beta/bmark.txt Save Del NOR /tmp/pardes-snap/lookpanes/cwd Del
+| 1 both.txt
+| 2 b-marker alpha beta zzboot.txt zqonly.txt pick.txt twin.txt
+| NOR /tmp/pardes-snap/lookpanes/cwd/beta/twin.txt Save Del
+| 1 TWIN-FROM-BETA NOR /tmp/pardes-snap/lookpanes/cwd/alpha Del
+|
+| amark.txt both.txt pick.txt twin.txt zqonly.txt
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha/pick.txt Save Del
+| 1 PICK-FROM-ALPHA
+|
+|
+|
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/beta/both.txt Save Del
+| 1 BOTH-FROM-BETA
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+| NOR /tmp/pardes-snap/lookpanes/cwd/alpha/zqonly.txt Save Del
+| 1 ONLY-IN-ALPHA
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+== style mru-beta
+|0: 0-37 #949494,#121212, 38-129 d,#121212,
+|1: 0-1 d,#262626, 2-59 #949494,#121212, 60-64 d,#121212, 65-66 d,#262626, 67-104 #949494,#121212, 105-129 d,#121212,
+|2: 0-1 d,#444444, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|3: 0-1 d,#444444, 2-6 #626262,#080808, 7-14 #bdbdbd,#080808, 15-64 d,#080808, 65-66 d,#444444, 67-121 #bdbdbd,#080808, 122-129 d,#080808,
+|4: 0-1 d,#626262, 2-58 #949494,#121212, 59-64 d,#121212, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|5: 0-1 d,#444444, 2-6 #949494,#121212, 7-20 #bdbdbd,#080808, 21-64 d,#080808, 65-66 d,#262626, 67-110 #949494,#121212, 111-129 d,#121212,
+|6: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|7: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|8: 0-1 d,#262626, 2-59 #949494,#121212, 60-64 d,#121212, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|9: 0-1 d,#444444, 2-6 #626262,#080808, 7-21 #bdbdbd,#080808, 22-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|10: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|11: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|12: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|13: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|14: 0-1 d,#262626, 2-58 #949494,#121212, 59-64 d,#121212, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|15: 0-1 d,#444444, 2-6 #626262,#080808, 7-20 #bdbdbd,#080808, 21-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|16: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|17: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|18: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|19: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|20: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|21: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|22: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|23: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|24: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|25: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|26: 0-1 d,#262626, 2-61 #949494,#121212, 62-64 d,#121212, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|27: 0-1 d,#444444, 2-6 #626262,#080808, 7-19 #bdbdbd,#080808, 20-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|28: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|29: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|30: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|31: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|32: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|33: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|34: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|35: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|36: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|37: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|38: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|39: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|40: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|41: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|42: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|43: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|44: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|45: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|46: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|47: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|48: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
+|49: 0-1 d,#444444, 2-6 #626262,#080808, 7-64 d,#080808, 65-66 d,#444444, 67-129 #bdbdbd,#080808,
diff --git a/test/snapshots/lookpanes.snap b/test/snapshots/lookpanes.snap
new file mode 100644
index 00000000..98648226
--- /dev/null
+++ b/test/snapshots/lookpanes.snap
@@ -0,0 +1,79 @@
+# a look resolves against EVERY open pane's directory, in access order: the
+# pane the click came from first, then the rest most-recently-focused first,
+# and only a word NO directory holds falls through to a search. Two dirs
+# (alpha, beta) hold same-named files and the shell's own cwd holds none of
+# them, so every look below would have been a search before.
+dirmk alpha
+dirmk beta
+file zzboot.txt x
+file alpha/amark.txt a
+file alpha/zqonly.txt ONLY-IN-ALPHA
+file alpha/pick.txt PICK-FROM-ALPHA
+file alpha/twin.txt TWIN-FROM-ALPHA
+file alpha/both.txt BOTH-FROM-ALPHA
+file beta/pick.txt PICK-FROM-BETA
+file beta/twin.txt TWIN-FROM-BETA
+file beta/both.txt BOTH-FROM-BETA
+# the file argument opens a pane whose directory is beta's, beside a shell in
+# the workdir; its first line is a word to look FROM that pane
+file beta/bmark.txt both.txt\nb-marker
+start 50 130 -n 2 beta/bmark.txt
+wait 8000 zzboot.txt
+stable 700 20000
+# an ordinary dir look on the shell's own ls output: a shell in ./alpha,
+# split below it
+press right 70 4
+release right 70 4
+wait 10000 amark.txt
+stable 700 20000
+# type the words to look into what is left of the workdir shell (past its own
+# ls output, on the same row: its last row is the resize handle)
+press left 95 4
+release left 95 4
+stable 400 5000
+key i
+text zqonly.txt pick.txt twin.txt
+key esc
+settle 700
+stable 400 5000
+snap shells
+# make the beta pane the most recently focused, so the walk has to pass it
+press left 10 3
+release left 10 3
+stable 700 10000
+# zqonly.txt is only under alpha: the clicked shell's cwd fails, beta fails,
+# the alpha shell answers. Before, this word became a search.
+press right 99 4
+release right 99 4
+wait 10000 ONLY-IN-ALPHA
+stable 700 20000
+snap other-dir
+# both.txt is in BOTH dirs and alpha is now the most recent (its file pane
+# just opened), but the CLICK still wins: looked inside the beta pane it
+# opens beta's copy
+press right 10 3
+release right 10 3
+wait 10000 BOTH-FROM-BETA
+stable 700 20000
+snap clicked-wins
+# from a pane whose own dir holds neither copy the access order decides:
+# focus the alpha shell, look pick.txt (in both dirs) -> alpha's
+press left 70 12
+release left 70 12
+stable 700 10000
+press right 109 4
+release right 109 4
+wait 10000 PICK-FROM-ALPHA
+stable 700 20000
+snap mru-alpha
+# focus the beta pane instead and the same look flips: twin.txt is in both
+# dirs, beta is most recent, beta's copy opens
+press left 10 3
+release left 10 3
+stable 700 10000
+press right 118 4
+release right 118 4
+wait 10000 TWIN-FROM-BETA
+stable 700 20000
+snap mru-beta
+snapstyle mru-beta