From d067c77813f9ce9fcc64b33e6b0284510ac02185 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 03:35:43 -0300 Subject: A REPL paste's Enter waits on the clock, not on stale core time sendToRepl timed the Enter's fallback from stepped_ns, which in an idle detached session is as old as the last frame: the wait was over before it began and the \r went out with the paste, which 3.13's REPL then reads as part of it (4 of 5 sends lost their Enter). The wait now starts from the host's clock and is checked against now_ns; the REPL's echo of the paste still sends it sooner, and the wake is scheduled for the deadline. fs.py sends five times after two idle seconds each in a real pty. Co-Authored-By: Claude Opus 5.5 --- src/exec.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/exec.zig') diff --git a/src/exec.zig b/src/exec.zig index 484b85d7..963c8763 100644 --- a/src/exec.zig +++ b/src/exec.zig @@ -450,7 +450,7 @@ const repl_enter_wait_ns: u64 = 250 * std.time.ns_per_ms; pub fn flushReplEnter(p: *Pardes, id: usize, answered: bool) void { const pane = p.panes[id] orelse return; if (pane.repl_enter == 0) return; - if (!answered and p.stepped_ns < pane.repl_enter_due_ns) return; + if (!answered and p.now_ns < pane.repl_enter_due_ns) return; for (0..pane.repl_enter) |_| p.emitWrite(id, "\r"); pane.repl_enter = 0; } @@ -499,7 +499,12 @@ pub fn sendToRepl(p: *Pardes, from: usize, to: usize, text: []const u8) void { // It goes once the REPL has answered the paste (its next output), // or after a moment if nothing comes. dst.repl_enter = enters; - dst.repl_enter_due_ns = p.stepped_ns + repl_enter_wait_ns; + // From the clock now, not core time: idle (a detached session + // nobody watches), `stepped_ns` and `now_ns` are as old as the last + // frame, and a wait counted from there would be over already, the + // Enter going out with the paste. + const now = if (p.host.vtable.now) |clock| clock(p.host.ctx) else p.now_ns; + dst.repl_enter_due_ns = now + repl_enter_wait_ns; } else for (0..enters) |_| p.emitWrite(to, "\r"); // Line by line, a blank line inside a Python block ends the block. if (!panes.Terminal.bracketedPaste(dst) and !dst.repl_warned and (if (dst.repl) |r| r.lang == python_lang else false) and -- cgit v1.3