F7.1: intra-diagram mermaid diffing (#22) #28
@@ -1,4 +1,63 @@
|
||||
// stub, replaced in Tasks 4-5
|
||||
/**
|
||||
* mermaidSequenceDiff — F7.1 (#22). Pure sequence-diagram parser + diff/emit.
|
||||
* Mermaid sequence diagrams have NO per-message color directive; the only
|
||||
* per-message hook is the `rect rgb(...) … end` background block. So the emitter
|
||||
* rebuilds the message stream (ghosted-removed messages re-inserted at their
|
||||
* baseline position) wrapping each added/changed/removed message in a one-message
|
||||
* tinted rect, and re-declares removed participants (INV-29/31). Deterministic;
|
||||
* no vscode, no DOM.
|
||||
*/
|
||||
import { diffArrays } from "diff";
|
||||
import { CW_COLORS } from "./mermaidDiff";
|
||||
|
||||
export interface SeqDiagram {
|
||||
header: string;
|
||||
participants: string[];
|
||||
/** Message/other statement lines, verbatim & trimmed, in order. */
|
||||
statements: string[];
|
||||
}
|
||||
|
||||
// `A->>B: text`, plus ->, -->, -->>, -x, --x, -), --) variants. Captures from/to.
|
||||
const MSG = /^([A-Za-z0-9_]+)\s*(-{1,2}(?:>>?|x|\)))\s*([A-Za-z0-9_]+)\s*:/;
|
||||
const PARTICIPANT = /^(?:participant|actor)\s+([A-Za-z0-9_]+)/;
|
||||
|
||||
export function parseSequence(source: string): SeqDiagram {
|
||||
const lines = source.split(/\r?\n/);
|
||||
let header = "";
|
||||
const declared: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
const statements: string[] = [];
|
||||
const addP = (p: string) => {
|
||||
if (!seen.has(p)) {
|
||||
seen.add(p);
|
||||
declared.push(p);
|
||||
}
|
||||
};
|
||||
for (const rawLine of lines) {
|
||||
const line = rawLine.trim();
|
||||
if (line === "" || line.startsWith("%%")) continue;
|
||||
if (!header && /^sequenceDiagram\b/.test(line)) {
|
||||
header = line;
|
||||
continue;
|
||||
}
|
||||
const pm = line.match(PARTICIPANT);
|
||||
if (pm) {
|
||||
addP(pm[1]);
|
||||
continue;
|
||||
}
|
||||
const mm = line.match(MSG);
|
||||
if (mm) {
|
||||
addP(mm[1]);
|
||||
addP(mm[3]);
|
||||
}
|
||||
statements.push(line);
|
||||
}
|
||||
return { header: header || "sequenceDiagram", participants: declared, statements };
|
||||
}
|
||||
|
||||
// Replaced in Task 5 with the real diff/emit.
|
||||
export function diffSequence(_beforeSrc: string, currentSrc: string): string {
|
||||
void diffArrays;
|
||||
void CW_COLORS;
|
||||
return currentSrc;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { parseSequence } from "../src/mermaidSequenceDiff";
|
||||
|
||||
describe("parseSequence", () => {
|
||||
it("captures explicit participants in order", () => {
|
||||
const d = parseSequence("sequenceDiagram\n participant A\n participant B\n A->>B: hi");
|
||||
expect(d.participants).toEqual(["A", "B"]);
|
||||
});
|
||||
|
||||
it("captures message statements verbatim (trimmed)", () => {
|
||||
const d = parseSequence("sequenceDiagram\n A->>B: hi\n B-->>A: bye");
|
||||
expect(d.statements).toEqual(["A->>B: hi", "B-->>A: bye"]);
|
||||
});
|
||||
|
||||
it("infers participants from messages when not declared", () => {
|
||||
const d = parseSequence("sequenceDiagram\n A->>B: hi\n B->>C: on");
|
||||
expect(d.participants).toEqual(["A", "B", "C"]);
|
||||
});
|
||||
|
||||
it("ignores comments and blank lines", () => {
|
||||
const d = parseSequence("sequenceDiagram\n%% note\n\n A->>B: hi");
|
||||
expect(d.statements).toEqual(["A->>B: hi"]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user