Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/mermaid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,5 @@ Mermaid blocks remain syntax-highlighted code blocks.
| Concurrent states | ❌ | ❌ | |
| State notes | ❌ | ❌ | |
| Choice, fork, and join pseudostates | ❌ | ❌ | |
| State direction declarations | ❌ | ❌ | |
| State direction declarations | ✅ | ✅ | `TD`, `TB`, `BT`, `LR`, and `RL` are supported |
| Styling and classes | ❌ | ❌ | |
16 changes: 16 additions & 0 deletions pkg/mermaid/parser_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ func parseState(doc *Document, statements []string) {
pseudoState := 0
for _, statement := range statements {
cursor := mermaidCursor{input: statement}
if cursor.consumeKeyword("direction") {
direction, ok := cursor.readIdentifier()
if ok && cursor.eof() && validMermaidDirection(direction) {
doc.Direction = strings.ToUpper(direction)
}
continue
}
if cursor.consumeKeyword("state") {
label, ok := cursor.readQuoted()
if !ok || !cursor.consumeKeyword("as") {
Expand Down Expand Up @@ -51,6 +58,15 @@ func parseState(doc *Document, statements []string) {
}
}

func validMermaidDirection(direction string) bool {
switch strings.ToUpper(direction) {
case "TD", "TB", "BT", "LR", "RL":
return true
default:
return false
}
}

func mermaidPseudoStateID(kind string, index int) string {
return "__" + kind + "_" + strings.Repeat("_", index)
}
23 changes: 23 additions & 0 deletions pkg/mermaid/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,29 @@ Working --> [*]: complete`)
assert.Equal(t, "End", doc.Nodes[doc.Edges[2].To].Label)
}

func TestParseStateDiagramDirection(t *testing.T) {
t.Parallel()

tests := []struct {
direction string
want string
}{
{direction: "LR", want: "LR"},
{direction: "rl", want: "RL"},
{direction: "TB", want: "TB"},
{direction: "BT", want: "BT"},
}
for _, tt := range tests {
t.Run(tt.direction, func(t *testing.T) {
t.Parallel()

doc, err := Parse("stateDiagram-v2\ndirection " + tt.direction + "\n[*] --> Idle\nIdle --> [*]")
require.NoError(t, err)
assert.Equal(t, tt.want, doc.Direction)
})
}
}

func TestSplitMermaidStatementsHonorsQuotedAndDelimitedSemicolons(t *testing.T) {
t.Parallel()

Expand Down
2 changes: 1 addition & 1 deletion pkg/tui/components/mermaid/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func Render(source string, width int) (string, bool) {
case mermaidparser.DiagramSequence:
return drawMermaidSequence(document.Edges, document.SequenceEvents, document.Participants, labels, width), true
case mermaidparser.DiagramState:
return drawMermaidGraph(document.Edges, document.NodeOrder, labels, width), true
return drawMermaidFlowchart(document.Edges, document.NodeOrder, labels, document.Direction, width), true
default:
return "", false
}
Expand Down
50 changes: 50 additions & 0 deletions pkg/tui/components/mermaid/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,56 @@ Note over A,B: Waiting for work
Note right of B: After`,
width: 60,
},
{
name: "state top down direction",
source: `stateDiagram-v2
state "Waiting for work" as Waiting
direction TD
[*] --> Waiting
Waiting --> Running: start
Running --> [*]: finish`,
width: 80,
},
{
name: "state top bottom direction",
source: `stateDiagram-v2
state "Waiting for work" as Waiting
direction TB
[*] --> Waiting
Waiting --> Running: start
Running --> [*]: finish`,
width: 80,
},
{
name: "state bottom top direction",
source: `stateDiagram-v2
state "Waiting for work" as Waiting
direction BT
[*] --> Waiting
Waiting --> Running: start
Running --> [*]: finish`,
width: 80,
},
{
name: "state left right direction",
source: `stateDiagram-v2
state "Waiting for work" as Waiting
direction LR
[*] --> Waiting
Waiting --> Running: start
Running --> [*]: finish`,
width: 80,
},
{
name: "state right left direction",
source: `stateDiagram-v2
state "Waiting for work" as Waiting
direction RL
[*] --> Waiting
Waiting --> Running: start
Running --> [*]: finish`,
width: 80,
},
{
name: "state lifecycle and cycle",
source: `stateDiagram-v2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
╭──────╮
│ End │
╰──────╯
▲
│ finish
╭────┴────╮
│ Running │
╰─────────╯
▲
│ start
╭─────────┴────────╮
│ Waiting for work │
╰──────────────────╯
▲
│
╭───┴───╮
│ Start │
╰───────╯
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
╭───────╮ ╭──────────────────╮ ╭─────────╮ ╭──────╮
│ Start ├────▶│ Waiting for work ├──start──▶│ Running ├──finish──▶│ End │
╰───────╯ ╰──────────────────╯ ╰─────────╯ ╰──────╯
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
╭──────╮ ╭─────────╮ ╭──────────────────╮ ╭───────╮
│ End │◀──finish──┤ Running │◀──start──┤ Waiting for work │◀────┤ Start │
╰──────╯ ╰─────────╯ ╰──────────────────╯ ╰───────╯
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
╭───────╮
│ Start │
╰───┬───╯
│
▼
╭──────────────────╮
│ Waiting for work │
╰─────────┬────────╯
│ start
▼
╭─────────╮
│ Running │
╰────┬────╯
│ finish
▼
╭──────╮
│ End │
╰──────╯
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
╭───────╮
│ Start │
╰───┬───╯
│
▼
╭──────────────────╮
│ Waiting for work │
╰─────────┬────────╯
│ start
▼
╭─────────╮
│ Running │
╰────┬────╯
│ finish
▼
╭──────╮
│ End │
╰──────╯
Loading