summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-30 23:32:31 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:18 -0300
commit48c129a701d60abdb836bbb400d949cc61ed2820 (patch)
tree42f6ab7e64bc02c208a062eb31ad4937e4339f04
parentafaa2428b346f6dd1d165abe1396dce9150fa05c (diff)
downloadpardes-48c129a701d60abdb836bbb400d949cc61ed2820.tar.gz
pardes-48c129a701d60abdb836bbb400d949cc61ed2820.zip
A command run from a pane gets acme's $winid, the serial of the pane it was run from; one from a column's or the workspace's tag gets none
PARDES_PANE names the command pane itself, so a script a click ran had no way to address the pane it was run in, as acme's win scripts do with $winid. The command pane now remembers that pane and its child gets winid set to it; a header tag's command, which no pane owns, gets winid unset. PARDES_PANE is unchanged. Co-Authored-By: Claude Opus 5.5 <[email protected]>
-rw-r--r--src/exec.zig9
-rw-r--r--src/host_io.zig11
-rw-r--r--src/panes.zig4
-rw-r--r--test/fs.py18
4 files changed, 42 insertions, 0 deletions
diff --git a/src/exec.zig b/src/exec.zig
index e15ad12c..47484105 100644
--- a/src/exec.zig
+++ b/src/exec.zig
@@ -792,6 +792,7 @@ fn runCommand(p: *Pardes, from: usize, line: []const u8) ?usize {
if (panes.terminal.onAlternateScreen(pane)) panes.terminal.feedOutput(p, pane, "\x1b[?1049l");
panes.terminal.feedOutput(p, pane, "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?2004l\x1b[?25h\x1b[0m");
echoCommand(p, pane, line);
+ pane.command_from = winid(p, src);
p.emitSpawn(id, pane.serial, pane.cwdSlice());
noteRun(p, pane, "run", line);
return id;
@@ -804,12 +805,20 @@ fn runCommand(p: *Pardes, from: usize, line: []const u8) ?usize {
p.reportError(from, "exec", err);
return null;
};
+ pane.command_from = winid(p, src);
echoCommand(p, pane, line);
placeNew(p, from, from, free, .command);
noteRun(p, pane, "run", line);
return free;
}
+/// The serial a command's `$winid` names: the pane it was run from, none
+/// (0) from a column's or the workspace's tag.
+fn winid(p: *const Pardes, src: ?*Pane) u32 {
+ if (p.exec_header) return 0;
+ return if (src) |s| s.serial else 0;
+}
+
/// `% line` above a command's output, every command, as a shell echoes
/// what it was given; its newlines the terminal's, so a multi-line command
/// does not staircase.
diff --git a/src/host_io.zig b/src/host_io.zig
index 1fab3729..8ddb0c90 100644
--- a/src/host_io.zig
+++ b/src/host_io.zig
@@ -1351,6 +1351,14 @@ test "the child environment replaces the launcher's terminal identity exactly on
extern "c" fn unsetenv(name: [*:0]const u8) c_int;
+fn exportWinid(serial: u32) void {
+ var buf: [16]u8 = undefined;
+ if (serial != 0) if (std.fmt.bufPrintSentinel(&buf, "{d}", .{serial}, 0)) |text| {
+ if (Shell.setenv("winid", text, 1) == 0) return;
+ } else |_| {};
+ _ = unsetenv("winid");
+}
+
/// The longest line a command pane runs (exec.zig refuses a longer one).
pub const exec_command_max = pardes.exec.command_max;
@@ -1418,6 +1426,9 @@ pub fn forkShell(
if (core) |c| (if (c.panes[pane]) |pn| pn.serial else 0) else 0,
if (core) |c| !c.opts.nested else false,
);
+ // acme's `$winid`: the pane a command was run from, so a script it runs
+ // can address that pane; unset for a shell and a header tag's command.
+ exportWinid(if (core) |c| (if (c.panes[pane]) |pn| (if (pn.command != null) pn.command_from else 0) else 0) else 0);
if (helper) |path| {
const c = core.?;
const pn = c.panes[pane].?;
diff --git a/src/panes.zig b/src/panes.zig
index 0380f89f..a395d6ed 100644
--- a/src/panes.zig
+++ b/src/panes.zig
@@ -124,6 +124,10 @@ pub const Pane = struct {
/// A command pane (exec.zig runCommand): the line its child runs as
/// `$SHELL -c`, owned, where an interactive terminal has null.
command: ?[]u8 = null,
+ /// The pane whose tag or text the command was run from, its child's
+ /// `$winid` (acme's name); 0 for one from a column's or the workspace's
+ /// tag, which no pane owns.
+ command_from: u32 = 0,
/// The command's child has exited, and with what status (null: unknown,
/// shown `exit ?`).
command_done: bool = false,
diff --git a/test/fs.py b/test/fs.py
index 0821b117..23647b45 100644
--- a/test/fs.py
+++ b/test/fs.py
@@ -1306,6 +1306,24 @@ def test(binary, quic=False):
# opened is deleted, as an $EDITOR must; one on a file already open
# waits on that pane; a session that goes away ends it with 1.
with session(binary, root, 'wait') as (client, address):
+ # A command run from a pane's tag gets acme's $winid, that pane;
+ # one from the workspace's tag, none.
+ def command_body(node, line):
+ client.write(node, line)
+ serial = int(client.read(node).split()[-1])
+ deadline = time.monotonic() + 10
+ while True:
+ log = client.read('/log').decode()
+ ran = log.rfind(f'\nrun {serial} ')
+ if ran >= 0 and f'\nexit {serial} ' in log[ran:]:
+ body = client.read(f'/pane/{serial}/body').decode().split('% echo winid')[-1]
+ client.remove(f'/pane/{serial}')
+ return body
+ assert time.monotonic() < deadline, log[-400:]
+ time.sleep(.05)
+ first_pane = min(int(row.split()[0]) for row in client.read('/index').splitlines())
+ assert f'winid={first_pane}\n' in command_body(f'/pane/{first_pane}/tagexec', b'echo winid=$winid\n')
+ assert 'winid=\n' in command_body('/tagexec', b'echo winid=$winid\n')
# A second --detach of a name a live session holds: said, exit 1,
# no Zig error trace.
second_env = {k: v for k, v in os.environ.items() if not k.startswith('PARDES_')}