diff options
| -rw-r--r-- | src/exec.zig | 9 | ||||
| -rw-r--r-- | src/host_io.zig | 11 | ||||
| -rw-r--r-- | src/panes.zig | 4 | ||||
| -rw-r--r-- | test/fs.py | 18 |
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, @@ -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_')} |
