diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 20:57:49 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 6a815117d242688c947a1788e4ffda9ff2bce959 (patch) | |
| tree | 311853ee7195e7b1cbd32cf130ec0f3458f72b2c /src | |
| parent | c2a1b54651be6b2532d48825d913b747a0edbea8 (diff) | |
| download | pardes-6a815117d242688c947a1788e4ffda9ff2bce959.tar.gz pardes-6a815117d242688c947a1788e4ffda9ff2bce959.zip | |
A shell command from the workspace's or a column's tag runs as a command pane in the session's directory, never typed into the terminal with the keyboard
A command clicked in a header tag, or written to /tagexec or a
column's exec, was typed into the active terminal when it sat at an
empty prompt, so it ran in that pane's directory and left no run or
exit record. It now always gets a command pane of its own in the
session's directory, as acme's row and column tags have no directory
but the session's. tags.md writes the rule down: pane words act on the
column's active pane, a column's words on the column, shell commands
as their own panes, and over 9P a pane's word is refused.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/exec.zig | 7 | ||||
| -rw-r--r-- | src/ninep/cols.zig | 21 |
2 files changed, 26 insertions, 2 deletions
diff --git a/src/exec.zig b/src/exec.zig index ea20f969..2b3ca40c 100644 --- a/src/exec.zig +++ b/src/exec.zig @@ -728,7 +728,10 @@ pub fn executeFrom(p: *Pardes, id: usize, txt: []const u8, from_body: bool) ?usi p.reportFailure(id, std.fmt.bufPrint(&said, "{s} is {s}, invalid here", .{ word, why }) catch "GUI-only, invalid here"); return null; } - if (!takesCommandLine(p, id)) return runCommand(p, id, cmd); + // A command from a column's or the workspace's tag is that tag's: a + // command pane in the session's directory, never typed into whichever + // terminal has the keyboard. + if (p.exec_header or !takesCommandLine(p, id)) return runCommand(p, id, cmd); panes.terminal.padOutputBelowEdits(p, id); panes.terminal.noteCommand(pane, cmd); if (panes.terminal.queuePendingCommand(pane, cmd) catch |err| { @@ -771,7 +774,7 @@ fn runCommand(p: *Pardes, from: usize, line: []const u8) ?usize { // would hang that job up. // From a column's tag, only a finished pane in that column is reused, // as the new one goes in that column (placeNew). - const reuse: ?usize = if (p.exec_column == null and src != null and src.?.command != null and src.?.command_done and !src.?.command_pty) from else for (p.panes, 0..) |slot, i| { + const reuse: ?usize = if (!p.exec_header and p.exec_column == null and src != null and src.?.command != null and src.?.command_done and !src.?.command_pty) from else for (p.panes, 0..) |slot, i| { const other = slot orelse continue; if (p.exec_column) |c| if (if (layout.findPane(p, i)) |f| f.col != c else true) continue; if (other.command != null and other.command_done and !other.command_pty and std.mem.eql(u8, other.cwdSlice(), dir)) break i; diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig index 7afcfbd8..6828612c 100644 --- a/src/ninep/cols.zig +++ b/src/ninep/cols.zig @@ -426,6 +426,27 @@ test "Help and Find at /tagexec act in the session's directory, not the keyboard } } +test "a shell command at /tagexec or a column's exec is a command pane in the session's directory, never typed into the keyboard's terminal" { + if (comptime !pardes.hosted) return; + const p = try Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 30 }); + defer p.deinit(); + while (p.nextEffect()) |_| {} + const term = p.panes[p.active].?; + try testing.expect(pardes.exec.takesCommandLine(p, p.active)); + const col_exec = tree.Node.ofCol(layout.columnSerial(p, 0), .exec); + for ([_]u64{ @intFromEnum(tree.TopFile.tagexec), col_exec }) |node| { + p.active = p.paneBySerial(term.serial).?; + const before = p.next_serial; + const r = th.wr(p, node, "echo from-the-tag\n"); + try testing.expectEqual(tree.Status.ok, r.reply.status); + try testing.expectEqualStrings("", r.pty()); + try testing.expectEqual(before + 1, p.next_serial); + const made = p.panes[p.paneBySerial(p.next_serial).?].?; // the newest + try testing.expectEqualStrings("echo from-the-tag", made.command.?); + try testing.expectEqualStrings(p.sessionDir(), made.cwdSlice()); + } +} + test "New and Tty run at /tagexec and a column's exec, with no pane at all too" { const p = try th.withFile(testing.allocator, "x\n"); defer p.deinit(); |
