mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-09 02:27:05 +02:00
Refactor mindmap validation to handle multiple root nodes and add SquareNode support
This commit is contained in:
@@ -41,9 +41,10 @@ export class MindmapValidator {
|
|||||||
checkSingleRoot(doc: MindmapDoc, accept: ValidationAcceptor): void {
|
checkSingleRoot(doc: MindmapDoc, accept: ValidationAcceptor): void {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.debug('CHECKING SINGLE ROOT');
|
console.debug('CHECKING SINGLE ROOT');
|
||||||
let rootNodeFound = false;
|
let rootNodeIndentation;
|
||||||
|
|
||||||
for (const row of doc.MindmapRows) {
|
for (const row of doc.MindmapRows) {
|
||||||
|
console.debug('ROW BY ROW', row.indent);
|
||||||
// Skip non-node items (e.g., class decorations, icon decorations)
|
// Skip non-node items (e.g., class decorations, icon decorations)
|
||||||
if (
|
if (
|
||||||
!row.item ||
|
!row.item ||
|
||||||
@@ -52,18 +53,24 @@ export class MindmapValidator {
|
|||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
// Check if this is a root node (no indentation)
|
rootNodeIndentation === undefined && // Check if this is a root node (no indentation)
|
||||||
if (row.indent === undefined) {
|
row.indent === undefined
|
||||||
if (rootNodeFound) {
|
) {
|
||||||
// If we've already found a root node, report an error
|
rootNodeIndentation = 0;
|
||||||
accept('error', 'Multiple root nodes are not allowed in a mindmap.', {
|
} else if (row.indent === undefined) {
|
||||||
node: row,
|
console.debug('FAIL 1', rootNodeIndentation, row.indent);
|
||||||
property: 'item',
|
// If we've already found a root node, report an error
|
||||||
});
|
accept('error', 'Multiple root nodes are not allowed in a mindmap.', {
|
||||||
} else {
|
node: row,
|
||||||
rootNodeFound = true;
|
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',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ Item:
|
|||||||
|
|
||||||
// Use a special rule order to handle the parsing precedence
|
// Use a special rule order to handle the parsing precedence
|
||||||
Node:
|
Node:
|
||||||
CircleNode | OtherComplex | SimpleNode | RoundedNode;
|
CircleNode | OtherComplex | SimpleNode | RoundedNode | SquareNode;
|
||||||
|
|
||||||
// Specifically handle double parentheses case - highest priority
|
// Specifically handle double parentheses case - highest priority
|
||||||
CircleNode:
|
CircleNode:
|
||||||
@@ -29,6 +29,9 @@ CircleNode:
|
|||||||
RoundedNode:
|
RoundedNode:
|
||||||
(id=ID)? desc=(ROUNDED_STR_QUOTES|ROUNDED_STR);
|
(id=ID)? desc=(ROUNDED_STR_QUOTES|ROUNDED_STR);
|
||||||
|
|
||||||
|
SquareNode:
|
||||||
|
(id=ID)? desc=(SQUARE_STR_QUOTES|SQUARE_STR);
|
||||||
|
|
||||||
// Handle other complex node variants
|
// Handle other complex node variants
|
||||||
OtherComplex:
|
OtherComplex:
|
||||||
id=ID
|
id=ID
|
||||||
@@ -62,6 +65,8 @@ terminal CLASS_KEYWORD: ':::';
|
|||||||
terminal CIRCLE_STR: /\(\(([\s\S]*?)\)\)/;
|
terminal CIRCLE_STR: /\(\(([\s\S]*?)\)\)/;
|
||||||
terminal ROUNDED_STR_QUOTES: /\(\"([\s\S]*?)\"\)/;
|
terminal ROUNDED_STR_QUOTES: /\(\"([\s\S]*?)\"\)/;
|
||||||
terminal ROUNDED_STR: /\(([\s\S]*?)\)/;
|
terminal ROUNDED_STR: /\(([\s\S]*?)\)/;
|
||||||
|
terminal SQUARE_STR_QUOTES: /\[\"([\s\S]*?)\"\]/;
|
||||||
|
terminal SQUARE_STR: /\[([\s\S]*?)\]/;
|
||||||
// terminal CIRCLE_STR: /(?!\(\()[\s\S]+?(?!\(\()/;
|
// terminal CIRCLE_STR: /(?!\(\()[\s\S]+?(?!\(\()/;
|
||||||
terminal ID: /[a-zA-Z0-9_\-\.\/]+/;
|
terminal ID: /[a-zA-Z0-9_\-\.\/]+/;
|
||||||
terminal STRING: /"[^"]*"|'[^']*'/;
|
terminal STRING: /"[^"]*"|'[^']*'/;
|
||||||
|
@@ -15,6 +15,10 @@ export class MindmapValueConverter extends AbstractMermaidValueConverter {
|
|||||||
return input.replace('(', '').replace(')', '').trim();
|
return input.replace('(', '').replace(')', '').trim();
|
||||||
} else if (rule.name === 'ROUNDED_STR_QUOTES') {
|
} else if (rule.name === 'ROUNDED_STR_QUOTES') {
|
||||||
return input.replace('("', '').replace('")', '').trim();
|
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') {
|
} else if (rule.name === 'ARCH_TEXT_ICON') {
|
||||||
return input.replace(/["()]/g, '');
|
return input.replace(/["()]/g, '');
|
||||||
} else if (rule.name === 'ARCH_TITLE') {
|
} else if (rule.name === 'ARCH_TITLE') {
|
||||||
|
@@ -133,7 +133,7 @@ describe('Hierarchy (ported from mindmap.spec.ts)', () => {
|
|||||||
expect(child2Node.id).toBe('child2');
|
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 str = 'mindmap\nroot\nfakeRoot';
|
||||||
const result = await validatedParse(str, { validation: true });
|
const result = await validatedParse(str, { validation: true });
|
||||||
// Langium parser may not throw, but should have parserErrors
|
// 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);
|
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 str = 'mindmap\n root\n fakeRoot\nrealRootWrongPlace';
|
||||||
const result = parse(str);
|
const r2 = await validatedParse(str, { validation: true });
|
||||||
expect(result.parserErrors.length).toBeGreaterThan(0);
|
expect(r2.diagnostics?.length).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user