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>
65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
/* 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";
|
|
|
|
describe("parseMessage", () => {
|
|
it("splits a gloss block into one entry per = line", () => {
|
|
const p = parseMessage(
|
|
"::gloss\n나 | S | I\n가 | V | go\n= I go.\n밥 | O | rice\n먹어 | V | eat\n= I eat rice.\n::",
|
|
);
|
|
expect(p.gloss).toHaveLength(2);
|
|
expect(p.gloss![0]!.en).toBe("I go.");
|
|
expect(p.gloss![0]!.parts.map((x) => x.ko)).toEqual(["나", "가"]);
|
|
expect(p.gloss![1]!.en).toBe("I eat rice.");
|
|
expect(p.gloss![1]!.parts.map((x) => x.ko)).toEqual(["밥", "먹어"]);
|
|
});
|
|
|
|
it("leaves a single-sentence block exactly as parse() produced it", () => {
|
|
const src = "::gloss\n저는 | T | I | 는\n갔어 | V | went | 었\n= I went.\n::";
|
|
const p = parseMessage(src);
|
|
expect(p.gloss).toHaveLength(1);
|
|
expect(p.gloss![0]!.en).toBe("I went.");
|
|
expect(p.gloss![0]!.parts[0]!.highlight).toBe("는");
|
|
});
|
|
|
|
it("keeps a trailing sentence that has no = line", () => {
|
|
const p = parseMessage("::gloss\n나 | S | I\n= I.\n밥 | O | rice\n::");
|
|
expect(p.gloss).toHaveLength(2);
|
|
expect(p.gloss![1]!.en).toBe("");
|
|
});
|
|
|
|
it("does not disturb the other blocks", () => {
|
|
const p = parseMessage(
|
|
[
|
|
"Have a look.",
|
|
"::gloss",
|
|
"나 | S | I",
|
|
"= I.",
|
|
"밥 | O | rice",
|
|
"= Rice.",
|
|
"::",
|
|
"::task translate",
|
|
"밥 먹어",
|
|
"::",
|
|
"::words",
|
|
"밥 | rice",
|
|
"::",
|
|
"::progress 55 | coming along",
|
|
].join("\n"),
|
|
);
|
|
expect(p.body).toBe("Have a look.");
|
|
expect(p.gloss).toHaveLength(2);
|
|
expect(p.task!.type).toBe("translate");
|
|
expect(p.words).toHaveLength(1);
|
|
expect(p.progress!.score).toBe(55);
|
|
});
|
|
|
|
it("passes a message with no gloss block straight through", () => {
|
|
const p = parseMessage("Just prose.\n::progress 10 | early days");
|
|
expect(p.gloss).toBeNull();
|
|
expect(p.body).toBe("Just prose.");
|
|
});
|
|
});
|