From b46c28425e43268be586600b512d096895add6ed Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Wed, 19 Jul 2023 22:49:39 -0300 Subject: [PATCH 01/45] Add text state and tests --- .../flowchart/parser/flow-text.spec.js | 188 +++++++++++++++ .../src/diagrams/flowchart/parser/flow.jison | 225 ++++++++++-------- 2 files changed, 310 insertions(+), 103 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index db43e75bf..6ab5ab2cd 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -305,6 +305,194 @@ describe('[Text] when parsing', () => { expect(vert['C'].type).toBe('round'); expect(vert['C'].text).toBe('Chimpansen hoppar'); }); + + const keywords = [ + 'graph', + 'flowchart', + 'flowchart-elk', + 'style', + 'default', + 'linkStyle', + 'interpolate', + 'classDef', + 'class', + 'href', + 'call', + 'click', + '_self', + '_blank', + '_parent', + '_top', + 'end', + 'subgraph', + 'kitty', + ]; + + it.each(keywords)('should handle %s keyword in square vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B[This node has a ${keyword} as text];` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('square'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in round vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B(This node has a ${keyword} as text);` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('round'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in diamond vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B{This node has a ${keyword} as text};` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('diamond'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in doublecircle vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B(((This node has a ${keyword} as text)));` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('doublecircle'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in ellipse vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B(-This node has a ${keyword} as text-);` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('ellipse'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in stadium vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B([This node has a ${keyword} as text]);` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('stadium'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in subroutine vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B[[This node has a ${keyword} as text]];` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('subroutine'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in rect vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B[|borders:lt|This node has a ${keyword} as text];` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('rect'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in cylinder vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B[(This node has a ${keyword} as text)];` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('cylinder'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in hexagon vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B{{This node has a ${keyword} as text}};` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('hexagon'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in odd vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B>This node has a ${keyword} as text];` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('odd'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in trapezoid vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B[/This node has a ${keyword} as text\\];` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('trapezoid'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in inv_trapezoid vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B[\\This node has a ${keyword} as text/];` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('inv_trapezoid'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in lean_right vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B[/This node has a ${keyword} as text/];` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('lean_right'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + + it.each(keywords)('should handle %s keyword in lean_left vertex', function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B[\\This node has a ${keyword} as text\\];` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('lean_left'); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); + it('should handle åäö and minus', function () { const res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};'); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 70fb49162..d0e65913a 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -13,6 +13,9 @@ %x acc_descr_multiline %x dir %x vertex +%x text +%x ellipseText +%x trapText %x click %x href %x callbackname @@ -23,31 +26,32 @@ %x close_directive %% -\%\%\{ { this.begin('open_directive'); return 'open_directive'; } -((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; } -":" { this.popState(); this.begin('arg_directive'); return ':'; } -\}\%\% { this.popState(); this.popState(); return 'close_directive'; } -((?:(?!\}\%\%).|\n)*) return 'arg_directive'; -accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; } -(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; } -accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; } -(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; } -accDescr\s*"{"\s* { this.begin("acc_descr_multiline");} +\%\%\{ { this.begin('open_directive'); return 'open_directive'; } +((?:(?!\}\%\%)[^:.])*) { this.begin('type_directive'); return 'type_directive'; } +":" { this.popState(); this.begin('arg_directive'); return ':'; } +\}\%\% { this.popState(); this.popState(); return 'close_directive'; } +((?:(?!\}\%\%).|\n)*) return 'arg_directive'; +accTitle\s*":"\s* { this.begin("acc_title");return 'acc_title'; } +(?!\n|;|#)*[^\n]* { this.popState(); return "acc_title_value"; } +accDescr\s*":"\s* { this.begin("acc_descr");return 'acc_descr'; } +(?!\n|;|#)*[^\n]* { this.popState(); return "acc_descr_value"; } +accDescr\s*"{"\s* { this.begin("acc_descr_multiline");} [\}] { this.popState(); } [^\}]* return "acc_descr_multiline_value"; -// .*[^\n]* { return "acc_descr_line"} -["][`] { this.begin("md_string");} -[^`"]+ { return "MD_STR";} -[`]["] { this.popState();} -["] this.begin("string"); +// .*[^\n]* { return "acc_descr_line"} + +["][`] { this.begin("md_string");} +[^`"]+ { return "MD_STR";} +[`]["] { this.popState();} +["] this.pushState("string"); ["] this.popState(); -[^"]* return "STR"; -"style" return 'STYLE'; -"default" return 'DEFAULT'; -"linkStyle" return 'LINKSTYLE'; -"interpolate" return 'INTERPOLATE'; -"classDef" return 'CLASSDEF'; -"class" return 'CLASS'; +[^"]+ return "STR"; +"style" return 'STYLE'; +"default" return 'DEFAULT'; +"linkStyle" return 'LINKSTYLE'; +"interpolate" return 'INTERPOLATE'; +"classDef" return 'CLASSDEF'; +"class" return 'CLASS'; /* ---interactivity command--- @@ -80,64 +84,80 @@ Function arguments are optional: 'call ()' simply executes 'callba that id. 'click ' can be followed by href or call commands in any desired order */ -"click"[\s]+ this.begin("click"); -[\s\n] this.popState(); -[^\s\n]* return 'CLICK'; +"click"[\s]+ this.begin("click"); +[\s\n] this.popState(); +[^\s\n]* return 'CLICK'; -"flowchart-elk" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} -"graph" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} -"flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} -"subgraph" return 'subgraph'; -"end"\b\s* return 'end'; +"flowchart-elk" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} +"graph" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} +"flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';} +"subgraph" return 'subgraph'; +"end"\b\s* return 'end'; -"_self" return 'LINK_TARGET'; -"_blank" return 'LINK_TARGET'; -"_parent" return 'LINK_TARGET'; -"_top" return 'LINK_TARGET'; +"_self" return 'LINK_TARGET'; +"_blank" return 'LINK_TARGET'; +"_parent" return 'LINK_TARGET'; +"_top" return 'LINK_TARGET'; -(\r?\n)*\s*\n { this.popState(); return 'NODIR'; } -\s*"LR" { this.popState(); return 'DIR'; } -\s*"RL" { this.popState(); return 'DIR'; } -\s*"TB" { this.popState(); return 'DIR'; } -\s*"BT" { this.popState(); return 'DIR'; } -\s*"TD" { this.popState(); return 'DIR'; } -\s*"BR" { this.popState(); return 'DIR'; } -\s*"<" { this.popState(); return 'DIR'; } -\s*">" { this.popState(); return 'DIR'; } -\s*"^" { this.popState(); return 'DIR'; } -\s*"v" { this.popState(); return 'DIR'; } +(\r?\n)*\s*\n { this.popState(); return 'NODIR'; } +\s*"LR" { this.popState(); return 'DIR'; } +\s*"RL" { this.popState(); return 'DIR'; } +\s*"TB" { this.popState(); return 'DIR'; } +\s*"BT" { this.popState(); return 'DIR'; } +\s*"TD" { this.popState(); return 'DIR'; } +\s*"BR" { this.popState(); return 'DIR'; } +\s*"<" { this.popState(); return 'DIR'; } +\s*">" { this.popState(); return 'DIR'; } +\s*"^" { this.popState(); return 'DIR'; } +\s*"v" { this.popState(); return 'DIR'; } -.*direction\s+TB[^\n]* return 'direction_tb'; -.*direction\s+BT[^\n]* return 'direction_bt'; -.*direction\s+RL[^\n]* return 'direction_rl'; -.*direction\s+LR[^\n]* return 'direction_lr'; +.*direction\s+TB[^\n]* return 'direction_tb'; +.*direction\s+BT[^\n]* return 'direction_bt'; +.*direction\s+RL[^\n]* return 'direction_rl'; +.*direction\s+LR[^\n]* return 'direction_lr'; + +[0-9]+ return 'NUM'; +\# return 'BRKT'; +":::" return 'STYLE_SEPARATOR'; +":" return 'COLON'; +"&" return 'AMP'; +";" return 'SEMI'; +"," return 'COMMA'; +"*" return 'MULT'; +\s*[xo<]?\-\-+[-xo>]\s* return 'LINK'; +\s*[xo<]?\=\=+[=xo>]\s* return 'LINK'; +\s*[xo<]?\-?\.+\-[xo>]?\s* return 'LINK'; +\s*\~\~[\~]+\s* return 'LINK'; +\s*[xo<]?\-\-\s* return 'START_LINK'; +\s*[xo<]?\=\=\s* return 'START_LINK'; +\s*[xo<]?\-\.\s* return 'START_LINK'; + +<*>"(-" { this.pushState("ellipseText"); return '(-'; } +[-/\)][\)] { this.popState(); return '-)'; } +[^/)]|-/!\)+ return "TEXT" + +<*>"([" { this.pushState("text"); return 'STADIUMSTART'; } +"])" { this.popState(); return 'STADIUMEND'; } + +<*>"[[" { this.pushState("text"); return 'SUBROUTINESTART'; } +"]]" { this.popState(); return 'SUBROUTINEEND'; } + +"[|" { return 'VERTEX_WITH_PROPS_START'; } + +\>/!\s { this.pushState("text"); return 'TAGEND'; } +<*>"[(" { this.pushState("text") ;return 'CYLINDERSTART'; } +")]" { this.popState(); return 'CYLINDEREND'; } + +<*>"(((" { this.pushState("text"); return 'DOUBLECIRCLESTART'; } +")))" { this.popState(); return 'DOUBLECIRCLEEND'; } + +<*>"[/" { this.pushState("trapText"); return 'TRAPSTART'; } +[\\(?=\])][\]] { this.popState(); return 'TRAPEND'; } +[^\\\/]+ return 'TEXT'; + +\/(?=\])\] { this.popState(); return 'INVTRAPEND'; } +<*>"[\\" { this.pushState("trapText"); return 'INVTRAPSTART'; } -[0-9]+ { return 'NUM';} -\# return 'BRKT'; -":::" return 'STYLE_SEPARATOR'; -":" return 'COLON'; -"&" return 'AMP'; -";" return 'SEMI'; -"," return 'COMMA'; -"*" return 'MULT'; -\s*[xo<]?\-\-+[-xo>]\s* return 'LINK'; -\s*[xo<]?\=\=+[=xo>]\s* return 'LINK'; -\s*[xo<]?\-?\.+\-[xo>]?\s* return 'LINK'; -\s*\~\~[\~]+\s* return 'LINK'; -\s*[xo<]?\-\-\s* return 'START_LINK'; -\s*[xo<]?\=\=\s* return 'START_LINK'; -\s*[xo<]?\-\.\s* return 'START_LINK'; -"(-" return '(-'; -"-)" return '-)'; -"([" return 'STADIUMSTART'; -"])" return 'STADIUMEND'; -"[[" return 'SUBROUTINESTART'; -"]]" return 'SUBROUTINEEND'; -"[|" return 'VERTEX_WITH_PROPS_START'; -"[(" return 'CYLINDERSTART'; -")]" return 'CYLINDEREND'; -"(((" return 'DOUBLECIRCLESTART'; -")))" return 'DOUBLECIRCLEEND'; \- return 'MINUS'; "." return 'DOT'; [\_] return 'UNDERSCORE'; @@ -150,11 +170,8 @@ that id. "^" return 'UP'; "\|" return 'SEP'; "v" return 'DOWN'; +[A-Za-z0-9_]+ return 'ALPHA_NUM'; [A-Za-z]+ return 'ALPHA'; -"\\]" return 'TRAPEND'; -"[/" return 'TRAPSTART'; -"/]" return 'INVTRAPEND'; -"[\\" return 'INVTRAPSTART'; [!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION'; [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| [\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| @@ -218,13 +235,16 @@ that id. [\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]| [\uFFD2-\uFFD7\uFFDA-\uFFDC] return 'UNICODE_TEXT'; -"|" return 'PIPE'; -"(" return 'PS'; -")" return 'PE'; -"[" return 'SQS'; -"]" return 'SQE'; -"{" return 'DIAMOND_START' -"}" return 'DIAMOND_STOP' + +"|" { this.popState(); return 'PIPE'; } +<*>"|" { this.pushState("text"); return 'PIPE'; } +<*>"(" { this.pushState("text"); return 'PS'; } +")" { this.popState(); return 'PE'; } +<*>"[" { this.pushState("text"); return 'SQS'; } +(\]) { this.popState(); return 'SQE'; } +<*>"{" { this.pushState("text"); return 'DIAMOND_START' } +(\}) { this.popState(); return 'DIAMOND_STOP' } +[^\]\)\}\|]+ return "TEXT"; "\"" return 'QUOTE'; (\r?\n)+ return 'NEWLINE'; \s return 'SPACE'; @@ -343,11 +363,11 @@ statement {$$=[];} | clickStatement separator {$$=[];} - | subgraph SPACE text SQS text SQE separator document end + | subgraph SPACE textNoTags SQS text SQE separator document end {$$=yy.addSubGraph($3,$8,$5);} - | subgraph SPACE text separator document end + | subgraph SPACE textNoTags separator document end {$$=yy.addSubGraph($3,$5,$3);} - // | subgraph SPACE text separator document end + // | subgraph SPACE textNoTags separator document end // {$$=yy.addSubGraph($3,$5,$3);} | subgraph separator document end {$$=yy.addSubGraph(undefined,$3,undefined);} @@ -392,7 +412,7 @@ vertex: idString SQS text SQE {$$ = $1;yy.addVertex($1,$3,'stadium');} | idString SUBROUTINESTART text SUBROUTINEEND {$$ = $1;yy.addVertex($1,$3,'subroutine');} - | idString VERTEX_WITH_PROPS_START ALPHA COLON ALPHA PIPE text SQE + | idString VERTEX_WITH_PROPS_START ALPHA_NUM COLON ALPHA_NUM PIPE text SQE {$$ = $1;yy.addVertex($1,$7,'rect',undefined,undefined,undefined, Object.fromEntries([[$3, $5]]));} | idString CYLINDERSTART text CYLINDEREND {$$ = $1;yy.addVertex($1,$3,'cylinder');} @@ -426,7 +446,7 @@ link: linkStatement arrowText {$1.text = $2;$$ = $1;} | linkStatement {$$ = $1;} - | START_LINK text LINK + | START_LINK textNoTags LINK {var inf = yy.destructLink($3, $1); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$2};} ; @@ -443,10 +463,6 @@ text: textToken { $$={text:$1, type: 'text'};} | text textToken { $$={text:$1.text+''+$2, type: $1.type};} - | STR - { $$={text: $1, type: 'text'};} - | MD_STR - { $$={text: $1, type: 'markdown'};} ; @@ -456,9 +472,13 @@ keywords textNoTags: textNoTagsToken - {$$=$1;} + {$$={text:$1, type: 'text'};} | textNoTags textNoTagsToken - {$$=$1+''+$2;} + {$$={text:$1.text+''+$2, type: $1.type};} + | STR + { $$={text: $1, type: 'text'};} + | MD_STR + { $$={text: $1, type: 'markdown'};} ; @@ -527,13 +547,14 @@ style: styleComponent {$$ = $1 + $2;} ; -styleComponent: ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ; +styleComponent: ALPHA_NUM | ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ; /* Token lists */ +idStringToken : alphaNumToken | DOWN | MINUS | DEFAULT; -textToken : textNoTagsToken | TAGSTART | TAGEND | START_LINK | PCT | DEFAULT; +textToken : textNoTagsToken | STR | MD_STR | TEXT; -textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords ; +textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords | START_LINK ; idString :idStringToken @@ -571,9 +592,7 @@ direction { $$={stmt:'dir', value:'LR'};} ; -alphaNumToken : PUNCTUATION | AMP | UNICODE_TEXT | NUM| ALPHA | COLON | COMMA | PLUS | EQUALS | MULT | DOT | BRKT| UNDERSCORE ; - -idStringToken : ALPHA|UNDERSCORE |UNICODE_TEXT | NUM| COLON | COMMA | PLUS | MINUS | DOWN |EQUALS | MULT | BRKT | DOT | PUNCTUATION | AMP | DEFAULT; +alphaNumToken : ALPHA_NUM | PUNCTUATION | AMP | UNICODE_TEXT | NUM| ALPHA | COLON | COMMA | PLUS | EQUALS | MULT | DOT | BRKT| UNDERSCORE ; graphCodeTokens: STADIUMSTART | STADIUMEND | SUBROUTINESTART | SUBROUTINEEND | VERTEX_WITH_PROPS_START | CYLINDERSTART | CYLINDEREND | TRAPSTART | TRAPEND | INVTRAPSTART | INVTRAPEND | PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAGSTART | TAGEND | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI; %% From 3496f275bc9dfbc7dab617282f0c9d81e6a72737 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Wed, 19 Jul 2023 22:56:53 -0300 Subject: [PATCH 02/45] Re-implement markdown as its own text type --- packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index d0e65913a..48f5f410b 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -40,7 +40,7 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multilin [^\}]* return "acc_descr_multiline_value"; // .*[^\n]* { return "acc_descr_line"} -["][`] { this.begin("md_string");} +["][`] { this.begin("md_string");} [^`"]+ { return "MD_STR";} [`]["] { this.popState();} ["] this.pushState("string"); @@ -463,6 +463,8 @@ text: textToken { $$={text:$1, type: 'text'};} | text textToken { $$={text:$1.text+''+$2, type: $1.type};} + | MD_STR + { $$={text: $1, type: 'markdown'};} ; @@ -552,7 +554,7 @@ styleComponent: ALPHA_NUM | ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | B /* Token lists */ idStringToken : alphaNumToken | DOWN | MINUS | DEFAULT; -textToken : textNoTagsToken | STR | MD_STR | TEXT; +textToken : textNoTagsToken | STR | TEXT; textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords | START_LINK ; From 5b987dee93f0f23eec22a1608cc6f944c8eebaa0 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Thu, 20 Jul 2023 13:05:18 -0300 Subject: [PATCH 03/45] Put parser in stable state --- .../src/diagrams/flowchart/parser/flow.jison | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 48f5f410b..a5ca17586 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -158,8 +158,8 @@ that id. \/(?=\])\] { this.popState(); return 'INVTRAPEND'; } <*>"[\\" { this.pushState("trapText"); return 'INVTRAPSTART'; } -\- return 'MINUS'; -"." return 'DOT'; +\- return 'MINUS'; +"." return 'DOT'; [\_] return 'UNDERSCORE'; \+ return 'PLUS'; \% return 'PCT'; @@ -170,9 +170,9 @@ that id. "^" return 'UP'; "\|" return 'SEP'; "v" return 'DOWN'; +[0-9]+ return 'NUM'; [A-Za-z0-9_]+ return 'ALPHA_NUM'; -[A-Za-z]+ return 'ALPHA'; -[!"#$%&'*+,-.`?\\_/] return 'PUNCTUATION'; +[!"#$%&'*+,-\.`?\\_/] return 'PUNCTUATION'; [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| [\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| [\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]| @@ -552,9 +552,9 @@ style: styleComponent styleComponent: ALPHA_NUM | ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ; /* Token lists */ -idStringToken : alphaNumToken | DOWN | MINUS | DEFAULT; +idStringToken : alphaNumToken | DOWN | MINUS | DEFAULT; -textToken : textNoTagsToken | STR | TEXT; +textToken : STR | TEXT; textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords | START_LINK ; @@ -566,9 +566,9 @@ idString ; alphaNum - : alphaNumStatement + : alphaNumToken {$$=$1;} - | alphaNum alphaNumStatement + | alphaNum alphaNumToken {$$=$1+''+$2;} ; From 7adb1bccb384cee272d44f1f6deb9c2e5b6ddd37 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Thu, 20 Jul 2023 23:49:28 -0300 Subject: [PATCH 04/45] Replace alphanum with NODE_STRING for most usecases What this allows is for idStrings that are separated by dashes or underscores to be considered one whole string rather than a bunch of tokens mixed together. This is necessary for examples such as a-node-graph[text]. Now, the last part of the idString 'graph' will be read as part of the NODE_STRING token rather than attempting to add a GRAPH token to the idString. --- .../src/diagrams/flowchart/parser/flow.jison | 39 ++++++++----------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index a5ca17586..02229e222 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -136,10 +136,10 @@ that id. [-/\)][\)] { this.popState(); return '-)'; } [^/)]|-/!\)+ return "TEXT" -<*>"([" { this.pushState("text"); return 'STADIUMSTART'; } +"([" { this.pushState("text"); return 'STADIUMSTART'; } "])" { this.popState(); return 'STADIUMEND'; } -<*>"[[" { this.pushState("text"); return 'SUBROUTINESTART'; } +"[[" { this.pushState("text"); return 'SUBROUTINESTART'; } "]]" { this.popState(); return 'SUBROUTINEEND'; } "[|" { return 'VERTEX_WITH_PROPS_START'; } @@ -152,27 +152,19 @@ that id. ")))" { this.popState(); return 'DOUBLECIRCLEEND'; } <*>"[/" { this.pushState("trapText"); return 'TRAPSTART'; } -[\\(?=\])][\]] { this.popState(); return 'TRAPEND'; } -[^\\\/]+ return 'TEXT'; - +[\\(?=\])][\]] { this.popState(); return 'TRAPEND'; } \/(?=\])\] { this.popState(); return 'INVTRAPEND'; } +\/(?!\])|\\(?!\])|[^\\\]\/]+ return 'TEXT'; <*>"[\\" { this.pushState("trapText"); return 'INVTRAPSTART'; } -\- return 'MINUS'; -"." return 'DOT'; -[\_] return 'UNDERSCORE'; -\+ return 'PLUS'; -\% return 'PCT'; -"=" return 'EQUALS'; -\= return 'EQUALS'; + "<" return 'TAGSTART'; ">" return 'TAGEND'; "^" return 'UP'; "\|" return 'SEP'; "v" return 'DOWN'; -[0-9]+ return 'NUM'; -[A-Za-z0-9_]+ return 'ALPHA_NUM'; -[!"#$%&'*+,-\.`?\\_/] return 'PUNCTUATION'; +([A-Za-z0-9!"#$%&'*+\.`?\\_]|\-(?=[^\>\-\.]))+ return 'NODE_STRING'; +"-" return 'MINUS' [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| [\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| [\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]| @@ -245,6 +237,7 @@ that id. <*>"{" { this.pushState("text"); return 'DIAMOND_START' } (\}) { this.popState(); return 'DIAMOND_STOP' } [^\]\)\}\|]+ return "TEXT"; + "\"" return 'QUOTE'; (\r?\n)+ return 'NEWLINE'; \s return 'SPACE'; @@ -412,7 +405,7 @@ vertex: idString SQS text SQE {$$ = $1;yy.addVertex($1,$3,'stadium');} | idString SUBROUTINESTART text SUBROUTINEEND {$$ = $1;yy.addVertex($1,$3,'subroutine');} - | idString VERTEX_WITH_PROPS_START ALPHA_NUM COLON ALPHA_NUM PIPE text SQE + | idString VERTEX_WITH_PROPS_START NODE_STRING COLON NODE_STRING PIPE text SQE {$$ = $1;yy.addVertex($1,$7,'rect',undefined,undefined,undefined, Object.fromEntries([[$3, $5]]));} | idString CYLINDERSTART text CYLINDEREND {$$ = $1;yy.addVertex($1,$3,'cylinder');} @@ -549,14 +542,14 @@ style: styleComponent {$$ = $1 + $2;} ; -styleComponent: ALPHA_NUM | ALPHA | COLON | MINUS | NUM | UNIT | SPACE | HEX | BRKT | DOT | STYLE | PCT ; +styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | HEX | BRKT | STYLE | PCT ; /* Token lists */ -idStringToken : alphaNumToken | DOWN | MINUS | DEFAULT; +idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT; -textToken : STR | TEXT; +textToken : STR | TEXT | TAGSTART | TAGEND; -textNoTagsToken: alphaNumToken | SPACE | MINUS | keywords | START_LINK ; +textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | keywords | START_LINK ; idString :idStringToken @@ -566,15 +559,17 @@ idString ; alphaNum - : alphaNumToken + : alphaNumStatement {$$=$1;} - | alphaNum alphaNumToken + | alphaNum alphaNumStatement {$$=$1+''+$2;} ; alphaNumStatement : DIR {$$=$1;} + | NODE_STRING + {$$=$1;} | alphaNumToken {$$=$1;} | DOWN From 69c91ae5edef445d0cf69f97038d58d0d518d75c Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Fri, 21 Jul 2023 00:06:18 -0300 Subject: [PATCH 05/45] Add unit test for edge case with lean_right/left vertices --- .../flowchart/parser/flow-text.spec.js | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index 6ab5ab2cd..9516d088b 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -363,13 +363,13 @@ describe('[Text] when parsing', () => { it.each(keywords)('should handle %s keyword in doublecircle vertex', function (keyword) { const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B(((This node has a ${keyword} as text)));` + `graph TD;A_${keyword}-->${keyword}_B(((This node has a ${keyword} as text)));` ); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('doublecircle'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + expect(vert[`${keyword}_B`].type).toBe('doublecircle'); + expect(vert[`${keyword}_B`].text).toBe(`This node has a ${keyword} as text`); }); it.each(keywords)('should handle %s keyword in ellipse vertex', function (keyword) { @@ -482,6 +482,24 @@ describe('[Text] when parsing', () => { expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); }); + it('should allow forward slashes in lean_right vertices', function () { + const rest = flow.parser.parse(`graph TD;A_node-->B[/This node has a / as text/];`); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('lean_right'); + expect(vert['B'].text).toBe(`This node has a / as text`); + }); + + it('should allow back slashes in lean_left vertices', function () { + const rest = flow.parser.parse(`graph TD;A_node-->B[\\This node has a \\ as text\\];`); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe('lean_left'); + expect(vert['B'].text).toBe(`This node has a \\ as text`); + }); + it.each(keywords)('should handle %s keyword in lean_left vertex', function (keyword) { const rest = flow.parser.parse( `graph TD;A_${keyword}_node-->B[\\This node has a ${keyword} as text\\];` From 0d7cc748b8d977974cba2b7d4c66ea2bf1ef2609 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Fri, 21 Jul 2023 17:33:33 -0300 Subject: [PATCH 06/45] Replace alphanum with idString where appropriate --- .../src/diagrams/flowchart/parser/flow.jison | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 02229e222..21c175062 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -118,6 +118,7 @@ that id. [0-9]+ return 'NUM'; \# return 'BRKT'; +\#[0-9]+ return 'HEX'; ":::" return 'STYLE_SEPARATOR'; ":" return 'COLON'; "&" return 'AMP'; @@ -163,7 +164,7 @@ that id. "^" return 'UP'; "\|" return 'SEP'; "v" return 'DOWN'; -([A-Za-z0-9!"#$%&'*+\.`?\\_]|\-(?=[^\>\-\.]))+ return 'NODE_STRING'; +([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.]))+ return 'NODE_STRING'; "-" return 'MINUS' [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| [\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| @@ -477,13 +478,11 @@ textNoTags: textNoTagsToken ; -classDefStatement:CLASSDEF SPACE DEFAULT SPACE stylesOpt +classDefStatement:CLASSDEF SPACE idString SPACE stylesOpt {$$ = $1;yy.addClass($3,$5);} - | CLASSDEF SPACE alphaNum SPACE stylesOpt - {$$ = $1;yy.addClass($3,$5);} ; -classStatement:CLASS SPACE alphaNum SPACE alphaNum +classStatement:CLASS SPACE idString SPACE alphaNum {$$ = $1;yy.setClass($3, $5);} ; @@ -504,7 +503,7 @@ clickStatement | CLICK STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);} ; -styleStatement:STYLE SPACE alphaNum SPACE stylesOpt +styleStatement:STYLE SPACE idString SPACE stylesOpt {$$ = $1;yy.addVertex($3,undefined,undefined,$5);} | STYLE SPACE HEX SPACE stylesOpt {$$ = $1;yy.updateLink($3,$5);} @@ -545,9 +544,9 @@ style: styleComponent styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | HEX | BRKT | STYLE | PCT ; /* Token lists */ -idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT; +idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON; -textToken : STR | TEXT | TAGSTART | TAGEND; +textToken : STR | TEXT | TAGSTART | TAGEND | UNICODE_TEXT; textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | keywords | START_LINK ; @@ -570,8 +569,6 @@ alphaNumStatement {$$=$1;} | NODE_STRING {$$=$1;} - | alphaNumToken - {$$=$1;} | DOWN {$$='v';} | MINUS From 0a4e5f5f6b48f415c9943d1f7b5254fc6355dfb5 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Fri, 21 Jul 2023 18:15:27 -0300 Subject: [PATCH 07/45] Slightly alter unit test node idString --- .../src/diagrams/flowchart/parser/flow-text.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index 9516d088b..a64f39ef0 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -363,18 +363,18 @@ describe('[Text] when parsing', () => { it.each(keywords)('should handle %s keyword in doublecircle vertex', function (keyword) { const rest = flow.parser.parse( - `graph TD;A_${keyword}-->${keyword}_B(((This node has a ${keyword} as text)));` + `graph TD;A_${keyword}-->-${keyword}-B(((This node has a ${keyword} as text)));` ); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); - expect(vert[`${keyword}_B`].type).toBe('doublecircle'); - expect(vert[`${keyword}_B`].text).toBe(`This node has a ${keyword} as text`); + expect(vert[`-${keyword}-B`].type).toBe('doublecircle'); + expect(vert[`-${keyword}-B`].text).toBe(`This node has a ${keyword} as text`); }); it.each(keywords)('should handle %s keyword in ellipse vertex', function (keyword) { const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B(-This node has a ${keyword} as text-);` + `graph TD;A_${keyword}-->B(-This node has a ${keyword} as text-);` ); const vert = flow.parser.yy.getVertices(); From 3fa3ed7b186e4fcaab75af0967f6072046561398 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Fri, 21 Jul 2023 18:15:43 -0300 Subject: [PATCH 08/45] Remove grammar and unit test that expects HEX token This was never really used and had many things wrong with it. I believe that if a hex was provided in the diagram specification, the alphanum grammar would break it up into a BRKT and NUM token and use the first line with the addVertex() function. Second, the styleLink grammar provides the exact same functionality with the linkStyle keyword. Third, updateLink() expects an array of nums, not a hex digit. Fourth, no documentation is provided on this grammar statement existing. Fifth, the unit test does not work in version 10.2.4 --- .../src/diagrams/flowchart/parser/flow-style.spec.js | 9 --------- .../mermaid/src/diagrams/flowchart/parser/flow.jison | 6 ++---- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-style.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-style.spec.js index 3feaa2469..1ab754308 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-style.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-style.spec.js @@ -26,15 +26,6 @@ describe('[Style] when parsing', () => { expect(vert['Q'].styles[0]).toBe('background:#fff'); }); - // log.debug(flow.parser.parse('graph TD;style Q background:#fff;')); - it('should handle styles for edges', function () { - const res = flow.parser.parse('graph TD;a-->b;\nstyle #0 stroke: #f66;'); - - const edges = flow.parser.yy.getEdges(); - - expect(edges.length).toBe(1); - }); - it('should handle multiple styles for a vortex', function () { const res = flow.parser.parse('graph TD;style R background:#fff,border:1px solid red;'); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 21c175062..b54eec151 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -118,7 +118,6 @@ that id. [0-9]+ return 'NUM'; \# return 'BRKT'; -\#[0-9]+ return 'HEX'; ":::" return 'STYLE_SEPARATOR'; ":" return 'COLON'; "&" return 'AMP'; @@ -505,8 +504,7 @@ clickStatement styleStatement:STYLE SPACE idString SPACE stylesOpt {$$ = $1;yy.addVertex($3,undefined,undefined,$5);} - | STYLE SPACE HEX SPACE stylesOpt - {$$ = $1;yy.updateLink($3,$5);} + ; linkStyleStatement @@ -541,7 +539,7 @@ style: styleComponent {$$ = $1 + $2;} ; -styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | HEX | BRKT | STYLE | PCT ; +styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | BRKT | STYLE | PCT ; /* Token lists */ idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON; From 8ff06e88ce2e60ee8212de0c7dafbd2ae9fecc4e Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Fri, 21 Jul 2023 18:27:20 -0300 Subject: [PATCH 09/45] Correct expected error message in unit test --- packages/mermaid/src/diagram.spec.ts | 2 +- packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagram.spec.ts b/packages/mermaid/src/diagram.spec.ts index aa613d8e5..6bbc057e3 100644 --- a/packages/mermaid/src/diagram.spec.ts +++ b/packages/mermaid/src/diagram.spec.ts @@ -49,7 +49,7 @@ describe('diagram detection', () => { "Parse error on line 2: graph TD; A--> --------------^ - Expecting 'AMP', 'ALPHA', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'MINUS', 'BRKT', 'DOT', 'PUNCTUATION', 'UNICODE_TEXT', 'PLUS', 'EQUALS', 'MULT', 'UNDERSCORE', got 'EOF'" + Expecting 'NODE_STRING', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'MINUS', got 'EOF'" `); await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects .toThrowErrorMatchingInlineSnapshot(` diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index b54eec151..4afeee124 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -504,7 +504,6 @@ clickStatement styleStatement:STYLE SPACE idString SPACE stylesOpt {$$ = $1;yy.addVertex($3,undefined,undefined,$5);} - ; linkStyleStatement From 0cc8f891156817d949e1e92880b186ef1e43c597 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Fri, 21 Jul 2023 19:40:04 -0300 Subject: [PATCH 10/45] Add edgeText states --- .../src/diagrams/flowchart/parser/flow.jison | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 4afeee124..4ce12393b 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -16,6 +16,9 @@ %x text %x ellipseText %x trapText +%x edgeText +%x thickEdgeText +%x dottedEdgeText %x click %x href %x callbackname @@ -40,10 +43,10 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multilin [^\}]* return "acc_descr_multiline_value"; // .*[^\n]* { return "acc_descr_line"} -["][`] { this.begin("md_string");} +["][`] { this.begin("md_string");} [^`"]+ { return "MD_STR";} [`]["] { this.popState();} -["] this.pushState("string"); +["] this.pushState("string"); ["] this.popState(); [^"]+ return "STR"; "style" return 'STYLE'; @@ -124,13 +127,21 @@ that id. ";" return 'SEMI'; "," return 'COMMA'; "*" return 'MULT'; -\s*[xo<]?\-\-+[-xo>]\s* return 'LINK'; -\s*[xo<]?\=\=+[=xo>]\s* return 'LINK'; -\s*[xo<]?\-?\.+\-[xo>]?\s* return 'LINK'; -\s*\~\~[\~]+\s* return 'LINK'; -\s*[xo<]?\-\-\s* return 'START_LINK'; -\s*[xo<]?\=\=\s* return 'START_LINK'; -\s*[xo<]?\-\.\s* return 'START_LINK'; + +\s*[xo<]?\-\-+[-xo>]\s* { this.popState(); return 'LINK'; } +\s*[xo<]?\-\-\s* { this.pushState("edgeText"); return 'START_LINK'; } +[^-]|\-(?!\-)+ return 'EDGE_TEXT'; + +\s*[xo<]?\=\=+[=xo>]\s* { this.popState(); return 'LINK'; } +\s*[xo<]?\=\=\s* { this.pushState("thickEdgeText"); return 'START_LINK'; } +[^=]|\=(?!=) return 'EDGE_TEXT'; + +\s*[xo<]?\-?\.+\-[xo>]?\s* { this.popState(); return 'LINK'; } +\s*[xo<]?\-\.\s* { this.pushState("dottedEdgeText"); return 'START_LINK'; } +[^\.]|\.(?!-) return 'EDGE_TEXT'; + + +<*>\s*\~\~[\~]+\s* return 'LINK'; <*>"(-" { this.pushState("ellipseText"); return '(-'; } [-/\)][\)] { this.popState(); return '-)'; } @@ -439,10 +450,19 @@ link: linkStatement arrowText {$1.text = $2;$$ = $1;} | linkStatement {$$ = $1;} - | START_LINK textNoTags LINK + | START_LINK edgeText LINK {var inf = yy.destructLink($3, $1); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$2};} ; +edgeText: edgeTextToken + {$$={text:$1, type:'text'};} + | edgeText edgeTextToken + {$$={text:$1.text+''+$2, type:$1.type};} + | MD_STR + {$$={text:$1, type:'markdown'};} + ; + + linkStatement: LINK {var inf = yy.destructLink($1);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};} ; @@ -547,6 +567,8 @@ textToken : STR | TEXT | TAGSTART | TAGEND | UNICODE_TEXT; textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | keywords | START_LINK ; +edgeTextToken : STR | EDGE_TEXT | UNICODE_TEXT ; + idString :idStringToken {$$=$1} From 20011c6882853551c377d64fed11cc36bca62aad Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Fri, 21 Jul 2023 20:44:37 -0300 Subject: [PATCH 11/45] Add unit tests for edge text --- .../flowchart/parser/flow-edges.spec.js | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-edges.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-edges.spec.js index dcac21ee7..80dce1bd6 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-edges.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-edges.spec.js @@ -6,6 +6,28 @@ setConfig({ securityLevel: 'strict', }); +const keywords = [ + 'graph', + 'flowchart', + 'flowchart-elk', + 'style', + 'default', + 'linkStyle', + 'interpolate', + 'classDef', + 'class', + 'href', + 'call', + 'click', + '_self', + '_blank', + '_parent', + '_top', + 'end', + 'subgraph', + 'kitty', +]; + describe('[Edges] when parsing', () => { beforeEach(function () { flow.parser.yy = flowDb; @@ -74,6 +96,23 @@ describe('[Edges] when parsing', () => { expect(edges[0].length).toBe(1); }); + it.each(keywords)('should handle %s as text in double edged nodes', function (keyword) { + const res = flow.parser.parse(`graph TD;\nA x-- ${keyword} --x B;`); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].id).toBe('A'); + expect(vert['B'].id).toBe('B'); + expect(edges.length).toBe(1); + expect(edges[0].start).toBe('A'); + expect(edges[0].end).toBe('B'); + expect(edges[0].type).toBe('double_arrow_cross'); + expect(edges[0].text).toBe(`${keyword}`); + expect(edges[0].stroke).toBe('normal'); + expect(edges[0].length).toBe(1); + }); + it('should handle double edged nodes and edges on thick arrows', function () { const res = flow.parser.parse('graph TD;\nA x==x B;'); @@ -108,6 +147,23 @@ describe('[Edges] when parsing', () => { expect(edges[0].length).toBe(1); }); + it.each(keywords)('should handle %s as text in thick double edged nodes', function (keyword) { + const res = flow.parser.parse(`graph TD;\nA x== ${keyword} ==x B;`); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].id).toBe('A'); + expect(vert['B'].id).toBe('B'); + expect(edges.length).toBe(1); + expect(edges[0].start).toBe('A'); + expect(edges[0].end).toBe('B'); + expect(edges[0].type).toBe('double_arrow_cross'); + expect(edges[0].text).toBe(`${keyword}`); + expect(edges[0].stroke).toBe('thick'); + expect(edges[0].length).toBe(1); + }); + it('should handle double edged nodes and edges on dotted arrows', function () { const res = flow.parser.parse('graph TD;\nA x-.-x B;'); @@ -143,6 +199,23 @@ describe('[Edges] when parsing', () => { }); }); + it.each(keywords)('should handle %s as text in dotted double edged nodes', function (keyword) { + const res = flow.parser.parse(`graph TD;\nA x-. ${keyword} .-x B;`); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].id).toBe('A'); + expect(vert['B'].id).toBe('B'); + expect(edges.length).toBe(1); + expect(edges[0].start).toBe('A'); + expect(edges[0].end).toBe('B'); + expect(edges[0].type).toBe('double_arrow_cross'); + expect(edges[0].text).toBe(`${keyword}`); + expect(edges[0].stroke).toBe('dotted'); + expect(edges[0].length).toBe(1); + }); + describe('circle', function () { it('should handle double edged nodes and edges', function () { const res = flow.parser.parse('graph TD;\nA o--o B;'); @@ -178,6 +251,23 @@ describe('[Edges] when parsing', () => { expect(edges[0].length).toBe(1); }); + it.each(keywords)('should handle double edged nodes with %s as text', function (keyword) { + const res = flow.parser.parse(`graph TD;\nA o-- ${keyword} --o B;`); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].id).toBe('A'); + expect(vert['B'].id).toBe('B'); + expect(edges.length).toBe(1); + expect(edges[0].start).toBe('A'); + expect(edges[0].end).toBe('B'); + expect(edges[0].type).toBe('double_arrow_circle'); + expect(edges[0].text).toBe(`${keyword}`); + expect(edges[0].stroke).toBe('normal'); + expect(edges[0].length).toBe(1); + }); + it('should handle double edged nodes and edges on thick arrows', function () { const res = flow.parser.parse('graph TD;\nA o==o B;'); @@ -212,6 +302,23 @@ describe('[Edges] when parsing', () => { expect(edges[0].length).toBe(1); }); + it.each(keywords)('should handle thick double edged nodes with %s as text', function (keyword) { + const res = flow.parser.parse(`graph TD;\nA o== ${keyword} ==o B;`); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].id).toBe('A'); + expect(vert['B'].id).toBe('B'); + expect(edges.length).toBe(1); + expect(edges[0].start).toBe('A'); + expect(edges[0].end).toBe('B'); + expect(edges[0].type).toBe('double_arrow_circle'); + expect(edges[0].text).toBe(`${keyword}`); + expect(edges[0].stroke).toBe('thick'); + expect(edges[0].length).toBe(1); + }); + it('should handle double edged nodes and edges on dotted arrows', function () { const res = flow.parser.parse('graph TD;\nA o-.-o B;'); @@ -245,6 +352,26 @@ describe('[Edges] when parsing', () => { expect(edges[0].stroke).toBe('dotted'); expect(edges[0].length).toBe(1); }); + + it.each(keywords)( + 'should handle dotted double edged nodes with %s as text', + function (keyword) { + const res = flow.parser.parse(`graph TD;\nA o-. ${keyword} .-o B;`); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].id).toBe('A'); + expect(vert['B'].id).toBe('B'); + expect(edges.length).toBe(1); + expect(edges[0].start).toBe('A'); + expect(edges[0].end).toBe('B'); + expect(edges[0].type).toBe('double_arrow_circle'); + expect(edges[0].text).toBe(`${keyword}`); + expect(edges[0].stroke).toBe('dotted'); + expect(edges[0].length).toBe(1); + } + ); }); it('should handle multiple edges', function () { From eea0ea5e07f622abed42252d1d5dc637288c8235 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Fri, 21 Jul 2023 21:08:33 -0300 Subject: [PATCH 12/45] Add unit tests for node id strings with keywords --- .../flowchart/parser/flow-singlenode.spec.js | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js index b959f019e..6b6e93857 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js @@ -6,6 +6,27 @@ setConfig({ securityLevel: 'strict', }); +const keywords = [ + 'graph', + 'flowchart', + 'flowchart-elk', + 'style', + 'default', + 'linkStyle', + 'interpolate', + 'classDef', + 'class', + 'href', + 'call', + 'click', + '_self', + '_blank', + '_parent', + '_top', + 'end', + 'subgraph', +]; + describe('[Singlenodes] when parsing', () => { beforeEach(function () { flow.parser.yy = flowDb; @@ -259,4 +280,30 @@ describe('[Singlenodes] when parsing', () => { expect(edges.length).toBe(0); expect(vert['i_d'].styles.length).toBe(0); }); + + it.each(keywords)('should handle keywords between dashes "-"', function (keyword) { + const res = flow.parser.parse(`graph TD;a-${keyword}-node;`); + const vert = flow.parser.yy.getVertices(); + expect(vert[`a-${keyword}-node`].text).toBe(`a-${keyword}-node`); + }); + + it.each(keywords)('should handle keywords between periods "."', function (keyword) { + const res = flow.parser.parse(`graph TD;a.${keyword}.node;`); + const vert = flow.parser.yy.getVertices(); + expect(vert[`a.${keyword}.node`].text).toBe(`a.${keyword}.node`); + }); + + it.each(keywords)('should handle keywords between underscores "_"', function (keyword) { + const res = flow.parser.parse(`graph TD;a_${keyword}_node;`); + const vert = flow.parser.yy.getVertices(); + expect(vert[`a_${keyword}_node`].text).toBe(`a_${keyword}_node`); + }); + + it.each(keywords)('should handle nodes ending in keywords', function (keyword) { + const res = flow.parser.parse(`graph TD;node_${keyword};node.${keyword};node-${keyword};`); + const vert = flow.parser.yy.getVertices(); + expect(vert[`node_${keyword}`].text).toBe(`node_${keyword}`); + expect(vert[`node.${keyword}`].text).toBe(`node.${keyword}`); + expect(vert[`node-${keyword}`].text).toBe(`node-${keyword}`); + }); }); From 3ab0e9998d4a4e6cce3139bc1a5681092391d2ae Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sat, 22 Jul 2023 14:39:45 -0300 Subject: [PATCH 13/45] Remove href state and give call higher precedence Similar to what was done in the class diagram parser, this will allow string tokens to appear in any state. This is especially helpful, because it will simplify the code and any future refactoring --- .../src/diagrams/flowchart/parser/flow.jison | 75 +++++++++---------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 4ce12393b..0b34839a1 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -43,29 +43,6 @@ accDescr\s*"{"\s* { this.begin("acc_descr_multilin [^\}]* return "acc_descr_multiline_value"; // .*[^\n]* { return "acc_descr_line"} -["][`] { this.begin("md_string");} -[^`"]+ { return "MD_STR";} -[`]["] { this.popState();} -["] this.pushState("string"); -["] this.popState(); -[^"]+ return "STR"; -"style" return 'STYLE'; -"default" return 'DEFAULT'; -"linkStyle" return 'LINKSTYLE'; -"interpolate" return 'INTERPOLATE'; -"classDef" return 'CLASSDEF'; -"class" return 'CLASS'; - -/* ----interactivity command--- -'href' adds a link to the specified node. 'href' can only be specified when the -line was introduced with 'click'. -'href ""' attaches the specified link to the node that was specified by 'click'. -*/ -"href"[\s]+["] this.begin("href"); -["] this.popState(); -[^"]* return 'HREF'; - /* ---interactivity command--- 'call' adds a callback to the specified node. 'call' can only be specified when @@ -81,6 +58,28 @@ Function arguments are optional: 'call ()' simply executes 'callba \) this.popState(); [^)]* return 'CALLBACKARGS'; +[^`"]+ { return "MD_STR";} +[`]["] { this.popState();} +<*>["][`] { this.begin("md_string");} +["] this.popState(); +[^"]+ return "STR"; +<*>["] this.pushState("string"); +"style" return 'STYLE'; +"default" return 'DEFAULT'; +"linkStyle" return 'LINKSTYLE'; +"interpolate" return 'INTERPOLATE'; +"classDef" return 'CLASSDEF'; +"class" return 'CLASS'; + +/* +---interactivity command--- +'href' adds a link to the specified node. 'href' can only be specified when the +line was introduced with 'click'. +'href ""' attaches the specified link to the node that was specified by 'click'. +*/ +"href" return 'HREF'; + + /* 'click' is the keyword to introduce a line that contains interactivity commands. 'click' must be followed by an existing node-id. All commands are attached to @@ -383,7 +382,7 @@ statement separator: NEWLINE | SEMI | EOF ; - + verticeStatement: verticeStatement link node { /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } } | verticeStatement link node spaceList @@ -506,20 +505,20 @@ classStatement:CLASS SPACE idString SPACE alphaNum ; clickStatement - : CLICK CALLBACKNAME {$$ = $1;yy.setClickEvent($1, $2);} - | CLICK CALLBACKNAME SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} - | CLICK CALLBACKNAME CALLBACKARGS {$$ = $1;yy.setClickEvent($1, $2, $3);} - | CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setTooltip($1, $5);} - | CLICK HREF {$$ = $1;yy.setLink($1, $2);} - | CLICK HREF SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);} - | CLICK HREF SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);} - | CLICK HREF SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);} - | CLICK alphaNum {$$ = $1;yy.setClickEvent($1, $2);} - | CLICK alphaNum SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} - | CLICK STR {$$ = $1;yy.setLink($1, $2);} - | CLICK STR SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);} - | CLICK STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);} - | CLICK STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);} + : CLICK CALLBACKNAME {$$ = $1;yy.setClickEvent($1, $2);} + | CLICK CALLBACKNAME SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} + | CLICK CALLBACKNAME CALLBACKARGS {$$ = $1;yy.setClickEvent($1, $2, $3);} + | CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setTooltip($1, $5);} + | CLICK HREF SPACE STR {$$ = $1;yy.setLink($1, $4);} + | CLICK HREF SPACE STR SPACE STR {$$ = $1;yy.setLink($1, $4);yy.setTooltip($1, $6);} + | CLICK HREF SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $4, $6);} + | CLICK HREF SPACE STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $4, $8);yy.setTooltip($1, $6);} + | CLICK alphaNum {$$ = $1;yy.setClickEvent($1, $2);} + | CLICK alphaNum SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} + | CLICK STR {$$ = $1;yy.setLink($1, $2);} + | CLICK STR SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);} + | CLICK STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);} + | CLICK STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);} ; styleStatement:STYLE SPACE idString SPACE stylesOpt From fd88b424b4d3094ed08d00d24e4e8d98967e141a Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sat, 22 Jul 2023 15:09:24 -0300 Subject: [PATCH 14/45] Add unit test for vertex and edge having both strings and text --- .../src/diagrams/flowchart/parser/flow-text.spec.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index a64f39ef0..922508e80 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -690,4 +690,16 @@ describe('[Text] when parsing', () => { expect(vert['A'].text).toBe(',.?!+-*'); expect(edges[0].text).toBe(',.?!+-*'); }); + + it('should handle strings and text at the same time', function () { + const res = flow.parser.parse( + 'graph TD;A(this node has "string" and text)-->|this link has "string" and text|C;' + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].text).toBe('this node has "string" and text'); + expect(edges[0].text).toBe('this link has "string" and text'); + }); }); From 4b9773a272b5c1eb1d5f2381640393a6698aa1da Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sun, 23 Jul 2023 21:25:19 -0300 Subject: [PATCH 15/45] Refactor token precedence that concern vertex text states --- .../src/diagrams/flowchart/parser/flow.jison | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 0b34839a1..80214cbff 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -142,29 +142,31 @@ that id. <*>\s*\~\~[\~]+\s* return 'LINK'; -<*>"(-" { this.pushState("ellipseText"); return '(-'; } [-/\)][\)] { this.popState(); return '-)'; } [^/)]|-/!\)+ return "TEXT" +<*>"(-" { this.pushState("ellipseText"); return '(-'; } -"([" { this.pushState("text"); return 'STADIUMSTART'; } "])" { this.popState(); return 'STADIUMEND'; } +<*>"([" { this.pushState("text"); return 'STADIUMSTART'; } -"[[" { this.pushState("text"); return 'SUBROUTINESTART'; } "]]" { this.popState(); return 'SUBROUTINEEND'; } +<*>"[[" { this.pushState("text"); return 'SUBROUTINESTART'; } "[|" { return 'VERTEX_WITH_PROPS_START'; } \>/!\s { this.pushState("text"); return 'TAGEND'; } -<*>"[(" { this.pushState("text") ;return 'CYLINDERSTART'; } + ")]" { this.popState(); return 'CYLINDEREND'; } +<*>"[(" { this.pushState("text") ;return 'CYLINDERSTART'; } -<*>"(((" { this.pushState("text"); return 'DOUBLECIRCLESTART'; } ")))" { this.popState(); return 'DOUBLECIRCLEEND'; } +<*>"(((" { this.pushState("text"); return 'DOUBLECIRCLESTART'; } -<*>"[/" { this.pushState("trapText"); return 'TRAPSTART'; } [\\(?=\])][\]] { this.popState(); return 'TRAPEND'; } \/(?=\])\] { this.popState(); return 'INVTRAPEND'; } \/(?!\])|\\(?!\])|[^\\\]\/]+ return 'TEXT'; +<*>"[/" { this.pushState("trapText"); return 'TRAPSTART'; } + <*>"[\\" { this.pushState("trapText"); return 'INVTRAPSTART'; } @@ -240,13 +242,16 @@ that id. "|" { this.popState(); return 'PIPE'; } <*>"|" { this.pushState("text"); return 'PIPE'; } -<*>"(" { this.pushState("text"); return 'PS'; } + ")" { this.popState(); return 'PE'; } +<*>"(" { this.pushState("text"); return 'PS'; } + +"]" { this.popState(); return 'SQE'; } <*>"[" { this.pushState("text"); return 'SQS'; } -(\]) { this.popState(); return 'SQE'; } -<*>"{" { this.pushState("text"); return 'DIAMOND_START' } + (\}) { this.popState(); return 'DIAMOND_STOP' } -[^\]\)\}\|]+ return "TEXT"; +<*>"{" { this.pushState("text"); return 'DIAMOND_START' } +[^\]\)\}\|\"]+ return "TEXT"; "\"" return 'QUOTE'; (\r?\n)+ return 'NEWLINE'; From fa8e02721a02ebbf61544ce21fe7521b656def0f Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sun, 23 Jul 2023 21:32:52 -0300 Subject: [PATCH 16/45] Refactor directive grammar to use name instead of position in production --- .../src/diagrams/flowchart/parser/flow.jison | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 80214cbff..d077c734f 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -283,11 +283,11 @@ openDirective ; typeDirective - : type_directive { yy.parseDirective($1, 'type_directive'); } + : type_directive { yy.parseDirective($type_directive, 'type_directive'); } ; argDirective - : arg_directive { $1 = $1.trim().replace(/'/g, '"'); yy.parseDirective($1, 'arg_directive'); } + : arg_directive { $arg_directive = $arg_directive.trim().replace(/'/g, '"'); yy.parseDirective($arg_directive, 'arg_directive'); } ; closeDirective @@ -303,15 +303,15 @@ document { $$ = [];} | document line { - if(!Array.isArray($2) || $2.length > 0){ - $1.push($2); + if(!Array.isArray($line) || $line.length > 0){ + $document.push($line); } - $$=$1;} + $$=$document;} ; line : statement - {$$=$1;} + {$$=$statement;} | SEMI | NEWLINE | SPACE @@ -324,15 +324,15 @@ graphConfig | GRAPH NODIR { yy.setDirection('TB');$$ = 'TB';} | GRAPH DIR FirstStmtSeperator - { yy.setDirection($2);$$ = $2;} + { yy.setDirection($DIR);$$ = $DIR;} // | GRAPH SPACE TAGEND FirstStmtSeperator - // { yy.setDirection("LR");$$ = $3;} + // { yy.setDirection("LR");$$ = $TAGEND;} // | GRAPH SPACE TAGSTART FirstStmtSeperator - // { yy.setDirection("RL");$$ = $3;} + // { yy.setDirection("RL");$$ = $TAGSTART;} // | GRAPH SPACE UP FirstStmtSeperator - // { yy.setDirection("BT");$$ = $3;} + // { yy.setDirection("BT");$$ = $UP;} // | GRAPH SPACE DOWN FirstStmtSeperator - // { yy.setDirection("TB");$$ = $3;} + // { yy.setDirection("TB");$$ = $DOWN;} ; ending: endToken ending From 087738df783f8744544678dcd8c69b7a2aca1b67 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sun, 23 Jul 2023 21:39:21 -0300 Subject: [PATCH 17/45] Refactor statement grammars to use name values instead of positions in production --- .../src/diagrams/flowchart/parser/flow.jison | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index d077c734f..e4466bc5d 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -360,7 +360,7 @@ spaceList statement : verticeStatement separator - { /* console.warn('finat vs', $1.nodes); */ $$=$1.nodes} + { /* console.warn('finat vs', $verticeStatement.nodes); */ $$=$verticeStatement.nodes} | styleStatement separator {$$=[];} | linkStyleStatement separator @@ -372,28 +372,28 @@ statement | clickStatement separator {$$=[];} | subgraph SPACE textNoTags SQS text SQE separator document end - {$$=yy.addSubGraph($3,$8,$5);} + {$$=yy.addSubGraph($textNoTags,$document,$text);} | subgraph SPACE textNoTags separator document end - {$$=yy.addSubGraph($3,$5,$3);} + {$$=yy.addSubGraph($textNoTags,$document,$textNoTags);} // | subgraph SPACE textNoTags separator document end - // {$$=yy.addSubGraph($3,$5,$3);} + // {$$=yy.addSubGraph($textNoTags,$document,$textNoTags);} | subgraph separator document end - {$$=yy.addSubGraph(undefined,$3,undefined);} + {$$=yy.addSubGraph(undefined,$document,undefined);} | direction - | acc_title acc_title_value { $$=$2.trim();yy.setAccTitle($$); } - | acc_descr acc_descr_value { $$=$2.trim();yy.setAccDescription($$); } - | acc_descr_multiline_value { $$=$1.trim();yy.setAccDescription($$); } + | acc_title acc_title_value { $$=$acc_title_value.trim();yy.setAccTitle($$); } + | acc_descr acc_descr_value { $$=$acc_descr_value.trim();yy.setAccDescription($$); } + | acc_descr_multiline_value { $$=$acc_descr_multiline_value.trim();yy.setAccDescription($$); } ; separator: NEWLINE | SEMI | EOF ; verticeStatement: verticeStatement link node - { /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } } + { /* console.warn('vs',$verticeStatement.stmt,$node); */ yy.addLink($verticeStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($verticeStatement.nodes) } } | verticeStatement link node spaceList - { /* console.warn('vs',$1.stmt,$3); */ yy.addLink($1.stmt,$3,$2); $$ = { stmt: $3, nodes: $3.concat($1.nodes) } } - |node spaceList {/*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }} - |node { /*console.warn('noda', $1);*/ $$ = {stmt: $1, nodes:$1 }} + { /* console.warn('vs',$verticeStatement.stmt,$node); */ yy.addLink($verticeStatement.stmt,$node,$link); $$ = { stmt: $node, nodes: $node.concat($verticeStatement.nodes) } } + |node spaceList {/*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }} + |node { /*console.warn('noda', $node);*/ $$ = {stmt: $node, nodes:$node }} ; node: styledVertex From 474e0b9c8292c6322847192ce16148999ae1e7d6 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sun, 23 Jul 2023 21:54:42 -0300 Subject: [PATCH 18/45] Refactor vertex grammars to use name values instead of position in production --- packages/mermaid/src/diagram.spec.ts | 2 +- .../src/diagrams/flowchart/parser/flow.jison | 44 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/mermaid/src/diagram.spec.ts b/packages/mermaid/src/diagram.spec.ts index 6bbc057e3..75a9e2d2c 100644 --- a/packages/mermaid/src/diagram.spec.ts +++ b/packages/mermaid/src/diagram.spec.ts @@ -49,7 +49,7 @@ describe('diagram detection', () => { "Parse error on line 2: graph TD; A--> --------------^ - Expecting 'NODE_STRING', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'MINUS', got 'EOF'" + Expecting 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'MINUS', got 'EOF'" `); await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects .toThrowErrorMatchingInlineSnapshot(` diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index e4466bc5d..90c935542 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -397,51 +397,51 @@ verticeStatement: verticeStatement link node ; node: styledVertex - { /* console.warn('nod', $1); */ $$ = [$1];} + { /* console.warn('nod', $styledVertex); */ $$ = [$styledVertex];} | node spaceList AMP spaceList styledVertex - { $$ = $1.concat($5); /* console.warn('pip', $1[0], $5, $$); */ } + { $$ = $node.concat($styledVertex); /* console.warn('pip', $node[0], $styledVertex, $$); */ } ; styledVertex: vertex - { /* console.warn('nod', $1); */ $$ = $1;} + { /* console.warn('nod', $vertex); */ $$ = $vertex;} | vertex STYLE_SEPARATOR idString - {$$ = $1;yy.setClass($1,$3)} + {$$ = $vertex;yy.setClass($vertex,$idString)} ; vertex: idString SQS text SQE - {$$ = $1;yy.addVertex($1,$3,'square');} + {$$ = $idString;yy.addVertex($idString,$text,'square');} | idString DOUBLECIRCLESTART text DOUBLECIRCLEEND - {$$ = $1;yy.addVertex($1,$3,'doublecircle');} + {$$ = $idString;yy.addVertex($idString,$text,'doublecircle');} | idString PS PS text PE PE - {$$ = $1;yy.addVertex($1,$4,'circle');} + {$$ = $idString;yy.addVertex($idString,$text,'circle');} | idString '(-' text '-)' - {$$ = $1;yy.addVertex($1,$3,'ellipse');} + {$$ = $idString;yy.addVertex($idString,$text,'ellipse');} | idString STADIUMSTART text STADIUMEND - {$$ = $1;yy.addVertex($1,$3,'stadium');} + {$$ = $idString;yy.addVertex($idString,$text,'stadium');} | idString SUBROUTINESTART text SUBROUTINEEND - {$$ = $1;yy.addVertex($1,$3,'subroutine');} - | idString VERTEX_WITH_PROPS_START NODE_STRING COLON NODE_STRING PIPE text SQE - {$$ = $1;yy.addVertex($1,$7,'rect',undefined,undefined,undefined, Object.fromEntries([[$3, $5]]));} + {$$ = $idString;yy.addVertex($idString,$text,'subroutine');} + | idString VERTEX_WITH_PROPS_START NODE_STRING\[field] COLON NODE_STRING\[value] PIPE text SQE + {$$ = $idString;yy.addVertex($idString,$text,'rect',undefined,undefined,undefined, Object.fromEntries([[$field, $value]]));} | idString CYLINDERSTART text CYLINDEREND - {$$ = $1;yy.addVertex($1,$3,'cylinder');} + {$$ = $idString;yy.addVertex($idString,$text,'cylinder');} | idString PS text PE - {$$ = $1;yy.addVertex($1,$3,'round');} + {$$ = $idString;yy.addVertex($idString,$text,'round');} | idString DIAMOND_START text DIAMOND_STOP - {$$ = $1;yy.addVertex($1,$3,'diamond');} + {$$ = $idString;yy.addVertex($idString,$text,'diamond');} | idString DIAMOND_START DIAMOND_START text DIAMOND_STOP DIAMOND_STOP - {$$ = $1;yy.addVertex($1,$4,'hexagon');} + {$$ = $idString;yy.addVertex($idString,$text,'hexagon');} | idString TAGEND text SQE - {$$ = $1;yy.addVertex($1,$3,'odd');} + {$$ = $idString;yy.addVertex($idString,$text,'odd');} | idString TRAPSTART text TRAPEND - {$$ = $1;yy.addVertex($1,$3,'trapezoid');} + {$$ = $idString;yy.addVertex($idString,$text,'trapezoid');} | idString INVTRAPSTART text INVTRAPEND - {$$ = $1;yy.addVertex($1,$3,'inv_trapezoid');} + {$$ = $idString;yy.addVertex($idString,$text,'inv_trapezoid');} | idString TRAPSTART text INVTRAPEND - {$$ = $1;yy.addVertex($1,$3,'lean_right');} + {$$ = $idString;yy.addVertex($idString,$text,'lean_right');} | idString INVTRAPSTART text TRAPEND - {$$ = $1;yy.addVertex($1,$3,'lean_left');} + {$$ = $idString;yy.addVertex($idString,$text,'lean_left');} | idString - { /*console.warn('h: ', $1);*/$$ = $1;yy.addVertex($1);} + { /*console.warn('h: ', $idString);*/$$ = $idString;yy.addVertex($idString);} ; From 45d92769aa4cbcd41685a6e2394682add487e197 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Mon, 24 Jul 2023 21:04:36 -0300 Subject: [PATCH 19/45] Change rest of grammar statements to use variable name instead of position --- .../src/diagrams/flowchart/parser/flow.jison | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 90c935542..1f5050117 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -447,41 +447,41 @@ vertex: idString SQS text SQE link: linkStatement arrowText - {$1.text = $2;$$ = $1;} + {$linkStatement.text = $arrowText;$$ = $linkStatement;} | linkStatement TESTSTR SPACE - {$1.text = $2;$$ = $1;} + {$linkStatement.text = $TESTSTR;$$ = $linkStatement;} | linkStatement arrowText SPACE - {$1.text = $2;$$ = $1;} + {$linkStatement.text = $arrowText;$$ = $linkStatement;} | linkStatement - {$$ = $1;} + {$$ = $linkStatement;} | START_LINK edgeText LINK - {var inf = yy.destructLink($3, $1); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$2};} + {var inf = yy.destructLink($LINK, $START_LINK); $$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length,"text":$edgeText};} ; edgeText: edgeTextToken - {$$={text:$1, type:'text'};} + {$$={text:$edgeTextToken, type:'text'};} | edgeText edgeTextToken - {$$={text:$1.text+''+$2, type:$1.type};} + {$$={text:$edgeText.text+''+$edgeTextToken, type:$edgeText.type};} | MD_STR - {$$={text:$1, type:'markdown'};} + {$$={text:$MD_STR, type:'markdown'};} ; linkStatement: LINK - {var inf = yy.destructLink($1);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};} + {var inf = yy.destructLink($LINK);$$ = {"type":inf.type,"stroke":inf.stroke,"length":inf.length};} ; arrowText: PIPE text PIPE - {$$ = $2;} + {$$ = $text;} ; text: textToken - { $$={text:$1, type: 'text'};} + { $$={text:$textToken, type: 'text'};} | text textToken - { $$={text:$1.text+''+$2, type: $1.type};} + { $$={text:$text.text+''+$textToken, type: $text.type};} | MD_STR - { $$={text: $1, type: 'markdown'};} + { $$={text: $MD_STR, type: 'markdown'};} ; @@ -491,75 +491,75 @@ keywords textNoTags: textNoTagsToken - {$$={text:$1, type: 'text'};} + {$$={text:$textNoTagsToken, type: 'text'};} | textNoTags textNoTagsToken - {$$={text:$1.text+''+$2, type: $1.type};} + {$$={text:$textNoTags.text+''+$textNoTagsToken, type: $textNoTags.type};} | STR - { $$={text: $1, type: 'text'};} + { $$={text: $STR, type: 'text'};} | MD_STR - { $$={text: $1, type: 'markdown'};} + { $$={text: $MD_STR, type: 'markdown'};} ; classDefStatement:CLASSDEF SPACE idString SPACE stylesOpt - {$$ = $1;yy.addClass($3,$5);} + {$$ = $CLASSDEF;yy.addClass($idString,$stylesOpt);} ; classStatement:CLASS SPACE idString SPACE alphaNum - {$$ = $1;yy.setClass($3, $5);} + {$$ = $CLASS;yy.setClass($idString, $alphaNum);} ; clickStatement - : CLICK CALLBACKNAME {$$ = $1;yy.setClickEvent($1, $2);} - | CLICK CALLBACKNAME SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} - | CLICK CALLBACKNAME CALLBACKARGS {$$ = $1;yy.setClickEvent($1, $2, $3);} - | CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $1;yy.setClickEvent($1, $2, $3);yy.setTooltip($1, $5);} - | CLICK HREF SPACE STR {$$ = $1;yy.setLink($1, $4);} - | CLICK HREF SPACE STR SPACE STR {$$ = $1;yy.setLink($1, $4);yy.setTooltip($1, $6);} - | CLICK HREF SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $4, $6);} - | CLICK HREF SPACE STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $4, $8);yy.setTooltip($1, $6);} - | CLICK alphaNum {$$ = $1;yy.setClickEvent($1, $2);} - | CLICK alphaNum SPACE STR {$$ = $1;yy.setClickEvent($1, $2);yy.setTooltip($1, $4);} - | CLICK STR {$$ = $1;yy.setLink($1, $2);} - | CLICK STR SPACE STR {$$ = $1;yy.setLink($1, $2);yy.setTooltip($1, $4);} - | CLICK STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $4);} - | CLICK STR SPACE STR SPACE LINK_TARGET {$$ = $1;yy.setLink($1, $2, $6);yy.setTooltip($1, $4);} + : CLICK CALLBACKNAME {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME);} + | CLICK CALLBACKNAME SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME);yy.setTooltip($CLICK, $STR);} + | CLICK CALLBACKNAME CALLBACKARGS {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);} + | CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);yy.setTooltip($CLICK, $STR);} + | CLICK HREF SPACE STR {$$ = $CLICK;yy.setLink($CLICK, $STR);} + | CLICK HREF SPACE STR\[link] SPACE STR\[target] {$$ = $CLICK;yy.setLink($CLICK, $link);yy.setTooltip($CLICK, $target);} + | CLICK HREF SPACE STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);} + | CLICK HREF SPACE STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);} + | CLICK alphaNum {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);} + | CLICK alphaNum SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);yy.setTooltip($CLICK, $STR);} + | CLICK STR {$$ = $CLICK;yy.setLink($CLICK, $STR);} + | CLICK STR\[link] SPACE STR\[tooltip] {$$ = $CLICK;yy.setLink($CLICK, $link);yy.setTooltip($CLICK, $tooltip);} + | CLICK STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);} + | CLICK STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);} ; styleStatement:STYLE SPACE idString SPACE stylesOpt - {$$ = $1;yy.addVertex($3,undefined,undefined,$5);} + {$$ = $STYLE;yy.addVertex($idString,undefined,undefined,$stylesOpt);} ; linkStyleStatement : LINKSTYLE SPACE DEFAULT SPACE stylesOpt - {$$ = $1;yy.updateLink([$3],$5);} + {$$ = $LINKSTYLE;yy.updateLink([$DEFAULT],$stylesOpt);} | LINKSTYLE SPACE numList SPACE stylesOpt - {$$ = $1;yy.updateLink($3,$5);} + {$$ = $LINKSTYLE;yy.updateLink($numList,$stylesOpt);} | LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt - {$$ = $1;yy.updateLinkInterpolate([$3],$7);yy.updateLink([$3],$9);} + {$$ = $LINKSTYLE;yy.updateLinkInterpolate([$DEFAULT],$alphaNum);yy.updateLink([$DEFAULT],$stylesOpt);} | LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum SPACE stylesOpt - {$$ = $1;yy.updateLinkInterpolate($3,$7);yy.updateLink($3,$9);} + {$$ = $LINKSTYLE;yy.updateLinkInterpolate($numList,$alphaNum);yy.updateLink($numList,$stylesOpt);} | LINKSTYLE SPACE DEFAULT SPACE INTERPOLATE SPACE alphaNum - {$$ = $1;yy.updateLinkInterpolate([$3],$7);} + {$$ = $LINKSTYLE;yy.updateLinkInterpolate([$DEFAULT],$alphaNum);} | LINKSTYLE SPACE numList SPACE INTERPOLATE SPACE alphaNum - {$$ = $1;yy.updateLinkInterpolate($3,$7);} + {$$ = $LINKSTYLE;yy.updateLinkInterpolate($numList,$alphaNum);} ; numList: NUM - {$$ = [$1]} + {$$ = [$NUM]} | numList COMMA NUM - {$1.push($3);$$ = $1;} + {$numList.push($NUM);$$ = $numList;} ; stylesOpt: style - {$$ = [$1]} + {$$ = [$style]} | stylesOpt COMMA style - {$1.push($3);$$ = $1;} + {$stylesOpt.push($style);$$ = $stylesOpt;} ; style: styleComponent |style styleComponent - {$$ = $1 + $2;} + {$$ = $style + $styleComponent;} ; styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | BRKT | STYLE | PCT ; @@ -575,23 +575,23 @@ edgeTextToken : STR | EDGE_TEXT | UNICODE_TEXT ; idString :idStringToken - {$$=$1} + {$$=$idStringToken} | idString idStringToken - {$$=$1+''+$2} + {$$=$idString+''+$idStringToken} ; alphaNum : alphaNumStatement - {$$=$1;} + {$$=$alphaNumStatement;} | alphaNum alphaNumStatement - {$$=$1+''+$2;} + {$$=$alphaNum+''+$alphaNumStatement;} ; alphaNumStatement : DIR - {$$=$1;} + {$$=$DIR;} | NODE_STRING - {$$=$1;} + {$$=$NODE_STRING;} | DOWN {$$='v';} | MINUS From 4cfbd0d380d5f7940d7c7c0cb933e27206efe6da Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Mon, 24 Jul 2023 21:05:17 -0300 Subject: [PATCH 20/45] Remove unused definitions --- packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 1f5050117..0219fec2d 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -609,7 +609,4 @@ direction { $$={stmt:'dir', value:'LR'};} ; -alphaNumToken : ALPHA_NUM | PUNCTUATION | AMP | UNICODE_TEXT | NUM| ALPHA | COLON | COMMA | PLUS | EQUALS | MULT | DOT | BRKT| UNDERSCORE ; - -graphCodeTokens: STADIUMSTART | STADIUMEND | SUBROUTINESTART | SUBROUTINEEND | VERTEX_WITH_PROPS_START | CYLINDERSTART | CYLINDEREND | TRAPSTART | TRAPEND | INVTRAPSTART | INVTRAPEND | PIPE | PS | PE | SQS | SQE | DIAMOND_START | DIAMOND_STOP | TAGSTART | TAGEND | ARROW_CROSS | ARROW_POINT | ARROW_CIRCLE | ARROW_OPEN | QUOTE | SEMI; %% From 651274bc6f69b2c4b751c62a25453e9425ca8a0b Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Mon, 24 Jul 2023 21:23:21 -0300 Subject: [PATCH 21/45] Only allow quotes to wrap entire string --- .../src/diagrams/flowchart/parser/flow-text.spec.js | 12 +++--------- .../mermaid/src/diagrams/flowchart/parser/flow.jison | 4 +++- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index 922508e80..ef72bb6b8 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -691,15 +691,9 @@ describe('[Text] when parsing', () => { expect(edges[0].text).toBe(',.?!+-*'); }); - it('should handle strings and text at the same time', function () { - const res = flow.parser.parse( - 'graph TD;A(this node has "string" and text)-->|this link has "string" and text|C;' - ); + it('should throw error for strings and text at the same time', function () { + const str = 'graph TD;A(this node has "string" and text)-->|this link has "string" and text|C;'; - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].text).toBe('this node has "string" and text'); - expect(edges[0].text).toBe('this link has "string" and text'); + expect(() => flow.parser.parse(str)).toThrowError("got 'STR'"); }); }); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 0219fec2d..7dbe3b4d4 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -480,6 +480,8 @@ text: textToken { $$={text:$textToken, type: 'text'};} | text textToken { $$={text:$text.text+''+$textToken, type: $text.type};} + | STR + {$$={text: $STR, type: 'text'};} | MD_STR { $$={text: $MD_STR, type: 'markdown'};} ; @@ -567,7 +569,7 @@ styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | BRKT | STYLE | PCT ; /* Token lists */ idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON; -textToken : STR | TEXT | TAGSTART | TAGEND | UNICODE_TEXT; +textToken : TEXT | TAGSTART | TAGEND | UNICODE_TEXT; textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | keywords | START_LINK ; From 47c100809bd2ede57244a5298875ff64ddedf6d6 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Mon, 24 Jul 2023 21:46:28 -0300 Subject: [PATCH 22/45] Add unit tests for all cases of TEXT and STR combinations --- .../flowchart/parser/flow-text.spec.js | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index ef72bb6b8..c043acd29 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -691,9 +691,35 @@ describe('[Text] when parsing', () => { expect(edges[0].text).toBe(',.?!+-*'); }); + it('should throw error at nested set of brackets', function () { + const str = 'graph TD; A[This is a () in text];'; + expect(() => flow.parser.parse(str)).toThrowError("got 'PE'"); + }); + it('should throw error for strings and text at the same time', function () { const str = 'graph TD;A(this node has "string" and text)-->|this link has "string" and text|C;'; expect(() => flow.parser.parse(str)).toThrowError("got 'STR'"); }); + + it('should throw error for escaping quotes in text state', function () { + //prettier-ignore + const str = 'graph TD; A[This is a \"()\" in text];'; //eslint-disable-line no-useless-escape + + expect(() => flow.parser.parse(str)).toThrowError("got 'STR'"); + }); + + it('should throw error for nested quoatation marks', function () { + const str = 'graph TD; A["This is a "()" in text"];'; + + expect(() => flow.parser.parse(str)).toThrowError("Expecting 'SQE'"); + }); + + it('should parse escaped quotes in a string state', function () { + //prettier-ignore + const str = 'graph TD; A["This is a \"()\" in text"];'; //eslint-disable-line no-useless-escape + + flow.parser.parse(str); + expect(flow.parser.getVertices[0].text).toBe('This is a "()" in text'); + }); }); From 20cd685ae371625099f858a7f0c38bcba87420fa Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Mon, 24 Jul 2023 22:43:06 -0300 Subject: [PATCH 23/45] Allow escaped quotations in strings --- packages/mermaid/src/diagrams/flowchart/flowDb.js | 1 + .../src/diagrams/flowchart/parser/flow-md-string.spec.js | 2 +- .../src/diagrams/flowchart/parser/flow-text.spec.js | 7 +++---- packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js index ea8fa71d2..8d17a30c0 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.js +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js @@ -91,6 +91,7 @@ export const addVertex = function (_id, textObj, type, style, classes, dir, prop if (textObj !== undefined) { config = configApi.getConfig(); txt = sanitizeText(textObj.text.trim()); + txt = textObj.type === 'string' ? txt.replaceAll('\\"', '"') : txt; vertices[id].labelType = textObj.type; // strip quotes if string starts and ends with a quote if (txt[0] === '"' && txt[txt.length - 1] === '"') { diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js index 0e6efaef1..9560a33df 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js @@ -24,7 +24,7 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t expect(vert['A'].labelType).toBe('markdown'); expect(vert['B'].id).toBe('B'); expect(vert['B'].text).toBe('The dog in the hog'); - expect(vert['B'].labelType).toBe('text'); + expect(vert['B'].labelType).toBe('string'); expect(edges.length).toBe(2); expect(edges[0].start).toBe('A'); expect(edges[0].end).toBe('B'); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index c043acd29..dd4050adb 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -717,9 +717,8 @@ describe('[Text] when parsing', () => { it('should parse escaped quotes in a string state', function () { //prettier-ignore - const str = 'graph TD; A["This is a \"()\" in text"];'; //eslint-disable-line no-useless-escape - - flow.parser.parse(str); - expect(flow.parser.getVertices[0].text).toBe('This is a "()" in text'); + flow.parser.parse('graph TD; A["This is a \\"()\\" in text"];'); //eslint-disable-line no-useless-escape + const vert = flow.parser.yy.getVertices(); + expect(vert['A'].text).toBe('This is a "()" in text'); }); }); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 7dbe3b4d4..f333702d6 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -61,9 +61,9 @@ Function arguments are optional: 'call ()' simply executes 'callba [^`"]+ { return "MD_STR";} [`]["] { this.popState();} <*>["][`] { this.begin("md_string");} +(\\(?=\")\"|[^"])+ return "STR"; ["] this.popState(); -[^"]+ return "STR"; -<*>["] this.pushState("string"); +<*>["] this.pushState("string"); "style" return 'STYLE'; "default" return 'DEFAULT'; "linkStyle" return 'LINKSTYLE'; @@ -481,7 +481,7 @@ text: textToken | text textToken { $$={text:$text.text+''+$textToken, type: $text.type};} | STR - {$$={text: $STR, type: 'text'};} + { $$ = {text: $STR, type: 'string'};} | MD_STR { $$={text: $MD_STR, type: 'markdown'};} ; From 30a9b5574d90da958f305372078ad9c80522641d Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Tue, 25 Jul 2023 20:34:48 -0300 Subject: [PATCH 24/45] Correct classDef and class grammar Previously, you were allowed to define a class called 'default' but were not allowed to use it because the classStatement grammar expected an alphanum, which did not include the word DEFAULT --- cypress/integration/rendering/flowchart.spec.js | 11 +++++++++++ .../mermaid/src/diagrams/flowchart/parser/flow.jison | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 4f6d6478e..6d8b4a49a 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -891,4 +891,15 @@ graph TD { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } ); }); + it('66: apply class called default on node called default', () => { + imgSnapshotTest( + ` + graph TD + classDef default fill:#000,stroke:#000,stroke-width:4px,color:#fff + default --> default2 + class default,default2 default + `, + { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } + ); + }); }); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index f333702d6..2ed1c7b2e 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -507,8 +507,8 @@ classDefStatement:CLASSDEF SPACE idString SPACE stylesOpt {$$ = $CLASSDEF;yy.addClass($idString,$stylesOpt);} ; -classStatement:CLASS SPACE idString SPACE alphaNum - {$$ = $CLASS;yy.setClass($idString, $alphaNum);} +classStatement:CLASS SPACE idString\[vertex] SPACE idString\[class] + {$$ = $CLASS;yy.setClass($vertex, $class);} ; clickStatement From fd461b7860f93dcc33fb24aa4a10333685e433a1 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Tue, 25 Jul 2023 20:56:08 -0300 Subject: [PATCH 25/45] Reimplement old alphaNumToken --- .../src/diagrams/flowchart/parser/flow.jison | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 2ed1c7b2e..6bc434c3c 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -175,7 +175,7 @@ that id. "^" return 'UP'; "\|" return 'SEP'; "v" return 'DOWN'; -([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.]))+ return 'NODE_STRING'; +([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.])|=(?!=))+ return 'NODE_STRING'; "-" return 'MINUS' [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| [\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]| @@ -575,6 +575,8 @@ textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | keywords | START_LINK ; edgeTextToken : STR | EDGE_TEXT | UNICODE_TEXT ; +alphaNumToken : NUM | UNICODE_TEXT | NODE_STRING | DIR | DOWN | MINUS | COMMA; + idString :idStringToken {$$=$idStringToken} @@ -583,22 +585,12 @@ idString ; alphaNum - : alphaNumStatement - {$$=$alphaNumStatement;} - | alphaNum alphaNumStatement - {$$=$alphaNum+''+$alphaNumStatement;} + : alphaNumToken + {$$=$alphaNumToken;} + | alphaNum alphaNumToken + {$$=$alphaNum+''+$alphaNumToken;} ; -alphaNumStatement - : DIR - {$$=$DIR;} - | NODE_STRING - {$$=$NODE_STRING;} - | DOWN - {$$='v';} - | MINUS - {$$='-';} - ; direction : direction_tb From 1df68f9a5fffdab42b3f406946023f18110f156e Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Wed, 26 Jul 2023 21:46:12 -0300 Subject: [PATCH 26/45] Remove class statement to show application of default class --- cypress/integration/rendering/flowchart.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 6d8b4a49a..81c1ceeb9 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -897,7 +897,6 @@ graph TD graph TD classDef default fill:#000,stroke:#000,stroke-width:4px,color:#fff default --> default2 - class default,default2 default `, { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } ); From 9eed2e278bd88bc4caf833e09ce23ec25d76ed74 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Wed, 26 Jul 2023 22:36:26 -0300 Subject: [PATCH 27/45] Add imgSnapshotTest for escaped quotes --- cypress/integration/rendering/flowchart.spec.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 81c1ceeb9..3f7368a3a 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -901,4 +901,10 @@ graph TD { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } ); }); + it('67: allow escaping quotes with backslash', () => { + imgSnapshotTest(` + graph TD + a_node("This has an escaped \\" in it") -- "edge string can escape too \\"" --> b_node + `); + }); }); From d8897426cde74efe044751f2921d127ebac940c6 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Wed, 26 Jul 2023 22:36:57 -0300 Subject: [PATCH 28/45] Correct edge strings to have same configuration as vertex strings --- packages/mermaid/src/diagrams/flowchart/flowDb.js | 1 + .../src/diagrams/flowchart/parser/flow-md-string.spec.js | 2 +- packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js index 8d17a30c0..33ae68a86 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.js +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js @@ -150,6 +150,7 @@ export const addSingleLink = function (_start, _end, type) { if (linkTextObj !== undefined) { edge.text = sanitizeText(linkTextObj.text.trim()); + edge.text = edge.text.replaceAll('\\"', '"'); // strip quotes if string starts and ends with a quote if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') { edge.text = edge.text.substring(1, edge.text.length - 1); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js index 9560a33df..13cb262e3 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-md-string.spec.js @@ -35,7 +35,7 @@ A["\`The cat in **the** hat\`"]-- "\`The *bat* in the chat\`" -->B["The dog in t expect(edges[1].end).toBe('C'); expect(edges[1].type).toBe('arrow_point'); expect(edges[1].text).toBe('The rat in the mat'); - expect(edges[1].labelType).toBe('text'); + expect(edges[1].labelType).toBe('string'); }); it('mardown formatting in subgraphs', function () { const res = flow.parser.parse(`flowchart LR diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 6bc434c3c..e8f0775b8 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -462,6 +462,8 @@ edgeText: edgeTextToken {$$={text:$edgeTextToken, type:'text'};} | edgeText edgeTextToken {$$={text:$edgeText.text+''+$edgeTextToken, type:$edgeText.type};} + |STR + {$$={text: $STR, type: 'string'};} | MD_STR {$$={text:$MD_STR, type:'markdown'};} ; @@ -573,7 +575,7 @@ textToken : TEXT | TAGSTART | TAGEND | UNICODE_TEXT; textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | keywords | START_LINK ; -edgeTextToken : STR | EDGE_TEXT | UNICODE_TEXT ; +edgeTextToken : EDGE_TEXT | UNICODE_TEXT ; alphaNumToken : NUM | UNICODE_TEXT | NODE_STRING | DIR | DOWN | MINUS | COMMA; From e25763d64574b847535aadc7bb56f73e433cb169 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Fri, 28 Jul 2023 21:19:30 -0300 Subject: [PATCH 29/45] Modified docs to mention escaping quotes with backslash --- docs/syntax/flowchart.md | 5 +++-- packages/mermaid/src/docs/syntax/flowchart.md | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/syntax/flowchart.md b/docs/syntax/flowchart.md index 92678fb6e..97c0113bf 100644 --- a/docs/syntax/flowchart.md +++ b/docs/syntax/flowchart.md @@ -605,15 +605,16 @@ flowchart LR ### Entity codes to escape characters It is possible to escape characters using the syntax exemplified here. +For quotation marks, you can escape them with a backslash. ```mermaid-example flowchart LR - A["A double quote:#quot;"] -->B["A dec char:#9829;"] + A["A double quote:\""] -->B["A dec char:#9829;"] ``` ```mermaid flowchart LR - A["A double quote:#quot;"] -->B["A dec char:#9829;"] + A["A double quote:\""] -->B["A dec char:#9829;"] ``` Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. diff --git a/packages/mermaid/src/docs/syntax/flowchart.md b/packages/mermaid/src/docs/syntax/flowchart.md index 2e3d78c30..2a3c003b2 100644 --- a/packages/mermaid/src/docs/syntax/flowchart.md +++ b/packages/mermaid/src/docs/syntax/flowchart.md @@ -387,10 +387,11 @@ flowchart LR ### Entity codes to escape characters It is possible to escape characters using the syntax exemplified here. +For quotation marks, you can escape them with a backslash. ```mermaid-example flowchart LR - A["A double quote:#quot;"] -->B["A dec char:#9829;"] + A["A double quote:\""] -->B["A dec char:#9829;"] ``` Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. From f269f8cc5b94edde1bff1b1b191e321f422e98a3 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sat, 29 Jul 2023 21:14:02 -0300 Subject: [PATCH 30/45] Refactor unit tests for vertex shape --- .../flowchart/parser/flow-text.spec.js | 183 +++--------------- 1 file changed, 32 insertions(+), 151 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index dd4050adb..3898e8775 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -328,81 +328,34 @@ describe('[Text] when parsing', () => { 'kitty', ]; - it.each(keywords)('should handle %s keyword in square vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B[This node has a ${keyword} as text];` - ); + const shapes = [ + { start: '[', end: ']', name: 'square' }, + { start: '(', end: ')', name: 'round' }, + { start: '{', end: '}', name: 'diamond' }, + { start: '(-', end: '-)', name: 'ellipse' }, + { start: '([', end: '])', name: 'stadium' }, + { start: '>', end: ']', name: 'odd' }, + { start: '[(', end: ')]', name: 'cylinder' }, + { start: '(((', end: ')))', name: 'doublecircle' }, + { start: '[/', end: '\\]', name: 'trapezoid' }, + { start: '[\\', end: '/]', name: 'inv_trapezoid' }, + { start: '[/', end: '/]', name: 'lean_right' }, + { start: '[\\', end: '\\]', name: 'lean_left' }, + { start: '[[', end: ']]', name: 'subroutine' }, + { start: '{{', end: '}}', name: 'hexagon' }, + ]; - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('square'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); + shapes.forEach((shape) => { + it.each(keywords)(`should handle %s keyword in ${shape.name} vertex`, function (keyword) { + const rest = flow.parser.parse( + `graph TD;A_${keyword}_node-->B${shape.start}This node has a ${keyword} as text${shape.end};` + ); - it.each(keywords)('should handle %s keyword in round vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B(This node has a ${keyword} as text);` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('round'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in diamond vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B{This node has a ${keyword} as text};` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('diamond'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in doublecircle vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}-->-${keyword}-B(((This node has a ${keyword} as text)));` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert[`-${keyword}-B`].type).toBe('doublecircle'); - expect(vert[`-${keyword}-B`].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in ellipse vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}-->B(-This node has a ${keyword} as text-);` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('ellipse'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in stadium vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B([This node has a ${keyword} as text]);` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('stadium'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in subroutine vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B[[This node has a ${keyword} as text]];` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('subroutine'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + expect(vert['B'].type).toBe(`${shape.name}`); + expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); + }); }); it.each(keywords)('should handle %s keyword in rect vertex', function (keyword) { @@ -416,72 +369,6 @@ describe('[Text] when parsing', () => { expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); }); - it.each(keywords)('should handle %s keyword in cylinder vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B[(This node has a ${keyword} as text)];` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('cylinder'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in hexagon vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B{{This node has a ${keyword} as text}};` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('hexagon'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in odd vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B>This node has a ${keyword} as text];` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('odd'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in trapezoid vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B[/This node has a ${keyword} as text\\];` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('trapezoid'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in inv_trapezoid vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B[\\This node has a ${keyword} as text/];` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('inv_trapezoid'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - - it.each(keywords)('should handle %s keyword in lean_right vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B[/This node has a ${keyword} as text/];` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('lean_right'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - it('should allow forward slashes in lean_right vertices', function () { const rest = flow.parser.parse(`graph TD;A_node-->B[/This node has a / as text/];`); @@ -500,17 +387,6 @@ describe('[Text] when parsing', () => { expect(vert['B'].text).toBe(`This node has a \\ as text`); }); - it.each(keywords)('should handle %s keyword in lean_left vertex', function (keyword) { - const rest = flow.parser.parse( - `graph TD;A_${keyword}_node-->B[\\This node has a ${keyword} as text\\];` - ); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - expect(vert['B'].type).toBe('lean_left'); - expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); - }); - it('should handle åäö and minus', function () { const res = flow.parser.parse('graph TD;A-->C{Chimpansen hoppar åäö-ÅÄÖ};'); @@ -693,7 +569,7 @@ describe('[Text] when parsing', () => { it('should throw error at nested set of brackets', function () { const str = 'graph TD; A[This is a () in text];'; - expect(() => flow.parser.parse(str)).toThrowError("got 'PE'"); + expect(() => flow.parser.parse(str)).toThrowError("got 'PS'"); }); it('should throw error for strings and text at the same time', function () { @@ -721,4 +597,9 @@ describe('[Text] when parsing', () => { const vert = flow.parser.yy.getVertices(); expect(vert['A'].text).toBe('This is a "()" in text'); }); + + it('should throw error', function () { + const str = `graph TD; node[hello ) world] --> works`; + expect(() => flow.parser.parse(str)).toThrowError("got 'PE'"); + }); }); From bed05ce0619eac4ff26478a145559a9b59f84d6f Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sat, 29 Jul 2023 21:23:05 -0300 Subject: [PATCH 31/45] Disallow any vertex shape start or end token in any text state --- packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index e8f0775b8..49a56e82b 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -143,7 +143,7 @@ that id. <*>\s*\~\~[\~]+\s* return 'LINK'; [-/\)][\)] { this.popState(); return '-)'; } -[^/)]|-/!\)+ return "TEXT" +[^\(\)\[\]\{\}]|-/!\)+ return "TEXT" <*>"(-" { this.pushState("ellipseText"); return '(-'; } "])" { this.popState(); return 'STADIUMEND'; } @@ -164,7 +164,7 @@ that id. [\\(?=\])][\]] { this.popState(); return 'TRAPEND'; } \/(?=\])\] { this.popState(); return 'INVTRAPEND'; } -\/(?!\])|\\(?!\])|[^\\\]\/]+ return 'TEXT'; +\/(?!\])|\\(?!\])|[^\\\[\]\(\)\{\}\/]+ return 'TEXT'; <*>"[/" { this.pushState("trapText"); return 'TRAPSTART'; } <*>"[\\" { this.pushState("trapText"); return 'INVTRAPSTART'; } @@ -251,7 +251,7 @@ that id. (\}) { this.popState(); return 'DIAMOND_STOP' } <*>"{" { this.pushState("text"); return 'DIAMOND_START' } -[^\]\)\}\|\"]+ return "TEXT"; +[^\[\]\(\)\{\}\|\"]+ return "TEXT"; "\"" return 'QUOTE'; (\r?\n)+ return 'NEWLINE'; From 844f9d96e75ed3713b5a7ddadd337815a56d66b0 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sat, 29 Jul 2023 22:16:28 -0300 Subject: [PATCH 32/45] Update and add new imgSnapshotTest --- cypress/integration/rendering/flowchart.spec.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 3f7368a3a..1ae10aba9 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -895,8 +895,8 @@ graph TD imgSnapshotTest( ` graph TD - classDef default fill:#000,stroke:#000,stroke-width:4px,color:#fff - default --> default2 + classDef default fill:#a34,stroke:#000,stroke-width:4px,color:#fff + hello --> default `, { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } ); @@ -907,4 +907,16 @@ graph TD a_node("This has an escaped \\" in it") -- "edge string can escape too \\"" --> b_node `); }); + it('68: should be able to style default node independently', () => { + imgSnapshotTest( + ` + flowchart TD + classDef default fill:#a34 + hello --> default + + style default stroke:#000,stroke-width:4px + `, + { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } + ); + }); }); From ed4feaebf2a237e98b363526f31360e793cfb396 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sat, 29 Jul 2023 22:27:31 -0300 Subject: [PATCH 33/45] Remove required space from TAGEND token regex Originally, I thought this was necessary to prevent parsing the token as part of an edge. I forgot that the token will always be separated from the link/edge by the node id. Added an unit test for an edge case to be certain. --- .../src/diagrams/flowchart/parser/flow-text.spec.js | 7 +++++++ packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index 3898e8775..3dfde5ccb 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -369,6 +369,13 @@ describe('[Text] when parsing', () => { expect(vert['B'].text).toBe(`This node has a ${keyword} as text`); }); + it('should handle edge case for odd vertex with node id ending with minus', function () { + const res = flow.parser.parse('graph TD;A_node-->odd->Vertex Text];'); + const vert = flow.parser.yy.getVertices(); + + expect(vert['odd-'].type).toBe('odd'); + expect(vert['odd-'].text).toBe('Vertex Text'); + }); it('should allow forward slashes in lean_right vertices', function () { const rest = flow.parser.parse(`graph TD;A_node-->B[/This node has a / as text/];`); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 49a56e82b..a4584c808 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -154,7 +154,7 @@ that id. "[|" { return 'VERTEX_WITH_PROPS_START'; } -\>/!\s { this.pushState("text"); return 'TAGEND'; } +\> { this.pushState("text"); return 'TAGEND'; } ")]" { this.popState(); return 'CYLINDEREND'; } <*>"[(" { this.pushState("text") ;return 'CYLINDERSTART'; } From b5456813e855dfc6396ee234e05ebda5b21c2832 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Sat, 29 Jul 2023 22:44:18 -0300 Subject: [PATCH 34/45] Show escaped quotes in docs using old and new method --- docs/syntax/flowchart.md | 8 +++++--- packages/mermaid/src/docs/syntax/flowchart.md | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/syntax/flowchart.md b/docs/syntax/flowchart.md index 97c0113bf..5a338c261 100644 --- a/docs/syntax/flowchart.md +++ b/docs/syntax/flowchart.md @@ -605,16 +605,18 @@ flowchart LR ### Entity codes to escape characters It is possible to escape characters using the syntax exemplified here. -For quotation marks, you can escape them with a backslash. +For quotation marks, you can escape them with a backslash as well. ```mermaid-example flowchart LR - A["A double quote:\""] -->B["A dec char:#9829;"] + A["A double quote:#quot;"] --> B["A dec char:#9829;"] + B --> C["This is a \"square\" vertex"] ``` ```mermaid flowchart LR - A["A double quote:\""] -->B["A dec char:#9829;"] + A["A double quote:#quot;"] --> B["A dec char:#9829;"] + B --> C["This is a \"square\" vertex"] ``` Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. diff --git a/packages/mermaid/src/docs/syntax/flowchart.md b/packages/mermaid/src/docs/syntax/flowchart.md index 2a3c003b2..b5308ad50 100644 --- a/packages/mermaid/src/docs/syntax/flowchart.md +++ b/packages/mermaid/src/docs/syntax/flowchart.md @@ -387,11 +387,12 @@ flowchart LR ### Entity codes to escape characters It is possible to escape characters using the syntax exemplified here. -For quotation marks, you can escape them with a backslash. +For quotation marks, you can escape them with a backslash as well. ```mermaid-example flowchart LR - A["A double quote:\""] -->B["A dec char:#9829;"] + A["A double quote:#quot;"] --> B["A dec char:#9829;"] + B --> C["This is a \"square\" vertex"] ``` Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. From 834c67ecaaacf46533f9be16d694eb9c50c0ad44 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Mon, 31 Jul 2023 21:35:56 -0300 Subject: [PATCH 35/45] Remove escaped quotes with backslash feature --- cypress/integration/rendering/flowchart.spec.js | 9 ++------- docs/syntax/flowchart.md | 2 -- packages/mermaid/src/diagrams/flowchart/flowDb.js | 1 - .../src/diagrams/flowchart/parser/flow-text.spec.js | 7 ------- .../mermaid/src/diagrams/flowchart/parser/flow.jison | 4 ++-- packages/mermaid/src/docs/syntax/flowchart.md | 1 - 6 files changed, 4 insertions(+), 20 deletions(-) diff --git a/cypress/integration/rendering/flowchart.spec.js b/cypress/integration/rendering/flowchart.spec.js index 1ae10aba9..e4766e792 100644 --- a/cypress/integration/rendering/flowchart.spec.js +++ b/cypress/integration/rendering/flowchart.spec.js @@ -901,13 +901,8 @@ graph TD { htmlLabels: true, flowchart: { htmlLabels: true }, securityLevel: 'loose' } ); }); - it('67: allow escaping quotes with backslash', () => { - imgSnapshotTest(` - graph TD - a_node("This has an escaped \\" in it") -- "edge string can escape too \\"" --> b_node - `); - }); - it('68: should be able to style default node independently', () => { + + it('67: should be able to style default node independently', () => { imgSnapshotTest( ` flowchart TD diff --git a/docs/syntax/flowchart.md b/docs/syntax/flowchart.md index 5a338c261..fb0b7f2e3 100644 --- a/docs/syntax/flowchart.md +++ b/docs/syntax/flowchart.md @@ -610,13 +610,11 @@ For quotation marks, you can escape them with a backslash as well. ```mermaid-example flowchart LR A["A double quote:#quot;"] --> B["A dec char:#9829;"] - B --> C["This is a \"square\" vertex"] ``` ```mermaid flowchart LR A["A double quote:#quot;"] --> B["A dec char:#9829;"] - B --> C["This is a \"square\" vertex"] ``` Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js index 33ae68a86..32ffb3df0 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.js +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js @@ -91,7 +91,6 @@ export const addVertex = function (_id, textObj, type, style, classes, dir, prop if (textObj !== undefined) { config = configApi.getConfig(); txt = sanitizeText(textObj.text.trim()); - txt = textObj.type === 'string' ? txt.replaceAll('\\"', '"') : txt; vertices[id].labelType = textObj.type; // strip quotes if string starts and ends with a quote if (txt[0] === '"' && txt[txt.length - 1] === '"') { diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js index 3dfde5ccb..b127e1b65 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-text.spec.js @@ -598,13 +598,6 @@ describe('[Text] when parsing', () => { expect(() => flow.parser.parse(str)).toThrowError("Expecting 'SQE'"); }); - it('should parse escaped quotes in a string state', function () { - //prettier-ignore - flow.parser.parse('graph TD; A["This is a \\"()\\" in text"];'); //eslint-disable-line no-useless-escape - const vert = flow.parser.yy.getVertices(); - expect(vert['A'].text).toBe('This is a "()" in text'); - }); - it('should throw error', function () { const str = `graph TD; node[hello ) world] --> works`; expect(() => flow.parser.parse(str)).toThrowError("got 'PE'"); diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index a4584c808..8268722f3 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -60,8 +60,8 @@ Function arguments are optional: 'call ()' simply executes 'callba [^`"]+ { return "MD_STR";} [`]["] { this.popState();} -<*>["][`] { this.begin("md_string");} -(\\(?=\")\"|[^"])+ return "STR"; +<*>["][`] { this.begin("md_string");} +[^"]+ return "STR"; ["] this.popState(); <*>["] this.pushState("string"); "style" return 'STYLE'; diff --git a/packages/mermaid/src/docs/syntax/flowchart.md b/packages/mermaid/src/docs/syntax/flowchart.md index b5308ad50..960bf9755 100644 --- a/packages/mermaid/src/docs/syntax/flowchart.md +++ b/packages/mermaid/src/docs/syntax/flowchart.md @@ -392,7 +392,6 @@ For quotation marks, you can escape them with a backslash as well. ```mermaid-example flowchart LR A["A double quote:#quot;"] --> B["A dec char:#9829;"] - B --> C["This is a \"square\" vertex"] ``` Numbers given are base 10, so `#` can be encoded as `#35;`. It is also supported to use HTML character names. From ef4f22841f87d3a0b1ac3e48f0d170c005edf3d2 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Tue, 1 Aug 2023 07:54:32 -0300 Subject: [PATCH 36/45] Add unit tests for stange node names --- .../flowchart/parser/flow-singlenode.spec.js | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js index 6b6e93857..e078af4cb 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js @@ -299,11 +299,44 @@ describe('[Singlenodes] when parsing', () => { expect(vert[`a_${keyword}_node`].text).toBe(`a_${keyword}_node`); }); - it.each(keywords)('should handle nodes ending in keywords', function (keyword) { + it.each(keywords)('should handle nodes ending in %s', function (keyword) { const res = flow.parser.parse(`graph TD;node_${keyword};node.${keyword};node-${keyword};`); const vert = flow.parser.yy.getVertices(); expect(vert[`node_${keyword}`].text).toBe(`node_${keyword}`); expect(vert[`node.${keyword}`].text).toBe(`node.${keyword}`); expect(vert[`node-${keyword}`].text).toBe(`node-${keyword}`); }); + + const errorKeywords = [ + 'graph', + 'flowchart', + 'flowchart-elk', + 'style', + 'linkStyle', + 'interpolate', + 'classDef', + 'class', + '_self', + '_blank', + '_parent', + '_top', + 'end', + 'subgraph', + ]; + it.each(errorKeywords)('should throw error at nodes beginning with %s', function (keyword) { + const str = `graph TD;${keyword}.node;${keyword}-node;${keyword}/node`; + const vert = flow.parser.yy.getVertices(); + + expect(() => flow.parser.parse(str)).toThrowError(); + }); + + const workingKeywords = ['default', 'href', 'click', 'call']; + + it.each(workingKeywords)('should parse node beginning with %s', function (keyword) { + flow.parser.parse(`graph TD; ${keyword}.node;${keyword}-node;${keyword}/node;`); + const vert = flow.parser.yy.getVertices(); + expect(vert[`${keyword}.node`].text).toBe(`${keyword}.node`); + expect(vert[`${keyword}-node`].text).toBe(`${keyword}-node`); + expect(vert[`${keyword}/node`].text).toBe(`${keyword}/node`); + }); }); From e3c5e6f095e591b653b3a806d6cac27d7f462321 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Tue, 1 Aug 2023 07:54:48 -0300 Subject: [PATCH 37/45] Modify HREF token regex to contain space This attempts to maintain the current behaviour. Previously, because HREF contained a space and called a state, the href token was able to be placed in the beginning of node ids (because it wouldn't conflict without the space). We require the space to keep that behaviour. --- .../mermaid/src/diagrams/flowchart/parser/flow.jison | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 8268722f3..e303dd178 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -77,7 +77,7 @@ Function arguments are optional: 'call ()' simply executes 'callba line was introduced with 'click'. 'href ""' attaches the specified link to the node that was specified by 'click'. */ -"href" return 'HREF'; +"href"[\s] return 'HREF'; /* @@ -518,10 +518,10 @@ clickStatement | CLICK CALLBACKNAME SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME);yy.setTooltip($CLICK, $STR);} | CLICK CALLBACKNAME CALLBACKARGS {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);} | CLICK CALLBACKNAME CALLBACKARGS SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $CALLBACKNAME, $CALLBACKARGS);yy.setTooltip($CLICK, $STR);} - | CLICK HREF SPACE STR {$$ = $CLICK;yy.setLink($CLICK, $STR);} - | CLICK HREF SPACE STR\[link] SPACE STR\[target] {$$ = $CLICK;yy.setLink($CLICK, $link);yy.setTooltip($CLICK, $target);} - | CLICK HREF SPACE STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);} - | CLICK HREF SPACE STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);} + | CLICK HREF STR {$$ = $CLICK;yy.setLink($CLICK, $STR);} + | CLICK HREF STR SPACE STR {$$ = $CLICK;yy.setLink($CLICK, $STR1);yy.setTooltip($CLICK, $STR2);} + | CLICK HREF STR SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $STR, $LINK_TARGET);} + | CLICK HREF STR\[link] SPACE STR\[tooltip] SPACE LINK_TARGET {$$ = $CLICK;yy.setLink($CLICK, $link, $LINK_TARGET);yy.setTooltip($CLICK, $tooltip);} | CLICK alphaNum {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);} | CLICK alphaNum SPACE STR {$$ = $CLICK;yy.setClickEvent($CLICK, $alphaNum);yy.setTooltip($CLICK, $STR);} | CLICK STR {$$ = $CLICK;yy.setLink($CLICK, $STR);} From dc57fcf7e7ddeb66ecdf4c1d07b93804068d1d01 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Tue, 1 Aug 2023 16:36:37 -0300 Subject: [PATCH 38/45] Remove replaceAll method in addLink --- packages/mermaid/src/diagrams/flowchart/flowDb.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.js b/packages/mermaid/src/diagrams/flowchart/flowDb.js index 32ffb3df0..ea8fa71d2 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.js +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.js @@ -149,7 +149,6 @@ export const addSingleLink = function (_start, _end, type) { if (linkTextObj !== undefined) { edge.text = sanitizeText(linkTextObj.text.trim()); - edge.text = edge.text.replaceAll('\\"', '"'); // strip quotes if string starts and ends with a quote if (edge.text[0] === '"' && edge.text[edge.text.length - 1] === '"') { edge.text = edge.text.substring(1, edge.text.length - 1); From 199fdce982956e3b40acebf6e4fa080cec042ef2 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Thu, 3 Aug 2023 18:59:39 -0300 Subject: [PATCH 39/45] Add unit tests for node ids with special Chars --- .../flowchart/parser/flow-singlenode.spec.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js index e078af4cb..d5797933d 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js @@ -27,6 +27,8 @@ const keywords = [ 'subgraph', ]; +const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-']; + describe('[Singlenodes] when parsing', () => { beforeEach(function () { flow.parser.yy = flowDb; @@ -339,4 +341,31 @@ describe('[Singlenodes] when parsing', () => { expect(vert[`${keyword}-node`].text).toBe(`${keyword}-node`); expect(vert[`${keyword}/node`].text).toBe(`${keyword}/node`); }); + + it.each(specialChars)( + 'should allow node ids of single special characters', + function (specialChar) { + flow.parser.parse(`graph TD; ${specialChar} --> A`); + const vert = flow.parser.yy.getVertices(); + expect(vert[`${specialChar}`].text).toBe(`${specialChar}`); + } + ); + + it.each(specialChars)( + 'should allow node ids with special characters at start of id', + function (specialChar) { + flow.parser.parse(`graph TD; ${specialChar}node --> A`); + const vert = flow.parser.yy.getVertices(); + expect(vert[`${specialChar}node`].text).toBe(`${specialChar}node`); + } + ); + + it.each(specialChars)( + 'should allow node ids with special characters at end of id', + function (specialChar) { + flow.parser.parse(`graph TD; node${specialChar} --> A`); + const vert = flow.parser.yy.getVertices(); + expect(vert[`node${specialChar}`].text).toBe(`node${specialChar}`); + } + ); }); From 971215e353eac693a8a04613bb1670f98b7ab9d1 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Thu, 3 Aug 2023 19:15:23 -0300 Subject: [PATCH 40/45] Correct idStringToken definition to include all individual special tokens --- packages/mermaid/src/diagram.spec.ts | 2 +- packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagram.spec.ts b/packages/mermaid/src/diagram.spec.ts index 75a9e2d2c..ae93a121a 100644 --- a/packages/mermaid/src/diagram.spec.ts +++ b/packages/mermaid/src/diagram.spec.ts @@ -49,7 +49,7 @@ describe('diagram detection', () => { "Parse error on line 2: graph TD; A--> --------------^ - Expecting 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'MINUS', got 'EOF'" + Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', got 'EOF'" `); await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects .toThrowErrorMatchingInlineSnapshot(` diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index e303dd178..54d28b553 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -175,6 +175,9 @@ that id. "^" return 'UP'; "\|" return 'SEP'; "v" return 'DOWN'; +"*" return 'MULT'; +"#" return 'BRKT'; +"&" return 'AMP'; ([A-Za-z0-9!"\#$%&'*+\.`?\\_\/]|\-(?=[^\>\-\.])|=(?!=))+ return 'NODE_STRING'; "-" return 'MINUS' [\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]| @@ -569,7 +572,7 @@ style: styleComponent styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | BRKT | STYLE | PCT ; /* Token lists */ -idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON; +idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON | AMP | BRKT | MULT; textToken : TEXT | TAGSTART | TAGEND | UNICODE_TEXT; From ee6fa94aa247f528dfc1e4ef4bf2981882acab0e Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Thu, 3 Aug 2023 20:05:11 -0300 Subject: [PATCH 41/45] Refactor unit tests and remove repetition --- .../flowchart/parser/flow-edges.spec.js | 362 +++--------------- .../flowchart/parser/flow-singlenode.spec.js | 2 +- 2 files changed, 61 insertions(+), 303 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-edges.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-edges.spec.js index 80dce1bd6..21f3a4355 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-edges.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-edges.spec.js @@ -28,6 +28,18 @@ const keywords = [ 'kitty', ]; +const doubleEndedEdges = [ + { edgeStart: 'x--', edgeEnd: '--x', stroke: 'normal', type: 'double_arrow_cross' }, + { edgeStart: 'x==', edgeEnd: '==x', stroke: 'thick', type: 'double_arrow_cross' }, + { edgeStart: 'x-.', edgeEnd: '.-x', stroke: 'dotted', type: 'double_arrow_cross' }, + { edgeStart: 'o--', edgeEnd: '--o', stroke: 'normal', type: 'double_arrow_circle' }, + { edgeStart: 'o==', edgeEnd: '==o', stroke: 'thick', type: 'double_arrow_circle' }, + { edgeStart: 'o-.', edgeEnd: '.-o', stroke: 'dotted', type: 'double_arrow_circle' }, + { edgeStart: '<--', edgeEnd: '-->', stroke: 'normal', type: 'double_arrow_point' }, + { edgeStart: '<==', edgeEnd: '==>', stroke: 'thick', type: 'double_arrow_point' }, + { edgeStart: '<-.', edgeEnd: '.->', stroke: 'dotted', type: 'double_arrow_point' }, +]; + describe('[Edges] when parsing', () => { beforeEach(function () { flow.parser.yy = flowDb; @@ -61,302 +73,10 @@ describe('[Edges] when parsing', () => { expect(edges[0].type).toBe('arrow_circle'); }); - describe('cross', function () { - it('should handle double edged nodes and edges', function () { - const res = flow.parser.parse('graph TD;\nA x--x B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_cross'); - expect(edges[0].text).toBe(''); - expect(edges[0].stroke).toBe('normal'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes with text', function () { - const res = flow.parser.parse('graph TD;\nA x-- text --x B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_cross'); - expect(edges[0].text).toBe('text'); - expect(edges[0].stroke).toBe('normal'); - expect(edges[0].length).toBe(1); - }); - - it.each(keywords)('should handle %s as text in double edged nodes', function (keyword) { - const res = flow.parser.parse(`graph TD;\nA x-- ${keyword} --x B;`); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_cross'); - expect(edges[0].text).toBe(`${keyword}`); - expect(edges[0].stroke).toBe('normal'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes and edges on thick arrows', function () { - const res = flow.parser.parse('graph TD;\nA x==x B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_cross'); - expect(edges[0].text).toBe(''); - expect(edges[0].stroke).toBe('thick'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes with text on thick arrows', function () { - const res = flow.parser.parse('graph TD;\nA x== text ==x B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_cross'); - expect(edges[0].text).toBe('text'); - expect(edges[0].stroke).toBe('thick'); - expect(edges[0].length).toBe(1); - }); - - it.each(keywords)('should handle %s as text in thick double edged nodes', function (keyword) { - const res = flow.parser.parse(`graph TD;\nA x== ${keyword} ==x B;`); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_cross'); - expect(edges[0].text).toBe(`${keyword}`); - expect(edges[0].stroke).toBe('thick'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes and edges on dotted arrows', function () { - const res = flow.parser.parse('graph TD;\nA x-.-x B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_cross'); - expect(edges[0].text).toBe(''); - expect(edges[0].stroke).toBe('dotted'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes with text on dotted arrows', function () { - const res = flow.parser.parse('graph TD;\nA x-. text .-x B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_cross'); - expect(edges[0].text).toBe('text'); - expect(edges[0].stroke).toBe('dotted'); - expect(edges[0].length).toBe(1); - }); - }); - - it.each(keywords)('should handle %s as text in dotted double edged nodes', function (keyword) { - const res = flow.parser.parse(`graph TD;\nA x-. ${keyword} .-x B;`); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_cross'); - expect(edges[0].text).toBe(`${keyword}`); - expect(edges[0].stroke).toBe('dotted'); - expect(edges[0].length).toBe(1); - }); - - describe('circle', function () { - it('should handle double edged nodes and edges', function () { - const res = flow.parser.parse('graph TD;\nA o--o B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_circle'); - expect(edges[0].text).toBe(''); - expect(edges[0].stroke).toBe('normal'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes with text', function () { - const res = flow.parser.parse('graph TD;\nA o-- text --o B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_circle'); - expect(edges[0].text).toBe('text'); - expect(edges[0].stroke).toBe('normal'); - expect(edges[0].length).toBe(1); - }); - - it.each(keywords)('should handle double edged nodes with %s as text', function (keyword) { - const res = flow.parser.parse(`graph TD;\nA o-- ${keyword} --o B;`); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_circle'); - expect(edges[0].text).toBe(`${keyword}`); - expect(edges[0].stroke).toBe('normal'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes and edges on thick arrows', function () { - const res = flow.parser.parse('graph TD;\nA o==o B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_circle'); - expect(edges[0].text).toBe(''); - expect(edges[0].stroke).toBe('thick'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes with text on thick arrows', function () { - const res = flow.parser.parse('graph TD;\nA o== text ==o B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_circle'); - expect(edges[0].text).toBe('text'); - expect(edges[0].stroke).toBe('thick'); - expect(edges[0].length).toBe(1); - }); - - it.each(keywords)('should handle thick double edged nodes with %s as text', function (keyword) { - const res = flow.parser.parse(`graph TD;\nA o== ${keyword} ==o B;`); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_circle'); - expect(edges[0].text).toBe(`${keyword}`); - expect(edges[0].stroke).toBe('thick'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes and edges on dotted arrows', function () { - const res = flow.parser.parse('graph TD;\nA o-.-o B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_circle'); - expect(edges[0].text).toBe(''); - expect(edges[0].stroke).toBe('dotted'); - expect(edges[0].length).toBe(1); - }); - - it('should handle double edged nodes with text on dotted arrows', function () { - const res = flow.parser.parse('graph TD;\nA o-. text .-o B;'); - - const vert = flow.parser.yy.getVertices(); - const edges = flow.parser.yy.getEdges(); - - expect(vert['A'].id).toBe('A'); - expect(vert['B'].id).toBe('B'); - expect(edges.length).toBe(1); - expect(edges[0].start).toBe('A'); - expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_circle'); - expect(edges[0].text).toBe('text'); - expect(edges[0].stroke).toBe('dotted'); - expect(edges[0].length).toBe(1); - }); - - it.each(keywords)( - 'should handle dotted double edged nodes with %s as text', - function (keyword) { - const res = flow.parser.parse(`graph TD;\nA o-. ${keyword} .-o B;`); + describe('edges', function () { + doubleEndedEdges.forEach((edgeType) => { + it(`should handle ${edgeType.stroke} ${edgeType.type} with no text`, function () { + const res = flow.parser.parse(`graph TD;\nA ${edgeType.edgeStart}${edgeType.edgeEnd} B;`); const vert = flow.parser.yy.getVertices(); const edges = flow.parser.yy.getEdges(); @@ -366,12 +86,50 @@ describe('[Edges] when parsing', () => { expect(edges.length).toBe(1); expect(edges[0].start).toBe('A'); expect(edges[0].end).toBe('B'); - expect(edges[0].type).toBe('double_arrow_circle'); - expect(edges[0].text).toBe(`${keyword}`); - expect(edges[0].stroke).toBe('dotted'); - expect(edges[0].length).toBe(1); - } - ); + expect(edges[0].type).toBe(`${edgeType.type}`); + expect(edges[0].text).toBe(''); + expect(edges[0].stroke).toBe(`${edgeType.stroke}`); + }); + + it(`should handle ${edgeType.stroke} ${edgeType.type} with text`, function () { + const res = flow.parser.parse( + `graph TD;\nA ${edgeType.edgeStart} text ${edgeType.edgeEnd} B;` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].id).toBe('A'); + expect(vert['B'].id).toBe('B'); + expect(edges.length).toBe(1); + expect(edges[0].start).toBe('A'); + expect(edges[0].end).toBe('B'); + expect(edges[0].type).toBe(`${edgeType.type}`); + expect(edges[0].text).toBe('text'); + expect(edges[0].stroke).toBe(`${edgeType.stroke}`); + }); + + it.each(keywords)( + `should handle ${edgeType.stroke} ${edgeType.type} with %s text`, + function (keyword) { + const res = flow.parser.parse( + `graph TD;\nA ${edgeType.edgeStart} ${keyword} ${edgeType.edgeEnd} B;` + ); + + const vert = flow.parser.yy.getVertices(); + const edges = flow.parser.yy.getEdges(); + + expect(vert['A'].id).toBe('A'); + expect(vert['B'].id).toBe('B'); + expect(edges.length).toBe(1); + expect(edges[0].start).toBe('A'); + expect(edges[0].end).toBe('B'); + expect(edges[0].type).toBe(`${edgeType.type}`); + expect(edges[0].text).toBe(`${keyword}`); + expect(edges[0].stroke).toBe(`${edgeType.stroke}`); + } + ); + }); }); it('should handle multiple edges', function () { diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js index d5797933d..8489ca725 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js @@ -27,7 +27,7 @@ const keywords = [ 'subgraph', ]; -const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-']; +const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/']; describe('[Singlenodes] when parsing', () => { beforeEach(function () { From 5a165e4c1deecb5389742f59c8f3feb8a9bf5baf Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Thu, 3 Aug 2023 20:11:50 -0300 Subject: [PATCH 42/45] Revert to old docs concerning quotations marks in string --- docs/syntax/flowchart.md | 1 - packages/mermaid/src/docs/syntax/flowchart.md | 1 - 2 files changed, 2 deletions(-) diff --git a/docs/syntax/flowchart.md b/docs/syntax/flowchart.md index fb0b7f2e3..0440c9169 100644 --- a/docs/syntax/flowchart.md +++ b/docs/syntax/flowchart.md @@ -605,7 +605,6 @@ flowchart LR ### Entity codes to escape characters It is possible to escape characters using the syntax exemplified here. -For quotation marks, you can escape them with a backslash as well. ```mermaid-example flowchart LR diff --git a/packages/mermaid/src/docs/syntax/flowchart.md b/packages/mermaid/src/docs/syntax/flowchart.md index 960bf9755..56cad19a0 100644 --- a/packages/mermaid/src/docs/syntax/flowchart.md +++ b/packages/mermaid/src/docs/syntax/flowchart.md @@ -387,7 +387,6 @@ flowchart LR ### Entity codes to escape characters It is possible to escape characters using the syntax exemplified here. -For quotation marks, you can escape them with a backslash as well. ```mermaid-example flowchart LR From aaf333676cd3cb91bec4245421ca5b2451a1426c Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Thu, 3 Aug 2023 20:15:11 -0300 Subject: [PATCH 43/45] Add underscore to unit test on special Chars --- .../src/diagrams/flowchart/parser/flow-singlenode.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js index 8489ca725..59336d8d4 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow-singlenode.spec.js @@ -27,7 +27,7 @@ const keywords = [ 'subgraph', ]; -const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/']; +const specialChars = ['#', ':', '0', '&', ',', '*', '.', '\\', 'v', '-', '/', '_']; describe('[Singlenodes] when parsing', () => { beforeEach(function () { From 850513fa3daeba5259a2edb37b2b86633e2b7c75 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Thu, 3 Aug 2023 20:56:01 -0300 Subject: [PATCH 44/45] Return Unicode Text to idStringToken definition --- packages/mermaid/src/diagram.spec.ts | 2 +- packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagram.spec.ts b/packages/mermaid/src/diagram.spec.ts index ae93a121a..99ce4e2c6 100644 --- a/packages/mermaid/src/diagram.spec.ts +++ b/packages/mermaid/src/diagram.spec.ts @@ -49,7 +49,7 @@ describe('diagram detection', () => { "Parse error on line 2: graph TD; A--> --------------^ - Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', got 'EOF'" + Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'EOF'" `); await expect(getDiagramFromText('sequenceDiagram; A-->B')).rejects .toThrowErrorMatchingInlineSnapshot(` diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index 54d28b553..da5c3ef6f 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -572,7 +572,7 @@ style: styleComponent styleComponent: NUM | NODE_STRING| COLON | UNIT | SPACE | BRKT | STYLE | PCT ; /* Token lists */ -idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON | AMP | BRKT | MULT; +idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON | AMP | BRKT | MULT | UNICODE_TEXT; textToken : TEXT | TAGSTART | TAGEND | UNICODE_TEXT; From c9db0ee749264d50878781e96edf8b7012fd7e51 Mon Sep 17 00:00:00 2001 From: Ibrahim Wassouf Date: Thu, 3 Aug 2023 21:05:41 -0300 Subject: [PATCH 45/45] Add specialChars in textNoTagsToken, alphaNumToken This will ensure that alphaNumToken does not lose any of the previously used tokens in its definition. The same tokens were added to textNoTagsToken explicitly, because it used to have alphaNumToken in its definition before I removed it. Previously, textNoTagsToken and alphaNumToken had many tokens in common in their definition. To avoid grammar conflicts, the alphaNumStatement grammar was created. However, I found this unintuitive and was an extra step just to avoid repetition in the definitions. I opted to have repetition in the definitions of textNoTagsToken and alphaNumToken and it be explicitly clear right away, rather than have extra grammar statements like alphaNumStatement which don't look like they do anything at first glance --- packages/mermaid/src/diagrams/flowchart/parser/flow.jison | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison index da5c3ef6f..8d746f808 100644 --- a/packages/mermaid/src/diagrams/flowchart/parser/flow.jison +++ b/packages/mermaid/src/diagrams/flowchart/parser/flow.jison @@ -576,11 +576,11 @@ idStringToken : NUM | NODE_STRING | DOWN | MINUS | DEFAULT | COMMA | COLON | A textToken : TEXT | TAGSTART | TAGEND | UNICODE_TEXT; -textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | keywords | START_LINK ; +textNoTagsToken: NUM | NODE_STRING | SPACE | MINUS | AMP | UNICODE_TEXT | COLON | MULT | BRKT | keywords | START_LINK ; edgeTextToken : EDGE_TEXT | UNICODE_TEXT ; -alphaNumToken : NUM | UNICODE_TEXT | NODE_STRING | DIR | DOWN | MINUS | COMMA; +alphaNumToken : NUM | UNICODE_TEXT | NODE_STRING | DIR | DOWN | MINUS | COMMA | COLON | AMP | BRKT | MULT; idString :idStringToken