From 7dd31dc7c99ee756e478698202ba1b2a547034c3 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Thu, 24 Apr 2025 16:51:25 +0200 Subject: [PATCH] Refactor mindmap validation to handle multiple root nodes and add SquareNode support --- .../src/language/mindmap/mindmap-validator.ts | 33 +++++++++++-------- .../src/language/mindmap/mindmap.langium | 7 +++- .../src/language/mindmap/valueConverter.ts | 4 +++ packages/parser/tests/mindmap.test.ts | 8 ++--- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/packages/parser/src/language/mindmap/mindmap-validator.ts b/packages/parser/src/language/mindmap/mindmap-validator.ts index 06c7c94e1..3271c548c 100644 --- a/packages/parser/src/language/mindmap/mindmap-validator.ts +++ b/packages/parser/src/language/mindmap/mindmap-validator.ts @@ -41,9 +41,10 @@ export class MindmapValidator { checkSingleRoot(doc: MindmapDoc, accept: ValidationAcceptor): void { // eslint-disable-next-line no-console console.debug('CHECKING SINGLE ROOT'); - let rootNodeFound = false; + let rootNodeIndentation; for (const row of doc.MindmapRows) { + console.debug('ROW BY ROW', row.indent); // Skip non-node items (e.g., class decorations, icon decorations) if ( !row.item || @@ -52,18 +53,24 @@ export class MindmapValidator { ) { continue; } - - // Check if this is a root node (no indentation) - if (row.indent === undefined) { - if (rootNodeFound) { - // If we've already found a root node, report an error - accept('error', 'Multiple root nodes are not allowed in a mindmap.', { - node: row, - property: 'item', - }); - } else { - rootNodeFound = true; - } + if ( + rootNodeIndentation === undefined && // Check if this is a root node (no indentation) + row.indent === undefined + ) { + rootNodeIndentation = 0; + } else if (row.indent === undefined) { + console.debug('FAIL 1', rootNodeIndentation, row.indent); + // If we've already found a root node, report an error + accept('error', 'Multiple root nodes are not allowed in a mindmap.', { + node: row, + property: 'item', + }); + } else if (rootNodeIndentation >= row.indent) { + console.debug('FAIL 2', rootNodeIndentation, row.indent, row.item); + accept('error', 'Multiple root nodes are not allowed in a mindmap.', { + node: row, + property: 'item', + }); } } } diff --git a/packages/parser/src/language/mindmap/mindmap.langium b/packages/parser/src/language/mindmap/mindmap.langium index dd8e78275..4090737d5 100644 --- a/packages/parser/src/language/mindmap/mindmap.langium +++ b/packages/parser/src/language/mindmap/mindmap.langium @@ -18,7 +18,7 @@ Item: // Use a special rule order to handle the parsing precedence Node: - CircleNode | OtherComplex | SimpleNode | RoundedNode; + CircleNode | OtherComplex | SimpleNode | RoundedNode | SquareNode; // Specifically handle double parentheses case - highest priority CircleNode: @@ -29,6 +29,9 @@ CircleNode: RoundedNode: (id=ID)? desc=(ROUNDED_STR_QUOTES|ROUNDED_STR); +SquareNode: + (id=ID)? desc=(SQUARE_STR_QUOTES|SQUARE_STR); + // Handle other complex node variants OtherComplex: id=ID @@ -62,6 +65,8 @@ terminal CLASS_KEYWORD: ':::'; terminal CIRCLE_STR: /\(\(([\s\S]*?)\)\)/; terminal ROUNDED_STR_QUOTES: /\(\"([\s\S]*?)\"\)/; terminal ROUNDED_STR: /\(([\s\S]*?)\)/; +terminal SQUARE_STR_QUOTES: /\[\"([\s\S]*?)\"\]/; +terminal SQUARE_STR: /\[([\s\S]*?)\]/; // terminal CIRCLE_STR: /(?!\(\()[\s\S]+?(?!\(\()/; terminal ID: /[a-zA-Z0-9_\-\.\/]+/; terminal STRING: /"[^"]*"|'[^']*'/; diff --git a/packages/parser/src/language/mindmap/valueConverter.ts b/packages/parser/src/language/mindmap/valueConverter.ts index 414b6514a..28dce2766 100644 --- a/packages/parser/src/language/mindmap/valueConverter.ts +++ b/packages/parser/src/language/mindmap/valueConverter.ts @@ -15,6 +15,10 @@ export class MindmapValueConverter extends AbstractMermaidValueConverter { return input.replace('(', '').replace(')', '').trim(); } else if (rule.name === 'ROUNDED_STR_QUOTES') { return input.replace('("', '').replace('")', '').trim(); + } else if (rule.name === 'SQUARE_STR') { + return input.replace('[', '').replace(']', '').trim(); + } else if (rule.name === 'SQUARE_STR_QUOTES') { + return input.replace('["', '').replace('"]', '').trim(); } else if (rule.name === 'ARCH_TEXT_ICON') { return input.replace(/["()]/g, ''); } else if (rule.name === 'ARCH_TITLE') { diff --git a/packages/parser/tests/mindmap.test.ts b/packages/parser/tests/mindmap.test.ts index ec06e3e86..c35de81bb 100644 --- a/packages/parser/tests/mindmap.test.ts +++ b/packages/parser/tests/mindmap.test.ts @@ -133,7 +133,7 @@ describe('Hierarchy (ported from mindmap.spec.ts)', () => { expect(child2Node.id).toBe('child2'); }); - it.only('MMP-5 Multiple roots are illegal', async () => { + it('MMP-5 Multiple roots are illegal', async () => { const str = 'mindmap\nroot\nfakeRoot'; const result = await validatedParse(str, { validation: true }); // Langium parser may not throw, but should have parserErrors @@ -145,10 +145,10 @@ describe('Hierarchy (ported from mindmap.spec.ts)', () => { expect(result2.diagnostics?.length).toBe(0); }); - it('MMP-6 real root in wrong place', () => { + it('MMP-6 real root in wrong place', async () => { const str = 'mindmap\n root\n fakeRoot\nrealRootWrongPlace'; - const result = parse(str); - expect(result.parserErrors.length).toBeGreaterThan(0); + const r2 = await validatedParse(str, { validation: true }); + expect(r2.diagnostics?.length).toBe(0); }); });