diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-28 11:22:53 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:14 -0300 |
| commit | 1c3842f136b9f98fae784559ec12d66d629c5909 (patch) | |
| tree | 9a53e64542e191b717052dbe5b5cd79cfd2c0754 /src/host_io.zig | |
| parent | 5cf50930dee214fb641bb95bbf8639b8f76930d9 (diff) | |
| download | pardes-1c3842f136b9f98fae784559ec12d66d629c5909.tar.gz pardes-1c3842f136b9f98fae784559ec12d66d629c5909.zip | |
Exit quits and Kill stops commands, as in acme
Kill quit the editor, which in acme is Exit; acme's Kill stops the
commands it started. Exit now quits as acme's does (exec.c, rowclean):
it refuses once, naming each pane with unsaved text, and quits when
asked again with nothing edited since (a small scratch is not asked
about). Kill, bare or with names, stops the commands pardes typed into a
terminal (an exec, a middle click, a pty/run) while their shell's marks
say they run, by SIGTERM to the terminal's foreground job, never to the
shell (acme posts the kill note, which terminates). Both are session
builtins; the topbar's Kill becomes Exit, same width, and every golden's
topbar row changed by exactly that word (checked line by line); the
builtins script scrolls one more row for the index's new line.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/host_io.zig')
| -rw-r--r-- | src/host_io.zig | 25 |
1 files changed, 25 insertions, 0 deletions
diff --git a/src/host_io.zig b/src/host_io.zig index a5efc7d1..5d9dd413 100644 --- a/src/host_io.zig +++ b/src/host_io.zig @@ -1337,8 +1337,14 @@ pub fn signalTty(shell_pid: libc.pid_t, master_fd: c_int, which: pardes.PtySigna .hup => libc.SIG.HUP, .quit => libc.SIG.QUIT, .kill => libc.SIG.KILL, + .command => libc.SIG.TERM, }; const fg = tcgetpgrp(master_fd); + // The shell leads its own group: a job it runs has another. + if (which == .command) { + if (fg > 0 and fg != shell_pid) _ = libc.kill(-fg, sig); + return; + } if (fg > 0) { _ = libc.kill(-fg, sig); return; @@ -1640,6 +1646,25 @@ test "an idle prompt is free, a foreground job takes the tty, and Ctrl-C hands i try std.testing.expect(sh.waitText(test_prompt, 10_000)); } +test "Kill's signal stops the foreground job and never the shell" { + if (comptime !tty_probe_platform) return error.SkipZigTest; + var sh = TestShell.start() orelse return error.SkipZigTest; + defer sh.stop(); + + // At the prompt the shell is the foreground: there is nothing to stop. + signalTty(sh.pid, sh.master, .command); + sleepMs(100); + try std.testing.expectEqual(@as(libc.pid_t, 0), libc.waitpid(sh.pid, null, libc.W.NOHANG)); + + sh.send("sleep 30\n"); + try std.testing.expect(sh.waitTaken(true, 10_000)); + sh.forget(); + signalTty(sh.pid, sh.master, .command); + try std.testing.expect(sh.waitTaken(false, 10_000)); + try std.testing.expect(sh.waitText(test_prompt, 10_000)); + try std.testing.expectEqual(@as(libc.pid_t, 0), libc.waitpid(sh.pid, null, libc.W.NOHANG)); +} + test "a background job is not the tty's owner" { if (comptime !tty_probe_platform) return error.SkipZigTest; var sh = TestShell.start() orelse return error.SkipZigTest; |
