@@ -363,21 +363,34 @@ fn parse_flowchart(input: &str) -> Result<ParseOutput> {
363363 Ok ( ParseOutput { graph, init_config } )
364364}
365365
366+ /// Split on `&` that is outside brackets, parens, braces, and quotes.
367+ fn split_ampersand_aware ( input : & str ) -> Vec < & str > {
368+ let masked = mask_bracket_content ( input) ;
369+ let mut parts = Vec :: new ( ) ;
370+ let mut start = 0 ;
371+ for ( i, ch) in masked. char_indices ( ) {
372+ if ch == '&' {
373+ let part = input[ start..i] . trim ( ) ;
374+ if !part. is_empty ( ) {
375+ parts. push ( part) ;
376+ }
377+ start = i + 1 ;
378+ }
379+ }
380+ let last = input[ start..] . trim ( ) ;
381+ if !last. is_empty ( ) {
382+ parts. push ( last) ;
383+ }
384+ parts
385+ }
386+
366387fn add_flowchart_edge ( line : & str , graph : & mut Graph , subgraph_stack : & [ usize ] ) -> bool {
367388 let Some ( ( left, label, right, edge_meta) ) = parse_edge_line ( line) else {
368389 return false ;
369390 } ;
370391
371- let sources: Vec < & str > = left
372- . split ( '&' )
373- . map ( |part| part. trim ( ) )
374- . filter ( |part| !part. is_empty ( ) )
375- . collect ( ) ;
376- let targets: Vec < & str > = right
377- . split ( '&' )
378- . map ( |part| part. trim ( ) )
379- . filter ( |part| !part. is_empty ( ) )
380- . collect ( ) ;
392+ let sources = split_ampersand_aware ( & left) ;
393+ let targets = split_ampersand_aware ( & right) ;
381394
382395 let mut source_ids = Vec :: new ( ) ;
383396 for source in sources {
@@ -3589,16 +3602,8 @@ fn parse_block_diagram(input: &str) -> Result<ParseOutput> {
35893602 continue ;
35903603 }
35913604 if let Some ( ( left, label, right, edge_meta) ) = parse_edge_line ( line) {
3592- let sources: Vec < & str > = left
3593- . split ( '&' )
3594- . map ( |part| part. trim ( ) )
3595- . filter ( |part| !part. is_empty ( ) )
3596- . collect ( ) ;
3597- let targets: Vec < & str > = right
3598- . split ( '&' )
3599- . map ( |part| part. trim ( ) )
3600- . filter ( |part| !part. is_empty ( ) )
3601- . collect ( ) ;
3605+ let sources = split_ampersand_aware ( & left) ;
3606+ let targets = split_ampersand_aware ( & right) ;
36023607
36033608 for source in & sources {
36043609 let ( source_id, source_label, source_shape, source_classes) =
@@ -6054,6 +6059,46 @@ mod tests {
60546059 assert ! ( parsed. graph. nodes. contains_key( "C" ) ) ;
60556060 }
60566061
6062+ #[ test]
6063+ fn parse_ampersand_in_source_label ( ) {
6064+ let input = r#"flowchart LR
6065+ A["Agent reads artifacts & computes deps"] --> B"# ;
6066+ let parsed = parse_mermaid ( input) . unwrap ( ) ;
6067+ assert_eq ! ( parsed. graph. edges. len( ) , 1 ) ;
6068+ assert_eq ! ( parsed. graph. nodes. len( ) , 2 ) ;
6069+ assert ! ( parsed. graph. nodes. contains_key( "A" ) ) ;
6070+ assert ! ( parsed. graph. nodes. contains_key( "B" ) ) ;
6071+ assert_eq ! (
6072+ parsed. graph. nodes[ "A" ] . label,
6073+ "Agent reads artifacts & computes deps"
6074+ ) ;
6075+ }
6076+
6077+ #[ test]
6078+ fn parse_ampersand_in_target_label ( ) {
6079+ let input = r#"flowchart LR
6080+ A --> B["List & select changes"]"# ;
6081+ let parsed = parse_mermaid ( input) . unwrap ( ) ;
6082+ assert_eq ! ( parsed. graph. edges. len( ) , 1 ) ;
6083+ assert_eq ! ( parsed. graph. nodes. len( ) , 2 ) ;
6084+ assert ! ( parsed. graph. nodes. contains_key( "A" ) ) ;
6085+ assert ! ( parsed. graph. nodes. contains_key( "B" ) ) ;
6086+ assert_eq ! ( parsed. graph. nodes[ "B" ] . label, "List & select changes" ) ;
6087+ }
6088+
6089+ #[ test]
6090+ fn parse_parallel_ampersand_with_label_ampersand ( ) {
6091+ let input = r#"flowchart LR
6092+ A["foo & bar"] & B --> C"# ;
6093+ let parsed = parse_mermaid ( input) . unwrap ( ) ;
6094+ assert_eq ! ( parsed. graph. edges. len( ) , 2 ) ;
6095+ assert_eq ! ( parsed. graph. nodes. len( ) , 3 ) ;
6096+ assert ! ( parsed. graph. nodes. contains_key( "A" ) ) ;
6097+ assert ! ( parsed. graph. nodes. contains_key( "B" ) ) ;
6098+ assert ! ( parsed. graph. nodes. contains_key( "C" ) ) ;
6099+ assert_eq ! ( parsed. graph. nodes[ "A" ] . label, "foo & bar" ) ;
6100+ }
6101+
60576102 #[ test]
60586103 fn parse_subgraph_style ( ) {
60596104 let input = "flowchart LR\n classDef hot fill:#f00,stroke:#0f0\n subgraph SG[Group]:::hot\n A --> B\n end\n class SG hot\n style SG fill:#faf,stroke:#111" ;
0 commit comments