notation: \ am Zeilenende setzt die Zeile fort (SPEC §1, D59)
Eine Zeile trägt alles auf einmal — im mitgelieferten Plan ist die längste 122 Zeichen lang, und seit das Textfeld nicht mehr umbricht (D49) muss man dafür schieben. Ein `\` am Zeilenende verteilt sie jetzt auf mehrere Textzeilen, ohne dass ein neuer Knoten entsteht. Leerraum davor ist Pflicht (`… \`, nicht `…\`) — bewusst gegen die Shell-Gewohnheit: Ohne die Regel verschluckt ein Label wie `C:\temp\` stumm den folgenden Knoten, mit ihr bekommt man nur keine Fortsetzung, was man sofort sieht. Der laute Fehler ist der bessere. Umgesetzt als Vor-Durchlauf `logicalLines()`: Die Folgezeile ist Teil der Zeile, bevor irgendetwas aus ihr gelesen wird — die Extraktionsreihenfolge aus §1 bleibt unangetastet. Alles gehört zur ersten Zeile: Einrückung, Warnungs-Zeilennummer, Rückschreiber; der Cursor in der Fortsetzung wählt ihren Knoten aus, wie bei Beschreibungszeilen. SPEC §1/§9, llms.md, Legende in neun Sprachen, D59; 20 neue Tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
257aab08dd
commit
410dacdc6c
@@ -1943,6 +1943,7 @@ const I18N = {
|
||||
hint_size:"Aufwand als T-Shirt-Größe in Klammern, Link einfach als URL anhängen:",
|
||||
hint_break:"Ab (M) gilt: weiter untergliedern — fehlt die Untergliederung, erscheint ein Platzhalter im Diagramm.",
|
||||
hint_comment:"Kommentare mit %% — als ganze Zeile oder am Zeilenende.",
|
||||
hint_cont:"Leerzeichen und \\ am Zeilenende — die nächste Zeile gehört noch dazu.",
|
||||
hint_people:"Personen mit @name — erscheinen unten rechts am Knoten.",
|
||||
hint_id:"Knoten-ID mit #name: vor dem Titel — erscheint im Tooltip des Knotens.",
|
||||
hint_deps:"Abhängigkeiten mit :#name,#name — erscheinen im Tooltip.",
|
||||
@@ -2043,6 +2044,7 @@ const I18N = {
|
||||
hint_size:"Effort as a T-shirt size in parentheses; add a link simply as a URL:",
|
||||
hint_break:"From (M) on: break it down further — if the breakdown is missing, a placeholder appears in the diagram.",
|
||||
hint_comment:"Comments with %% — whole line or at the end of a line.",
|
||||
hint_cont:"A space and \\ at the end of a line — the next line still belongs to it.",
|
||||
hint_people:"People with @name — shown at the bottom-right of the node.",
|
||||
hint_id:"Node ID with #name: before the title — shown in the node's tooltip.",
|
||||
hint_deps:"Dependencies with :#name,#name — shown in the tooltip.",
|
||||
@@ -2143,6 +2145,7 @@ const I18N = {
|
||||
hint_size:"Esfuerzo como talla de camiseta entre paréntesis; añade un enlace simplemente como URL:",
|
||||
hint_break:"A partir de (M): sigue desglosando — si falta el desglose, aparece un marcador de posición en el diagrama.",
|
||||
hint_comment:"Comentarios con %% — línea completa o al final de la línea.",
|
||||
hint_cont:"Un espacio y \\ al final de una línea — la línea siguiente sigue perteneciendo a ella.",
|
||||
hint_people:"Personas con @nombre — aparecen abajo a la derecha del nodo.",
|
||||
hint_id:"ID de nodo con #nombre: delante del título — visible en el tooltip del nodo.",
|
||||
hint_deps:"Dependencias con :#nombre,#nombre — visibles en el tooltip.",
|
||||
@@ -2243,6 +2246,7 @@ const I18N = {
|
||||
hint_size:"Effort en taille de T-shirt entre parenthèses ; ajoutez un lien simplement comme URL :",
|
||||
hint_break:"À partir de (M) : décomposer davantage — si la décomposition manque, un espace réservé apparaît dans le diagramme.",
|
||||
hint_comment:"Commentaires avec %% — ligne entière ou en fin de ligne.",
|
||||
hint_cont:"Une espace et \\ en fin de ligne — la ligne suivante en fait encore partie.",
|
||||
hint_people:"Personnes avec @nom — affichées en bas à droite du nœud.",
|
||||
hint_id:"ID de nœud avec #nom : devant le titre — visible dans l’infobulle du nœud.",
|
||||
hint_deps:"Dépendances avec :#nom,#nom — visibles dans l’infobulle.",
|
||||
@@ -2343,6 +2347,7 @@ const I18N = {
|
||||
hint_size:"Nakład jako rozmiar koszulki w nawiasach; link dodaj po prostu jako URL:",
|
||||
hint_break:"Od (M): dziel dalej — gdy brakuje podziału, w diagramie pojawia się symbol zastępczy.",
|
||||
hint_comment:"Komentarze z %% — cały wiersz lub na końcu wiersza.",
|
||||
hint_cont:"Spacja i \\ na końcu wiersza — następny wiersz nadal do niego należy.",
|
||||
hint_people:"Osoby z @nazwa — pokazywane w prawym dolnym rogu węzła.",
|
||||
hint_id:"ID węzła przez #nazwa: przed tytułem — widoczne w podpowiedzi węzła.",
|
||||
hint_deps:"Zależności przez :#nazwa,#nazwa — widoczne w podpowiedzi.",
|
||||
@@ -2443,6 +2448,7 @@ const I18N = {
|
||||
hint_size:"Трудоёмкость как размер футболки в скобках; ссылку добавьте просто как URL:",
|
||||
hint_break:"С (M): дробите дальше — если декомпозиции нет, в диаграмме появляется заполнитель.",
|
||||
hint_comment:"Комментарии через %% — вся строка или в конце строки.",
|
||||
hint_cont:"Пробел и \\ в конце строки — следующая строка всё ещё относится к ней.",
|
||||
hint_people:"Люди через @имя — показываются справа внизу узла.",
|
||||
hint_id:"ID узла через #имя: перед заголовком — виден во всплывающей подсказке узла.",
|
||||
hint_deps:"Зависимости через :#имя,#имя — видны в подсказке.",
|
||||
@@ -2543,6 +2549,7 @@ const I18N = {
|
||||
hint_size:"प्रयास कोष्ठक में टी-शर्ट आकार के रूप में; लिंक बस URL के रूप में जोड़ें:",
|
||||
hint_break:"(M) से आगे: और विभाजित करें — विभाजन न होने पर आरेख में प्लेसहोल्डर दिखता है।",
|
||||
hint_comment:"%% से टिप्पणियाँ — पूरी पंक्ति या पंक्ति के अंत में।",
|
||||
hint_cont:"पंक्ति के अंत में स्पेस और \\ — अगली पंक्ति उसी की बनी रहती है।",
|
||||
hint_people:"@नाम से व्यक्ति — नोड के नीचे-दाएँ दिखते हैं।",
|
||||
hint_id:"शीर्षक से पहले #नाम: के रूप में नोड आईडी — नोड के टूलटिप में दिखती है।",
|
||||
hint_deps:":#नाम,#नाम से निर्भरताएँ — टूलटिप में दिखती हैं।",
|
||||
@@ -2643,6 +2650,7 @@ const I18N = {
|
||||
hint_size:"用括号中的 T 恤尺码表示工作量;链接直接作为 URL 附加:",
|
||||
hint_break:"从 (M) 起:继续细分——若缺少细分,图表中会出现占位符。",
|
||||
hint_comment:"用 %% 注释——整行或行尾。",
|
||||
hint_cont:"行尾的空格加 \\ —— 下一行仍属于这一行。",
|
||||
hint_people:"用 @姓名 表示人员——显示在节点右下角。",
|
||||
hint_id:"用 #名称: 写在标题前指定节点 ID——显示在节点提示中。",
|
||||
hint_deps:"用 :#名称,#名称 表示依赖——显示在提示中。",
|
||||
@@ -2743,6 +2751,7 @@ const I18N = {
|
||||
hint_size:"工数は括弧内の T シャツサイズで;リンクは URL としてそのまま追加:",
|
||||
hint_break:"(M) 以上:さらに分解 — 分解がないと図にプレースホルダーが表示されます。",
|
||||
hint_comment:"%% でコメント — 行全体または行末。",
|
||||
hint_cont:"行末の空白と \\ — 次の行も同じ行に属します。",
|
||||
hint_people:"@名前 で担当者 — ノードの右下に表示されます。",
|
||||
hint_id:"タイトルの前に #名前: でノード ID — ノードのツールチップに表示されます。",
|
||||
hint_deps:":#名前,#名前 で依存関係 — ツールチップに表示されます。",
|
||||
@@ -2780,6 +2789,7 @@ function buildHint(){
|
||||
<code>- [ ] Backend (L) https://…</code><br>
|
||||
${esc(t('hint_break'))}<br>
|
||||
${esc(t('hint_comment'))}
|
||||
${esc(t('hint_cont'))}
|
||||
${esc(t('hint_people'))}
|
||||
${esc(t('hint_id'))}
|
||||
${esc(t('hint_deps'))}
|
||||
|
||||
+55
-4
@@ -38,6 +38,41 @@ export function setFoldMark(line, mark){
|
||||
|
||||
const RE_LINE = /^([ \t]*)([-|+]|=(?=[ \t]))?\s*(?:([><])(?=[ \t])\s*)?(?:\[([^\]])\]\s*)?(?:([><])(?=[ \t])\s*)?(.*)$/;
|
||||
const RE_ID_TOKEN = /(^|\s)#([\p{L}\p{N}._-]+)/u;
|
||||
/* Fortsetzungszeile (SPEC §1): Leerraum, dann `\` als letztes Zeichen. Der
|
||||
Leerraum davor ist Pflicht — ohne ihn verschluckte ein Label wie `C:\temp\`
|
||||
stumm den folgenden Knoten. */
|
||||
const RE_CONT = /(^|[ \t])\\[ \t]*$/;
|
||||
const RE_SEP = /^[ \t]*-{3,}[ \t]*$/;
|
||||
|
||||
/* Text → logische Zeilen: `%%`-Kommentare weg (SPEC §1, Schritt 1), dann
|
||||
Fortsetzungen anhängen (Schritt 1b). Ergebnis je Eintrag:
|
||||
`{raw, line, cont}` — `line` ist die Nummer der ERSTEN Textzeile (sie trägt
|
||||
Einrückung, Gate und Statusbox und wird von allen Rückschreibern angefasst),
|
||||
`cont` sind die Nummern der angehängten.
|
||||
|
||||
Verbunden wird mit genau einem Leerzeichen, die Einrückung der Folgezeile
|
||||
entfällt — bis auf den Fall, dass von der ersten Zeile nur die Einrückung
|
||||
übrig bleibt (` \`): Dann trägt sie die Ebene und muss stehen bleiben.
|
||||
Hinter dem `---`-Trenner wird nicht mehr verbunden; dort ist der
|
||||
Zeilenumbruch Absatzstruktur (SPEC §1). */
|
||||
export function logicalLines(text){
|
||||
const lines = text.split('\n');
|
||||
const out = [];
|
||||
let inDesc = false;
|
||||
for(let i = 0; i < lines.length; i++){
|
||||
const rec = {raw: lines[i].replace(/%%.*$/, ''), line: i + 1, cont: []};
|
||||
if(!inDesc && RE_SEP.test(rec.raw)) inDesc = true;
|
||||
while(!inDesc && RE_CONT.test(rec.raw)){
|
||||
rec.raw = rec.raw.replace(RE_CONT, '$1');
|
||||
if(i + 1 >= lines.length) break; /* letzte Zeile: der `\` entfällt einfach */
|
||||
if(rec.raw && !/[ \t]$/.test(rec.raw)) rec.raw += ' ';
|
||||
rec.raw += lines[++i].replace(/%%.*$/, '').replace(/^[ \t]*/, '');
|
||||
rec.cont.push(i + 1);
|
||||
}
|
||||
out.push(rec);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Kurzschreibweise der Knoten-ID auflösen: `#.kc` unter `#prod-stage` wird zu
|
||||
`#prod-stage.kc` (D55). Das ist eine **Eingabehilfe**, keine Notation — die
|
||||
@@ -55,12 +90,19 @@ export function expandShortIds(text){
|
||||
const lines = text.split('\n');
|
||||
const stack = []; /* {width, id} — auch Knoten OHNE ID stehen drin */
|
||||
let changed = false;
|
||||
let fortsetzung = false; /* die vorige Zeile endete auf `\` (SPEC §1) */
|
||||
for(let i = 0; i < lines.length; i++){
|
||||
const raw = lines[i];
|
||||
if(/^\s*-{3,}\s*$/.test(raw)) break;
|
||||
const k = raw.indexOf('%%');
|
||||
const head = k === -1 ? raw : raw.slice(0, k);
|
||||
const tail = k === -1 ? '' : raw.slice(k);
|
||||
/* Eine Fortsetzungszeile trägt keinen eigenen Knoten: Sie darf weder den
|
||||
Vorfahren-Stapel verändern noch als Ort einer Kurzform gelten — die ID
|
||||
steht an der ersten Zeile. */
|
||||
const warFortsetzung = fortsetzung;
|
||||
fortsetzung = RE_CONT.test(head);
|
||||
if(warFortsetzung) continue;
|
||||
const m = head.match(RE_LINE);
|
||||
if(!m) continue;
|
||||
const body = m[6];
|
||||
@@ -100,8 +142,10 @@ const REALIZED = new Set(['arbeit', 'durchstich', 'fertig', 'prod']);
|
||||
`desc` (SPEC §11/D40) ist der Beschreibungstext: `"`-Zeilen unter dem
|
||||
Knoten (Kurzform) und ID-Blöcke aus dem `---`-Beschreibungsteil (Langform),
|
||||
in Dokumentreihenfolge mit Zeilenumbrüchen zusammengefügt; null ohne.
|
||||
`descLines` sind die ZEILENNUMMERN dieser Beschreibung (SPEC §9): Steht der
|
||||
Cursor dort, gilt dieser Knoten als ausgewählt.
|
||||
`descLines` sind die ZEILENNUMMERN der Zeilen, die zu diesem Knoten gehören,
|
||||
ohne einen eigenen zu tragen (SPEC §9): die der Beschreibung und die der
|
||||
Fortsetzungen hinter `\`. Steht der Cursor dort, gilt dieser Knoten als
|
||||
ausgewählt.
|
||||
`fold` ('>'|'<'|null, SPEC §1/D38) ist nur der ANFANGSZUSTAND der Faltung —
|
||||
den wirksamen Zustand rechnet `initialCollapsed()` in model.js.
|
||||
`deps` sind ID-Strings, keine Knoten-Referenzen — aufgelöst wird erst beim
|
||||
@@ -147,8 +191,11 @@ export function parse(text){
|
||||
arr.push(text);
|
||||
};
|
||||
|
||||
text.split('\n').forEach((raw, i) => {
|
||||
raw = raw.replace(/%%.*$/, ''); /* %%-Kommentare entfernen (Mermaid-Konvention) */
|
||||
/* Kommentare sind bereits entfernt und Fortsetzungszeilen angehängt
|
||||
(`logicalLines`, SPEC §1 Schritt 1 und 1b). `i` ist ab hier die Nummer der
|
||||
ERSTEN Textzeile einer logischen Zeile, minus eins. */
|
||||
logicalLines(text).forEach(rec => {
|
||||
const raw = rec.raw, i = rec.line - 1;
|
||||
/* Trenner `---` (SPEC §11, D40): drei oder mehr Bindestriche, umgebender
|
||||
Leerraum erlaubt — ab hier gilt der Beschreibungsteil. Es gibt keinen
|
||||
Schlusszaun; weitere Trennzeilen darin haben keine Bedeutung. */
|
||||
@@ -265,6 +312,10 @@ export function parse(text){
|
||||
parent.children.push(node);
|
||||
stack.push({node, width});
|
||||
lastNode = node;
|
||||
/* Die Fortsetzungszeilen gehören diesem Knoten (SPEC §1/§9) — genau wie
|
||||
Beschreibungszeilen tragen sie keinen eigenen und wählen ihn deshalb
|
||||
aus, wenn der Cursor darin steht. */
|
||||
rec.cont.forEach(n => ownLine(node, n - 1));
|
||||
if(id != null && !idNodes.has(id)) idNodes.set(id, node);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user