summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-28 15:53:29 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:15 -0300
commitb67dce15e662ae44de3286686f5a0afc147b918d (patch)
tree9f9dac843b61501bf778bf1d07ed7ee598b26db6 /src/pardes.zig
parent2868137e34c973a47cbf667a431e885ca23cd9a2 (diff)
downloadpardes-b67dce15e662ae44de3286686f5a0afc147b918d.tar.gz
pardes-b67dce15e662ae44de3286686f5a0afc147b918d.zip
A REPL takes text only while its program has the terminal
A binding outlived its program: a command pane reused kept the REPL of the command before, a done command pane could be bound, a document pane (a terminal with no emulator) was accepted, and after Ctrl-D in python3 the text went to the shell, which ran import os as ImageMagick's. A command pane's binding now ends with its command and a done one is refused, a pane with no emulator is refused, and an interactive terminal's REPL is live only while a program other than its shell holds the tty (the host's tty_taken); when none of the language's REPLs is, nothing is sent or run and the pane says so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/pardes.zig')
-rw-r--r--src/pardes.zig34
1 files changed, 33 insertions, 1 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index 429f33b9..f8328289 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -1446,6 +1446,11 @@ test "a REPL bound for a language takes the exec made on a body in it; tags, Exe
return to[pane][0..len[pane]];
}
};
+ // The programs the REPLs run hold their terminals' ttys.
+ var host: FakeTtyQuery = .{};
+ host.install(p);
+ host.taken[1] = true;
+ host.taken[2] = true;
// Only a terminal is a REPL; a language's name is the syntax table's.
try std.testing.expect(p.executeBuiltinLine(0, "Repl python"));
try std.testing.expect(file.repl == null);
@@ -1503,6 +1508,18 @@ test "a REPL bound for a language takes the exec made on a body in it; tags, Exe
Sent.drain(p);
try std.testing.expect(Sent.len[1] == 0 and Sent.len[2] == 0);
+ // A REPL whose program has left (Ctrl-D: the shell has the tty) is
+ // sent nothing, and nothing runs: it says so.
+ host.taken[1] = false;
+ host.taken[2] = false;
+ p.exec_from_body = true;
+ _ = exec.execute(p, 0, "import os");
+ Sent.drain(p);
+ try std.testing.expect(Sent.len[1] == 0 and Sent.len[2] == 0 and !Sent.spawned);
+ try std.testing.expect(std.mem.indexOf(u8, file.msg[0..file.msg_len], "no program running") != null);
+ host.taken[1] = true;
+ host.taken[2] = true;
+
// Unbound, its letter is free again; bare, it says what it is.
try std.testing.expect(p.executeBuiltinLine(1, "Repl -"));
try std.testing.expect(p.panes[1].?.repl == null);
@@ -1510,6 +1527,21 @@ test "a REPL bound for a language takes the exec made on a body in it; tags, Exe
try std.testing.expectEqualStrings("python-a", exec.replId(&idbuf, p.panes[1].?));
try std.testing.expect(p.executeBuiltinLine(2, "Repl"));
try std.testing.expectEqualStrings("Repl: python-b", p.panes[2].?.msg[0..p.panes[2].?.msg_len]);
+
+ // A command pane is a REPL while its command runs: done, it is none,
+ // and a done one cannot be bound. A document pane is no terminal.
+ const cmd = p.freeSlot().?;
+ _ = try p.newCommand(cmd, "", "python3");
+ try std.testing.expect(p.executeBuiltinLine(cmd, "Repl python"));
+ try std.testing.expect(p.panes[cmd].?.repl != null);
+ p.update(.{ .exited = .{ .pane = @intCast(cmd), .status = 0 } });
+ try std.testing.expect(p.panes[cmd].?.repl == null);
+ try std.testing.expect(p.executeBuiltinLine(cmd, "Repl python"));
+ try std.testing.expect(p.panes[cmd].?.repl == null);
+ const doc_slot = p.freeSlot().?;
+ const doc = try p.newDocPane(doc_slot);
+ try std.testing.expect(p.executeBuiltinLine(doc_slot, "Repl python"));
+ try std.testing.expect(doc.repl == null);
}
test "Tty+fish, one word a tag can hold, opens a terminal on that shell" {
@@ -5172,7 +5204,7 @@ pub const Pardes = struct {
// Its letter sends it there; anything else takes it back.
if (key.ctrl or key.alt or key.cp > 'z' or key.cp < 'a') return;
for (p.panes, 0..) |target, to| if (target) |repl| if (repl.repl) |r|
- if (r.lang == ask.lang and r.letter == key.cp and exec.replLive(repl))
+ if (r.lang == ask.lang and r.letter == key.cp and exec.replLive(p, to))
return exec.sendToRepl(p, id, to, ask.text);
return;
}