feat: Cursor in einer Beschreibung wählt ihren Knoten aus

Bisher fiel die Hervorhebung weg, sobald der Cursor von der Knotenzeile auf die
`"`-Zeile darunter rückte: Die trägt keinen Knoten, also fand die Zeilensuche
nichts. Falsche Auskunft — sie trägt keinen EIGENEN Knoten, gehört aber zu
einem, und wer in ihr schreibt, arbeitet an genau diesem.

Gilt für beide Formen, nicht nur die Kurzform: Die Zuordnung entsteht an
derselben Stelle im Parser, an der auch die `---`-Blöcke landen, und dort trägt
die Begründung sogar weiter — der Langtext steht am Dateiende, weit weg von
seinem Knoten. Zugeordnet werden Textzeilen, Block-Kopfzeile (sie nennt den
Knoten) und Leerzeilen innerhalb eines Blocks; nicht der `---`-Trenner und
nichts unter unbekannter ID.

`node.descLines` entsteht im Parser, der Renderer gibt sie als
`data-desc-lines="3 4 5"` aus, app.js trifft sie per `[data-desc-lines~="N"]`.
Bewusst getrennt von `descLines` (dem Text): Dort fallen Leerzeilen zu einem
Absatztrenner zusammen und Blocktext unter unbekannter ID kommt gar nicht an.

Beide Richtungen gehen jetzt über eine Auflösung (`nodeOfLine()`) — sonst wäre
der ausdrückliche Alt+Klick aus einer Beschreibung heraus stumm geblieben, und
das ist gerade der Ort, an dem man ihn braucht.

5 neue Tests (204 gesamt). SPEC §9 und D40 (Nachtrag 2) nachgezogen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-23 08:01:56 +02:00
co-authored by Claude Fable 5
parent e223734d58
commit 24a70ca49e
7 changed files with 122 additions and 13 deletions
+32
View File
@@ -123,6 +123,38 @@ describe('Darstellung — Tooltip, ”-Marke, aria', () => {
const {html} = render(`[ ] Ohne #auth`);
expect(html.match(/title="([^"]*)"/)[1]).not.toContain('─');
});
});
/* Der Cursor in einer Beschreibung wählt ihren Knoten aus (SPEC §9): Die Zeile
trägt keinen eigenen Knoten, gehört aber zu einem. Grundlage ist
`node.descLines`; app.js findet den Knoten darüber per `data-desc-lines~=`. */
describe('Zeilenzuordnung der Beschreibung (`descLines`)', () => {
it('ordnet `"`-Zeilen dem vorangehenden Knoten zu', () => {
const [a, b] = roots(`[ ] Erster\n " Eine Zeile.\n " Noch eine.\n[ ] Zweiter`);
expect(a.descLines).toEqual([2, 3]);
expect(b.descLines).toBe(null);
});
it('ordnet den `---`-Block samt Kopfzeile und Leerzeilen zu', () => {
const [n] = roots(`[ ] Auth #auth\n---\n#auth\n Erster Absatz.\n\n Zweiter Absatz.`);
expect(n.descLines).toEqual([3, 4, 5, 6]);
});
it('führt Kurz- und Langform am selben Knoten zusammen', () => {
const [n] = roots(`[ ] Auth #auth\n " Kurz.\n---\n#auth\n Lang.`);
expect(n.descLines).toEqual([2, 4, 5]);
});
it('ordnet nichts zu, wo es keinen Knoten gibt', () => {
const {roots: r, warnings} = parse(`---\n#fehlt\n Text.`);
expect(r).toEqual([]);
expect(warnings.map(w => w.type)).toEqual(['unknownDesc']);
});
it('gibt die Zeilen als `data-desc-lines` aus', () => {
const {html} = render(`[ ] Auth\n " Kapselt Login.`);
expect(html).toContain('data-desc-lines="2"');
});
it('ändert Knoten ohne Beschreibung nicht', () => {
const {html} = render(`[ ] Ohne`);