fix(tutor): find gloss blocks in the raw text — the new parse() still leaks
The 16 Sep lib fixed both defects gloss.ts used to work around: a gloss
block closes at each "=" line, and every block is collected. So the
sentence splitting goes.
Three defects remain in how a block ENDS, measured on the new lib:
· deleting one block still deletes the "::" of the next, so a whole
::words block reaches the prose as "words" plus its rows;
· an unclosed gloss still swallows the following paragraph as parts;
· two gloss blocks written back to back share one terminator, and lib
never sees the second.
The old rebuild also only re-parsed the FIRST gloss block, which against
a lib that accumulates would have dropped every block after it.
One pass over the raw text now yields both the prose and each gloss
block's content, a gloss block ending where its rows end; lib parses each
block on its own, so the row format is still entirely lib's.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
/* The ::task block leaking into the message body.
|
||||
/* Blocks leaking into the message body.
|
||||
|
||||
Reported from the app: the raw text "::task translate" and its five
|
||||
sentences appeared as prose above the exercise those same lines had
|
||||
already been rendered into.
|
||||
Reported from the app, twice: raw exercise markup appeared as prose above
|
||||
the exercise it had already been rendered into.
|
||||
|
||||
It is lib/blocks.js, not the model. parse() deletes each block with a
|
||||
regex whose terminator (?:\n::|$) is part of the match, so deleting one
|
||||
@@ -180,6 +179,11 @@ describe("an unclosed ::gloss block", () => {
|
||||
"나 바다",
|
||||
].join("\n");
|
||||
|
||||
it("lib alone reads the following prose as gloss parts — pinned as it is", () => {
|
||||
const parts = (parse(RAW).gloss ?? []).flatMap((g) => g.parts.map((p) => p.ko));
|
||||
expect(parts).toContain("Now practice these.");
|
||||
});
|
||||
|
||||
it("ends the block where its rows end", () => {
|
||||
expect(glossContent("나 | S | I\n= I sea\n\nprose here\nmore prose")).toBe("나 | S | I\n= I sea\n");
|
||||
});
|
||||
@@ -201,6 +205,14 @@ describe("an unclosed ::gloss block", () => {
|
||||
expect(body).not.toMatch(/::/);
|
||||
});
|
||||
|
||||
it("finds a second gloss block written straight after the first", () => {
|
||||
// Both blocks share one terminator, so lib alone never sees the second.
|
||||
const src = "::gloss\n나 | S | I\n= I.\n::gloss\n밥 | O | rice\n= Rice.\n::";
|
||||
expect(parse(src).gloss).toHaveLength(1);
|
||||
expect(parseMessage(src).gloss!.map((g) => g.en)).toEqual(["I.", "Rice."]);
|
||||
expect(parseMessage(src).body).toBe("");
|
||||
});
|
||||
|
||||
it("leaves a properly closed block exactly as lib parses it", () => {
|
||||
const closed = "::gloss\n물 | N | water\n= Water.\n::\nAfter.";
|
||||
const g = parseMessage(closed).gloss ?? [];
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* The multi-sentence gloss workaround. */
|
||||
/* Gloss blocks as the app renders them: lib parses the rows, parseMessage
|
||||
decides where each block ends. */
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { parseMessage } from "@app/domain/gloss.js";
|
||||
|
||||
Reference in New Issue
Block a user