mermaid-js / mermaid

Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown
https://mermaid.js.org
MIT License
71.02k stars 6.38k forks source link

nested state grouping works improperly #3700

Open xenoterracide opened 1 year ago

xenoterracide commented 1 year ago
stateDiagram-v2
  [*] --> Unknown
  [*] --> Safe
  Unknown --> [*]
  Unknown --> Init
  Unknown --> Fault
  Unknown --> Manual
  Unknown --> Idle
  Unknown --> EStop
  Init --> Manual
  Init --> Idle: Initialize
  Manual --> Idle: Initialize
  Idle --> Manual
  Fault --> DoubleFault: Throw Fault
  Fault --> Idle
  DoubleFault --> [*]

  state DeNester {
    Idle --> IdleAtCC
    IdleAtC --> Obstructed
    Obstructed --> Fault
  }

  state EArm {
    Idle --> GoToProductWait
    GoToProductWait
  }

The grouping here seems all off. Idle and Fault should both be in the outer context.

Screen Shot 2022-10-19 at 4 41 06 PM
SpacyRicochet commented 1 year ago

I came across the same issue. It looks like if you add an edge inside a subgraph, both of the connected nodes are added/inserted into the subgraph, even if the node itself was declared outside of the subgraph.

Here is a simple example of the issue;

flowchart TD
A
subgraph S1
B
end
B --> A

C
subgraph S2
D
D --> C
end

E
subgraph S3
F
E --> F
end

mermaid-diagram-2022-10-26-123118

weedySeaDragon commented 1 year ago

This is a minimal state diagram that shows the problem:

stateDiagram-v2
   [*] --> A
   A --> b1
   state B {
    b1  --> C
   }
   C --> [*]

Link to the above in Mermaid Live online editor

Interestingly, adding a note to C changes the layout. Here is a diagram with 3 examples. Example #2 is the same as Example #1 except it has a note attached.

stateDiagram-v2
    A : Example 1
   A --> b1
   note right of B1 
      C is first encountered \nwithin state B1\nand so is assumed\n to be within it.
    end note
    B1 : Composite B #1
   state B1{
    b1  --> C
   }
   C --> D
   note right of D 
     The relationship 'C --> D'\nis outside (after) state B1\n so D is interprested as outside of B.
   end note

    A2 : Example 2\n(note attached)
    A2 --> b1_2

    B2 : Composite B #2\n(same as B1)
    state B2 {
      b1_2--> C2 
    }
    C2 --> D2
    note left of C2 
      This is the same as\nExample 1 except\nC2 has a note attached.\nThis changes how C2\nis interpreted.\nHere is causes C2 to \nbe placed outside of\n Composite B #2!
    end note

    A3 : Example 3\nrelationship after\nComposite B #3 ends
    A3 --> b1_3
    B3 : Composite B #3
    state B3 {
      b1_3
    }
    b1_3 --> C3 
    C3 --> D3
    note left of C3 
     This works\nwhen the relationship\nfrom b1_3 to C3\nis put after the close\nof Composite B #3
    end note

Link to the about in the Mermaid Live online editor

weedySeaDragon commented 1 year ago

I can work on this. (It is related to being able to define and/or a classDef within a nested composite (child state section).

weedySeaDragon commented 1 year ago

@SpacyRicochet Your example is for a flowchart. Although the error is happening in both, the code is different (there is no reason you would necessarily know that). It would be best if you copied your example and opened a new bug report specifically for flowcharts.

jmccabe commented 4 days ago

Interestingly, adding a note to C changes the layout. Here is a diagram with 3 examples. Example #2 is the same as Example #1 except it has a note attached.

With reference to this example, I wondered what would happen if the whole lot was enclosed in another outer state, so tried this....

stateDiagram-v2
    A : Example 1
    [*] --> Outer
    state Outer {
        A --> b1
        note right of B1 
            C is first encountered \nwithin state B1\nand so is assumed\n to be within it.
        end note
        B1 : Composite B #1
        state B1{
            b1  --> C
        }
        C --> D
        note right of D 
            The relationship 'C --> D'\nis outside (after) state B1\n so D is interprested as outside of B.
        end note

        A2 : Example 2\n(note attached)
        A2 --> b1_2

        B2 : Composite B #2\n(same as B1)
        state B2 {
            b1_2--> C2 
        }
        C2 --> D2
        note left of C2 
            This is the same as\nExample 1 except\nC2 has a note attached.\nThis changes how C2\nis interpreted.\nHere is causes C2 to \nbe placed outside of\n Composite B #2!
        end note

        A3 : Example 3\nrelationship after\nComposite B #3 ends
        A3 --> b1_3
        B3 : Composite B #3
        state B3 {
            b1_3
        }
        b1_3 --> C3 
        C3 --> D3
        note left of C3 
            This works\nwhen the relationship\nfrom b1_3 to C3\nis put after the close\nof Composite B #3
        end note
    }

But, on the live editor (and here, it would seem!), no diagram is shown - the error is displayed here so it might be that there is just something wrong (as I'm new to Mermaid), but I'm at a bit of a loss as to why just wrapping all that in another outer state would totally break it.

Moving the closing brace of state Outer to just above the b1_3 --> C3 transition does show something, but obviously isn't what I'd want it to look like.

stateDiagram-v2
    A : Example 1
    [*] --> Outer
    state Outer {
        A --> b1
        note right of B1 
            C is first encountered \nwithin state B1\nand so is assumed\n to be within it.
        end note
        B1 : Composite B #1
        state B1{
            b1  --> C
        }
        C --> D
        note right of D 
            The relationship 'C --> D'\nis outside (after) state B1\n so D is interprested as outside of B.
        end note

        A2 : Example 2\n(note attached)
        A2 --> b1_2

        B2 : Composite B #2\n(same as B1)
        state B2 {
            b1_2--> C2 
        }
        C2 --> D2
        note left of C2 
            This is the same as\nExample 1 except\nC2 has a note attached.\nThis changes how C2\nis interpreted.\nHere is causes C2 to \nbe placed outside of\n Composite B #2!
        end note

        A3 : Example 3\nrelationship after\nComposite B #3 ends
        A3 --> b1_3
        B3 : Composite B #3
        state B3 {
            b1_3
        }
    }
        b1_3 --> C3 
        C3 --> D3
        note left of C3 
            This works\nwhen the relationship\nfrom b1_3 to C3\nis put after the close\nof Composite B #3
        end note

This is the sort of thing I've tried; it's a slightly more complex state diagram, but..

stateDiagram-v2
    [*] --> A
    state A {
        [*] --> B
        state check <<choice>>

        B --> check : B Result
        check --> C : success
        check --> D : failure

        C --> E : Ev1

        state F { 
            [*] --> G
            G --> I : Ev2
            I --> G : Ev3
            G --> H : Ev4
            H --> G
            I --> J : success
            J --> H : Ev4
            G --> E : Ev1
        }
    }

Basically, state E, which is used quite early on, is an inner state of A but is being shown as an inner state of F because of the G --> E : Ev1 declared inside state F. If I move that line outside state F, to the state A level...:

stateDiagram-v2
    [*] --> A
    state A {
        [*] --> B
        state check <<choice>>

        B --> check : B Result
        check --> C : success
        check --> D : failure

        C --> E : Ev1

        state F { 
            [*] --> G
            G --> I : Ev2
            I --> G : Ev3
            G --> H : Ev4
            H --> G
            I --> J : success
            J --> H : Ev4
        }
        G --> E : Ev1
    }

E moves to the right level, but it takes G with it.

Maybe I'm doing something wrong, but...