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/pardes.zig | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/pardes.zig') diff --git a/src/pardes.zig b/src/pardes.zig index dab27160..9297c829 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -1655,10 +1655,13 @@ test "a REPL bound for a language takes the exec made on a body in it; tags, Exe try std.testing.expectEqualStrings("\r", Sent.said(1)); // Nothing answering, it goes when its wait is up. _ = exec.executeFrom(p, 0, "print(2)", true); - p.stepped_ns += 300 * std.time.ns_per_ms; + p.update(.tick); // on the clock: not yet, however old core time is + Sent.drain(p); + try std.testing.expect(std.mem.endsWith(u8, Sent.said(1), "\x1b[200~print(2)\x1b[201~")); + p.now_ns = p.panes[1].?.repl_enter_due_ns; // the clock reaches its wait p.update(.tick); Sent.drain(p); - try std.testing.expect(std.mem.endsWith(u8, Sent.said(1), "\x1b[200~print(2)\x1b[201~\r")); + try std.testing.expectEqualStrings("\r", Sent.said(1)); p.update(.{ .output = .{ .pane = 1, .bytes = "\x1b[?2004l" } }); Sent.len[1] = 0; // A block left open by its indented last line gets the blank line that @@ -6571,9 +6574,10 @@ pub const Pardes = struct { const wait = Messages.messageWake(p, pane) orelse continue; frames = if (frames) |soonest| @min(soonest, wait) else wait; }; - // A paste's Enter waiting on its REPL goes at the end of its wait. + // A paste's Enter waiting on its REPL goes at the end of its wait, + // a time on the clock: the frames from `stepped_ns` that reach it. for (p.panes) |slot| if (slot) |pane| if (pane.repl_enter > 0) { - const wait = @max(1, (pane.repl_enter_due_ns -| p.stepped_ns) / animation.frame_ns); + const wait = @max(1, (pane.repl_enter_due_ns -| p.stepped_ns + animation.frame_ns - 1) / animation.frame_ns); frames = if (frames) |soonest| @min(soonest, wait) else wait; }; return if (frames) |n| p.stepped_ns + n * animation.frame_ns else null; -- cgit v1.3