Deliver a line to a single line listener without the listener snapshot (#2531)

This commit is contained in:
Chris Tate
2026-10-29 15:03:53 -05:00
committed by GitHub
4 changed files with 50 additions and 0 deletions
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/readline-line-listener-changes.ts"
],
"diags": []
}
+11
View File
@@ -105,6 +105,17 @@ void scr_rl_emit(ScrRl *rl, int ev, ScrStr *line, ScrError *err) {
RlList *l = &rl->ev[ev];
size_t n = l->n;
if (n == 0) return;
/* One persistent line listener (the usual 'line' handler): no snapshot
* and no once-listener pass. The retain keeps the closure alive if the
* listener removes itself. */
if (n == 1 && ev == RL_EV_LINE && !l->v[0].once && !l->v[0].iter) {
RlEntry e = l->v[0];
if (scr_exc_pending()) return;
scr_closure_retain(e.cb);
((ScrRlLineFn)e.fn)(e.cb, scr_str_retain(line));
scr_closure_release(e.cb);
return;
}
/* Most events have a listener or two: their snapshot stays on the stack. */
RlEntry small[4];
RlEntry *snap = n <= 4 ? small : malloc(n * sizeof *snap);
@@ -0,0 +1,32 @@
// 'line' listeners that change while a line is delivered: a listener added
// by the only line listener starts with the next line, after which both
// run in order; a once listener next to a persistent one runs for one line;
// and no listener sees a line twice.
import { createInterface } from "node:readline";
import { Readable } from "node:stream";
const text = "a\nb\nc\nd\ne\nf\n";
const first = createInterface({ input: Readable.from([text]) });
let seen = 0;
const counting = (line: string): void => {
seen++;
console.log("counting", line);
if (seen === 2) first.on("line", (later: string) => console.log("later", later));
};
first.on("line", counting);
first.on("close", () => {
console.log("first closed", seen);
const second = createInterface({ input: Readable.from([text]) });
second.once("line", (line: string) => console.log("once", line));
second.on("line", (line: string) => console.log("every", line));
second.on("close", () => {
console.log("second closed");
const third = createInterface({ input: Readable.from(["x\ny\n", "z\n"]) });
const seenLines: string[] = [];
third.on("line", (line: string) => seenLines.push(line));
third.on("close", () => console.log("third", seenLines.join(",")));
});
});
@@ -0,0 +1 @@
{ "plain": 4500, "san": 7400 }