@@ -16,7 +16,7 @@ const (
1616
1717var (
1818 // participantRegex matches participant declarations: participant [ID] [as Label]
19- participantRegex = regexp .MustCompile (`^\s*participant\s+(?:"([^"]+)"|(\S+))(?:\s+as\s+(.+))?$` )
19+ participantRegex = regexp .MustCompile (`(?i) ^\s*participant\s+(?:"([^"]+)"|(\S+))(?:\s+as\s+(.+))?$` )
2020
2121 // messageRegex matches messages: [From][arrow][To]: [Label]. The arrow is one
2222 // of ->>, -->>, -> or -->. Unquoted participant names exclude the arrow
@@ -26,24 +26,24 @@ var (
2626 messageRegex = regexp .MustCompile (`^\s*(?:"([^"]+)"|([^\s<>-]+))\s*(-->>|-->|->>|->)\s*(?:"([^"]+)"|([^\s<>-]+))\s*:\s*(.*)$` )
2727
2828 // autonumberRegex matches the autonumber directive
29- autonumberRegex = regexp .MustCompile (`^\s*autonumber\s*$` )
29+ autonumberRegex = regexp .MustCompile (`(?i) ^\s*autonumber\s*$` )
3030
3131 // fragmentStartRegex matches the opening line of a control-flow fragment,
3232 // e.g. "loop every minute", "opt is premium", "alt is valid". Group 1 is the
3333 // keyword, group 2 is the (optional) label describing the condition.
34- fragmentStartRegex = regexp .MustCompile (`^\s*(loop|opt|alt)\b\s*(.*)$` )
34+ fragmentStartRegex = regexp .MustCompile (`(?i) ^\s*(loop|opt|alt)\b\s*(.*)$` )
3535
3636 // fragmentElseRegex matches an "else" divider inside an alt block. Group 1 is
3737 // the (optional) condition label for the following section.
38- fragmentElseRegex = regexp .MustCompile (`^\s*else\b\s*(.*)$` )
38+ fragmentElseRegex = regexp .MustCompile (`(?i) ^\s*else\b\s*(.*)$` )
3939
4040 // fragmentEndRegex matches the "end" line that closes a fragment.
41- fragmentEndRegex = regexp .MustCompile (`^\s*end\s*$` )
41+ fragmentEndRegex = regexp .MustCompile (`(?i) ^\s*end\s*$` )
4242
4343 // noteRegex matches note annotations: "Note over A: text", "note left of A:
4444 // text", "Note over A,B: text" (case-insensitive keyword). Group 1 is the
4545 // placement, group 2 the participant list, group 3 the text.
46- noteRegex = regexp .MustCompile (`^\s*[Nn]ote \s+(right of|left of|over)\s+([^:]+?)\s*:\s*(.*)$` )
46+ noteRegex = regexp .MustCompile (`(?i) ^\s*note \s+(right of|left of|over)\s+([^:]+?)\s*:\s*(.*)$` )
4747)
4848
4949// SequenceDiagram represents a parsed sequence diagram.
@@ -202,11 +202,25 @@ func IsSequenceDiagram(input string) bool {
202202 if trimmed == "" || strings .HasPrefix (trimmed , "%%" ) {
203203 continue
204204 }
205- return strings . HasPrefix (trimmed , SequenceDiagramKeyword )
205+ return hasSequenceKeyword (trimmed )
206206 }
207207 return false
208208}
209209
210+ // hasSequenceKeyword reports whether a line is the sequenceDiagram declaration,
211+ // case-insensitively (mermaid's sequence grammar is case-insensitive). The
212+ // keyword must stand as a whole token — followed by whitespace or end of line —
213+ // so a node id like "sequenceDiagramFoo" in a flowchart isn't misrouted here.
214+ func hasSequenceKeyword (line string ) bool {
215+ lower := strings .ToLower (strings .TrimSpace (line ))
216+ kw := strings .ToLower (SequenceDiagramKeyword )
217+ if ! strings .HasPrefix (lower , kw ) {
218+ return false
219+ }
220+ rest := lower [len (kw ):]
221+ return rest == "" || rest [0 ] == ' ' || rest [0 ] == '\t'
222+ }
223+
210224func Parse (input string ) (* SequenceDiagram , error ) {
211225 input = strings .TrimSpace (input )
212226 if input == "" {
@@ -219,7 +233,7 @@ func Parse(input string) (*SequenceDiagram, error) {
219233 return nil , fmt .Errorf ("no content found" )
220234 }
221235
222- if ! strings . HasPrefix (strings .TrimSpace (lines [0 ]), SequenceDiagramKeyword ) {
236+ if ! hasSequenceKeyword (strings .TrimSpace (lines [0 ])) {
223237 return nil , fmt .Errorf ("expected %q keyword" , SequenceDiagramKeyword )
224238 }
225239 lines = lines [1 :]
@@ -252,7 +266,7 @@ func Parse(input string) (*SequenceDiagram, error) {
252266 // "Note->>B: hi") still parses as a message further down.
253267 if m := noteRegex .FindStringSubmatch (trimmed ); m != nil {
254268 placement := NoteOver
255- switch m [1 ] {
269+ switch strings . ToLower ( m [1 ]) { // keyword may be any case
256270 case "left of" :
257271 placement = NoteLeftOf
258272 case "right of" :
@@ -272,7 +286,7 @@ func Parse(input string) (*SequenceDiagram, error) {
272286 // wrapping is irrelevant for single-line ASCII, so just strip it.
273287 text := strings .TrimSpace (m [3 ])
274288 for _ , pre := range []string {"nowrap:" , "wrap:" } {
275- if strings .HasPrefix (text , pre ) {
289+ if strings .HasPrefix (strings . ToLower ( text ) , pre ) {
276290 text = strings .TrimSpace (text [len (pre ):])
277291 break
278292 }
@@ -301,7 +315,7 @@ func Parse(input string) (*SequenceDiagram, error) {
301315
302316 // A fragment opener ("loop"/"opt"/"alt") starts a framed block.
303317 if match := fragmentStartRegex .FindStringSubmatch (trimmed ); match != nil {
304- fType := map [string ]FragmentType {"loop" : FragmentLoop , "opt" : FragmentOpt , "alt" : FragmentAlt }[match [1 ]]
318+ fType := map [string ]FragmentType {"loop" : FragmentLoop , "opt" : FragmentOpt , "alt" : FragmentAlt }[strings . ToLower ( match [1 ]) ]
305319 sd .Events = append (sd .Events , Event {
306320 Kind : EventFragmentStart ,
307321 Fragment : & Fragment {Type : fType , Label : strings .TrimSpace (match [2 ])},
0 commit comments