summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pardes.zig51
-rw-r--r--test/snapshots/topbarexec.golden63
-rw-r--r--test/snapshots/topbarexec.snap45
3 files changed, 147 insertions, 12 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index b8b2de33..61950562 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -8818,7 +8818,17 @@ pub const Pardes = struct {
// a topbar word runs on the PRESS — there is no
// drag to chord into, so the argument is simply
// whatever is selected right now: select a word,
- if (word.len > 0) _ = p.execute(p.active, p.withArg(word, p.heldSelection(p.active)));
+ // ...but only for a builtin that HAS somewhere to
+ // put one (see takesArg): a word left selected in
+ // some pane an hour ago is not an argument to
+ // Kill, and splicing it made a name that matches
+ // no builtin and therefore went to a shell.
+ const named = std.meta.stringToEnum(Builtin, word);
+ const held = if (named) |b|
+ (if (takesArg(b)) p.heldSelection(p.active) else null)
+ else
+ p.heldSelection(p.active); // not a builtin: an ordinary acme 2-1
+ if (word.len > 0) _ = p.execute(p.active, p.withArg(word, held));
}
return;
}
@@ -10998,17 +11008,7 @@ pub const Pardes = struct {
const sp = std.mem.indexOfAny(u8, cmd, " \t");
const bi: ?Builtin = std.meta.stringToEnum(Builtin, cmd) orelse blk: {
const head = std.meta.stringToEnum(Builtin, cmd[0 .. sp orelse break :blk null]) orelse break :blk null;
- break :blk switch (head) {
- .Restore, .Find, .Grep, .Rename, .WsSymbols, .Theme, .Look, .Exec => head,
- // Font is a BRANCH and not another name in that prong for the
- // reason output_pane.traits has the same shape: it is not in
- // the enum at all off the gui, and a comptime-false `if` is
- // the one form that is not analysed there.
- else => {
- if (platform == .gui) if (head == .Font) break :blk head;
- break :blk null;
- },
- };
+ break :blk if (takesArg(head)) head else null;
};
const b = bi orelse return false;
p.exec_depth += 1;
@@ -11018,6 +11018,33 @@ pub const Pardes = struct {
return true;
}
+ /// Which builtins match their name with a TAIL — `Restore <path>`,
+ /// `Grep <pat>`, `Theme <name>`, and the two verbs, `Look <word>` /
+ /// `Exec <cmd>`, which is what makes `` @`Look .` `` nest. Every other
+ /// name must match WHOLE, so `Kill foo` is a shell command and not Kill.
+ ///
+ /// Asked in two places and so written in one. The second asker is the
+ /// topbar's execute, and it is why this is a function: a topbar click runs
+ /// the word under it WITH whatever happens to be selected (select a word,
+ /// click Grep, it greps that word), and a builtin that takes no argument
+ /// was getting one anyway — `Kill` with a stale sweep somewhere became
+ /// `Kill <word>`, which is not Kill, so it fell through to the shell and
+ /// ran as a command nobody typed. A selection is only an argument to
+ /// something that has somewhere to put it.
+ fn takesArg(b: Builtin) bool {
+ switch (b) {
+ .Restore, .Find, .Grep, .Rename, .WsSymbols, .Theme, .Look, .Exec => return true,
+ // Font is a BRANCH and not another name in that prong for the
+ // reason output_pane.traits has the same shape: it is not in the
+ // enum at all off the gui, and a comptime-false `if` is the one
+ // form that is not analysed there.
+ else => {
+ if (platform == .gui) if (b == .Font) return true;
+ return false;
+ },
+ }
+ }
+
fn applyStartupConfig(p: *Pardes) void {
var lines = std.mem.splitScalar(u8, p.opts.startup_config orelse return, '\n');
while (lines.next()) |line| _ = p.executeBuiltinLine(p.active, line);
diff --git a/test/snapshots/topbarexec.golden b/test/snapshots/topbarexec.golden
new file mode 100644
index 00000000..7d27c4cd
--- /dev/null
+++ b/test/snapshots/topbarexec.golden
@@ -0,0 +1,63 @@
+== snap selected grid=90x20 cursor=14,3
+|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
+| /tmp/pardes-snap/topbarexec/cwd New Del /tmp/pardes-snap/topbarexec/cwd New Del
+|
+| zmarkfile.txt zmarkfile.txt
+|
+|
+|
+|
+|
+|
+|
+| /tmp/pardes-snap/topbarexec/cwd New Del
+|
+| zmarkfile.txt
+|
+|
+|
+|
+|
+|
+== snap noarg-runs grid=90x20 cursor=14,3
+|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
+| /tmp/pardes-snap/topbarexec/cwd New Del /tmp/pardes-s╭────────────────────────────╮
+| │pardes resizes=1 │
+| zmarkfile.txt zmarkfile.txt│terms 3 cols 2 │
+| │active #0 43x9 │
+| │scroll 0/9 │
+| │pinch 1.0000 │
+| │tscroll 0.0000 │
+| ╰────────────────────────────╯
+|
+|
+| /tmp/pardes-snap/topbarexec/cwd New Del
+|
+| zmarkfile.txt
+|
+|
+|
+|
+|
+|
+== snap arg-still-spliced grid=90x20 cursor=14,3
+|New Newcol Find Grep Help Tutor Dump NextColor Debug Kill
+| /tmp/pardes-snap/topbarexec/cwd New Del /tmp/pardes-snap/topbarexec/cwd New Del
+|
+| zmarkfile.txt zmarkfile.txt
+|
+|
+|
+|
+|
+| /tmp/pardes-snap/topbarexec/cwd/+Search New
+| 1 zmarkfile.txt
+| /tmp/pardes-snap/topbarexec/cwd New Del
+|
+| zmarkfile.txt
+|
+|
+|
+|
+|
+|
diff --git a/test/snapshots/topbarexec.snap b/test/snapshots/topbarexec.snap
new file mode 100644
index 00000000..bebaed3f
--- /dev/null
+++ b/test/snapshots/topbarexec.snap
@@ -0,0 +1,45 @@
+# A topbar word runs on a middle click, and it runs WITH whatever is selected:
+# sweep a word, click Grep, it greps that word. That is acme's 2-1 chord with
+# no drag to chord into, and it is the whole reason the topbar reads the
+# selection at all.
+#
+# But a selection is only an argument to a builtin that has somewhere to put
+# one. `Kill`, `Debug`, `NextColor` and the rest match their name WHOLE — that
+# is what makes `Kill foo` a shell command and not Kill — so splicing a sweep
+# onto them produced a name matching no builtin, which fell out of the builtin
+# parser and into a SHELL, as a command nobody typed (`bash: Debug: command
+# not found`). It only ever bit you with a terminal open, because that is
+# where the stray text went.
+#
+# So both halves are pinned here, against the same live selection: the no-arg
+# builtin runs, and the arg-taking one still gets the word.
+file zmarkfile.txt hay ZQNEEDLE stack
+start 20 90 -n 3
+wait 8000 zmarkfile.txt
+stable 700 20000
+# sweep `zmarkfile.txt` out of the shell's own greeting: an ordinary left drag,
+# and the kind of stale highlight that is simply lying around in real use
+press left 3 4
+drag left 15 4
+release left 15 4
+stable 700 15000
+snap selected
+# Debug takes NO argument. Middle-clicked with that sweep live it must open the
+# overlay — not send `Debug zmarkfile.txt` to bash. The box IS the assertion.
+press middle 50 1
+release middle 50 1
+stable 700 15000
+snap noarg-runs
+# ...and off again, so the overlay does not cover what comes next
+press middle 50 1
+release middle 50 1
+stable 700 15000
+# Find DOES take one, and still gets it: the sweep is the pattern, so the
+# results buffer names the file it matched. An empty buffer would mean Find
+# ran with no argument (which arms the prompt instead), so the ROW is the
+# assertion — the splice is what put it there.
+press middle 13 1
+release middle 13 1
+wait 10000 +Search
+stable 700 15000
+snap arg-still-spliced