9 Commits

20 changed files with 2301 additions and 25 deletions

View File

@ -23,6 +23,6 @@
- [ ] PlantUML parser - [ ] PlantUML parser
- [ ] (Message numbering) - [ ] (Message numbering)
- [ ] Mainframes - [ ] Mainframes
- [ ] Different types of groups (alt/loop/etc.) - [x] Different types of groups (alt/loop/etc.)
- [ ] Delays - [ ] Delays
- [ ] Auto-fit in parent - [ ] Auto-fit in parent

View File

@ -63,6 +63,45 @@ _grp("Group 1", desc: "Description", {
}) })
```) ```)
#let alt = example(```
_par("a", display-name: "Alice")
_par("b", display-name: "Bob")
_alt(
"first encounter", {
_seq("a", "b", comment: "Who are you ?")
_seq("b", "a", comment: "I'm Bob")
},
"know eachother", {
_seq("a", "b", comment: "Hello Bob")
_seq("b", "a", comment: "Hello Alice")
},
"best friends", {
_seq("a", "b", comment: "Hi !")
_seq("b", "a", comment: "Hi !")
}
)
```)
#let loop = example(```
_par("a", display-name: "Alice")
_par("b", display-name: "Bob")
_loop("default loop", {
_seq("a", "b", comment: "Are you here ?")
})
_gap()
_loop("min loop", min: 1, {
_seq("a", "b", comment: "Are you here ?")
})
_gap()
_loop("min-max loop", min: 1, max: 5, {
_seq("a", "b", comment: "Are you still here ?")
})
```)
#let sync = example(``` #let sync = example(```
_par("alice", display-name: "Alice") _par("alice", display-name: "Alice")
_par("bob", display-name: "Bob") _par("bob", display-name: "Bob")

View File

@ -2,7 +2,7 @@
/// #examples.grp /// #examples.grp
/// - name (content): The group's name /// - name (content): The group's name
/// - desc (none, content): Optional description /// - desc (none, content): Optional description
/// - type (str): The groups's type (unused for the moment) /// - type (str): The groups's type (should only be set through other functions like @@_alt() or @@_loop() )
/// - elmts (array): Elements inside the group (can be sequences, other groups, notes, etc.) /// - elmts (array): Elements inside the group (can be sequences, other groups, notes, etc.)
#let _grp( #let _grp(
name, name,
@ -11,9 +11,48 @@
elmts elmts
) = {} ) = {}
/// Synchronizes multiple sequences /// Creates an alt-else group of sequences
///
/// It contains at least one section but can have as many as needed
/// #examples.alt
/// - desc (content): The alt's label
/// - elmts (array): Elements inside the alt's first section
/// - ..args (content, array): Complementary "else" sections.\ You can add as many else sections as you need by passing a content (else section label) followed by an array of elements (see example)
#let _alt(
desc,
elmts,
..args
)
/// Creates a looped group of sequences
/// #examples.loop
/// - desc (content): Loop description
/// - min (none, number): Optional lower bound of the loop
/// - max (auto, number): Upper bound of the loop. If left as `auto` and `min` is set, it will be infinity (`'*'`)
/// - elmts (array): Elements inside the group
#let _loop(
desc,
min: none,
max: auto,
elmts
) = {}
/// Synchronizes multiple sequences\
/// All elements inside a synchronized group will start at the same time
/// #examples.sync /// #examples.sync
/// - elmts (array): Synchronized elements (generally sequences or notes) /// - elmts (array): Synchronized elements (generally sequences or notes)
#let _sync( #let _sync(
elmts elmts
) )
/// Creates an optional group\
/// This is a simple wrapper around @@_grp()
/// - desc (content): Group description
/// - elmts (array): Elements inside the group
#let _opt(desc, elmts) = {}
/// Creates a break group\
/// This is a simple wrapper around @@_grp()
/// - desc (content): Group description
/// - elmts (array): Elements inside the group
#let _break(desc, elmts) = {}

BIN
gallery/doc_examples.pdf Normal file

Binary file not shown.

689
gallery/doc_examples.typ Normal file
View File

@ -0,0 +1,689 @@
#import "../src/lib.typ": from-plantuml
#set page(width: auto, height: auto)
#let examples = (
(
[Basic Examples],
```
@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
Alice -> Bob: Another authentication Request
Alice <-- Bob: Another authentication Response
@enduml
```
),
(
[Declaring participant],
```
@startuml
participant Participant as Foo
actor Actor as Foo1
boundary Boundary as Foo2
control Control as Foo3
entity Entity as Foo4
database Database as Foo5
collections Collections as Foo6
queue Queue as Foo7
Foo -> Foo1 : To actor
Foo -> Foo2 : To boundary
Foo -> Foo3 : To control
Foo -> Foo4 : To entity
Foo -> Foo5 : To database
Foo -> Foo6 : To collections
Foo -> Foo7: To queue
@enduml
```
),
(
[Declaring participant (2)],
```
@startuml
actor Bob #red
' The only difference between actor
'and participant is the drawing
participant Alice
participant "I have a really\nlong name" as L #99FF99
/' You can also declare:
participant L as "I have a really\nlong name" #99FF99
'/
Alice->Bob: Authentication Request
Bob->Alice: Authentication Response
Bob->L: Log transaction
@enduml
```
),
(
[Use non-letters in participants],
```
@startuml
Alice -> "Bob()" : Hello
"Bob()" -> "This is very\nlong" as Long
' You can also declare:
' "Bob()" -> Long as "This is very\nlong"
Long --> "Bob()" : ok
@enduml
```
),
(
[Message to Self],
```
@startuml
Alice -> Alice: This is a signal to self.\nIt also demonstrates\nmultiline \ntext
@enduml
```
),
(
[Message to Self (2)],
```
@startuml
Alice <- Alice: This is a signal to self.\nIt also demonstrates\nmultiline \ntext
@enduml
```
),
(
[Change arrow style],
```
@startuml
Bob ->x Alice
Bob -> Alice
Bob ->> Alice
Bob -\ Alice
Bob \\- Alice
Bob //-- Alice
Bob ->o Alice
Bob o\\-- Alice
Bob <-> Alice
Bob <->o Alice
@enduml
```
),
(
[Grouping message],
```
@startuml
Alice -> Bob: Authentication Request
alt successful case
Bob -> Alice: Authentication Accepted
else some kind of failure
Bob -> Alice: Authentication Failure
group My own label
Alice -> Log : Log attack start
loop 1000 times
Alice -> Bob: DNS Attack
end
Alice -> Log : Log attack end
end
else Another type of failure
Bob -> Alice: Please repeat
end
@enduml
```
),
(
[Secondary group label],
```
@startuml
Alice -> Bob: Authentication Request
Bob -> Alice: Authentication Failure
group My own label [My own label 2]
Alice -> Log : Log attack start
loop 1000 times
Alice -> Bob: DNS Attack
end
Alice -> Log : Log attack end
end
@enduml
```
),
(
[Notes on messages],
```
@startuml
Alice->Bob : hello
note left: this is a first note
Bob->Alice : ok
note right: this is another note
Bob->Bob : I am thinking
note left
a note
can also be defined
on several lines
end note
@enduml
```
),
(
[Some other notes],
```
@startuml
participant Alice
participant Bob
note left of Alice #aqua
This is displayed
left of Alice.
end note
note right of Alice: This is displayed right of Alice.
note over Alice: This is displayed over Alice.
note over Alice, Bob #FFAAAA: This is displayed\n over Bob and Alice.
note over Bob, Alice
This is yet another
example of
a long note.
end note
@enduml
```
),
(
[Changing notes shape \[hnote, rnote\]],
```
@startuml
caller -> server : conReq
hnote over caller : idle
caller <- server : conConf
rnote over server
"r" as rectangle
"h" as hexagon
endrnote
rnote over server
this is
on several
lines
endrnote
hnote over caller
this is
on several
lines
endhnote
@enduml
```
),
(
[Note over all participants \[across\]],
```
@startuml
Alice->Bob:m1
Bob->Charlie:m2
note over Alice, Charlie: Old method for note over all part. with:\n ""note over //FirstPart, LastPart//"".
note across: New method with:\n""note across""
Bob->Alice
hnote across:Note across all part.
@enduml
```
),
(
[Several notes aligned at the same level \[/\]],
```
@startuml
note over Alice : initial state of Alice
note over Bob : initial state of Bob
Bob -> Alice : hello
@enduml
```
),
(
[Several notes aligned at the same level \[/\] (2)],
```
@startuml
note over Alice : initial state of Alice
/ note over Bob : initial state of Bob
Bob -> Alice : hello
@enduml
```
),
(
[Divider or separator],
```
@startuml
== Initialization ==
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
== Repetition ==
Alice -> Bob: Another authentication Request
Alice <-- Bob: another authentication Response
@enduml
```
),
(
[Delay],
```
@startuml
Alice -> Bob: Authentication Request
...
Bob --> Alice: Authentication Response
...5 minutes later...
Bob --> Alice: Good Bye !
@enduml
```
),
(
[Space],
```
@startuml
Alice -> Bob: message 1
Bob --> Alice: ok
|||
Alice -> Bob: message 2
Bob --> Alice: ok
||45||
Alice -> Bob: message 3
Bob --> Alice: ok
@enduml
```
),
(
[Lifeline Activation and Destruction],
```
@startuml
participant User
User -> A: DoWork
activate A
A -> B: << createRequest >>
activate B
B -> C: DoWork
activate C
C --> B: WorkDone
destroy C
B --> A: RequestCreated
deactivate B
A -> User: Done
deactivate A
@enduml
```
),
(
[Lifeline Activation and Destruction (2)],
```
@startuml
participant User
User -> A: DoWork
activate A #FFBBBB
A -> A: Internal call
activate A #DarkSalmon
A -> B: << createRequest >>
activate B
B --> A: RequestCreated
deactivate B
deactivate A
A -> User: Done
deactivate A
@enduml
```
),
/*(
[Lifeline Activation and Destruction (3)],
```
@startuml
'autoactivate on
alice -> bob : hello
bob -> bob : self call
bill -> bob /'#005500'/ : hello from thread 2
bob -> george ** : create
return done in thread 2
return rc
bob -> george !! : delete
return success
@enduml
```
),*/
(
[Return],
```
@startuml
Bob -> Alice : hello
activate Alice
Alice -> Alice : some action
return bye
@enduml
```
),
(
[Participant creation],
```
@startuml
Bob -> Alice : hello
create Other
Alice -> Other : new
create /'control'/ String
Alice -> String
note right : You can also put notes!
Alice --> Bob : ok
@enduml
```
),
(
[Shortcut syntax for activation, deactivation, creation],
```
@startuml
alice -> bob ++ : hello
bob -> bob ++ : self call
bob -> bib ++ /' #005500'/ : hello
bob -> george ** : create
return done
return rc
bob -> george !! : delete
return success
@enduml
```
),
(
[Shortcut syntax for activation, deactivation, creation (2)],
```
@startuml
alice -> bob ++ : hello1
bob -> charlie --++ : hello2
charlie --> alice -- : ok
@enduml
```
),
(
[Shortcut syntax for activation, deactivation, creation (3)],
```
@startuml
alice -> bob ++ /'#gold'/: hello
bob -> alice --++ /'#gold'/: you too
alice -> bob --: step1
alice -> bob : step2
@enduml
@enduml
```
),
(
[Incoming and outgoing messages],
```
@startuml
[-> A: DoWork
activate A
A -> A: Internal call
activate A
A ->] : << createRequest >>
A<--] : RequestCreated
deactivate A
[<- A: Done
deactivate A
@enduml
```
),
(
[Incoming and outgoing messages (2)],
```
@startuml
participant Alice
participant Bob #lightblue
Alice -> Bob
Bob -> Carol
...
[-> Bob
[o-> Bob
[o->o Bob
[x-> Bob
...
[<- Bob
[x<- Bob
...
Bob ->]
Bob ->o]
Bob o->o]
Bob ->x]
...
Bob <-]
Bob x<-]
@enduml
```
),
(
[Short arrows for incoming and outgoing messages],
```
@startuml
?-> Alice : ""?->""\n**short** to actor1
[-> Alice : ""[->""\n**from start** to actor1
[-> Bob : ""[->""\n**from start** to actor2
?-> Bob : ""?->""\n**short** to actor2
Alice ->] : ""->]""\nfrom actor1 **to end**
Alice ->? : ""->?""\n**short** from actor1
Alice -> Bob : ""->"" \nfrom actor1 to actor2
@enduml
```
),
(
[Normal arrow],
```
@startuml
participant Alice as a
participant Bob as b
a -> b : ""-> ""
a ->> b : ""->> ""
a -\ b : ""-\ ""
a -\\ b : ""-\\\\""
a -/ b : ""-/ ""
a -// b : ""-// ""
a ->x b : ""->x ""
a x-> b : ""x-> ""
a o-> b : ""o-> ""
a ->o b : ""->o ""
a o->o b : ""o->o ""
a <-> b : ""<-> ""
a o<->o b : ""o<->o""
a x<->x b : ""x<->x""
a ->>o b : ""->>o ""
a -\o b : ""-\o ""
a -\\o b : ""-\\\\o""
a -/o b : ""-/o ""
a -//o b : ""-//o ""
a x->o b : ""x->o ""
@enduml
```
),
(
[Itself arrow],
```
@startuml
participant Alice as a
participant Bob as b
a -> a : ""-> ""
a ->> a : ""->> ""
a -\ a : ""-\ ""
a -\\ a : ""-\\\\""
a -/ a : ""-/ ""
a -// a : ""-// ""
a ->x a : ""->x ""
a x-> a : ""x-> ""
a o-> a : ""o-> ""
a ->o a : ""->o ""
a o->o a : ""o->o ""
a <-> a : ""<-> ""
a o<->o a : ""o<->o""
a x<->x a : ""x<->x""
a ->>o a : ""->>o ""
a -\o a : ""-\o ""
a -\\o a : ""-\\\\o""
a -/o a : ""-/o ""
a -//o a : ""-//o ""
a x->o a : ""x->o ""
@enduml
```
),
(
[Incoming messages (with '|')],
```
@startuml
participant Alice as a
participant Bob as b
[-> b : ""[-> ""
[->> b : ""[->> ""
[-\ b : ""[-\ ""
[-\\ b : ""[-\\\\""
[-/ b : ""[-/ ""
[-// b : ""[-// ""
[->x b : ""[->x ""
[x-> b : ""[x-> ""
[o-> b : ""[o-> ""
[->o b : ""[->o ""
[o->o b : ""[o->o ""
[<-> b : ""[<-> ""
[o<->o b : ""[o<->o""
[x<->x b : ""[x<->x""
[->>o b : ""[->>o ""
[-\o b : ""[-\o ""
[-\\o b : ""[-\\\\o""
[-/o b : ""[-/o ""
[-//o b : ""[-//o ""
[x->o b : ""[x->o ""
@enduml
```
),
(
[Outgoing messages (with '|')],
```
@startuml
participant Alice as a
participant Bob as b
a ->] : ""->] ""
a ->>] : ""->>] ""
a -\] : ""-\] ""
a -\\] : ""-\\\\]""
a -/] : ""-/] ""
a -//] : ""-//] ""
a ->x] : ""->x] ""
a x->] : ""x->] ""
a o->] : ""o->] ""
a ->o] : ""->o] ""
a o->o] : ""o->o] ""
a <->] : ""<->] ""
a o<->o] : ""o<->o]""
a x<->x] : ""x<->x]""
a ->>o] : ""->>o] ""
a -\o] : ""-\o] ""
a -\\o] : ""-\\\\o]""
a -/o] : ""-/o] ""
a -//o] : ""-//o] ""
a x->o] : ""x->o] ""
@enduml
```
),
(
[Short incoming (with '?')],
```
@startuml
participant Alice as a
participant Bob as b
a -> b : //Long long label//
?-> b : ""?-> ""
?->> b : ""?->> ""
?-\ b : ""?-\ ""
?-\\ b : ""?-\\\\""
?-/ b : ""?-/ ""
?-// b : ""?-// ""
?->x b : ""?->x ""
?x-> b : ""?x-> ""
?o-> b : ""?o-> ""
?->o b : ""?->o ""
?o->o b : ""?o->o ""
?<-> b : ""?<-> ""
?o<->o b : ""?o<->o""
?x<->x b : ""?x<->x""
?->>o b : ""?->>o ""
?-\o b : ""?-\o ""
?-\\o b : ""?-\\\\o ""
?-/o b : ""?-/o ""
?-//o b : ""?-//o ""
?x->o b : ""?x->o ""
@enduml
```
),
(
[Short outgoing (with '?')],
```
@startuml
participant Alice as a
participant Bob as b
a -> b : //Long long label//
a ->? : ""->? ""
a ->>? : ""->>? ""
a -\? : ""-\? ""
a -\\? : ""-\\\\?""
a -/? : ""-/? ""
a -//? : ""-//? ""
a ->x? : ""->x? ""
a x->? : ""x->? ""
a o->? : ""o->? ""
a ->o? : ""->o? ""
a o->o? : ""o->o? ""
a <->? : ""<->? ""
a o<->o? : ""o<->o?""
a x<->x? : ""x<->x?""
a ->>o? : ""->>o? ""
a -\o? : ""-\o? ""
a -\\o? : ""-\\\\o?""
a -/o? : ""-/o? ""
a -//o? : ""-//o? ""
a x->o? : ""x->o? ""
@enduml
```
)
)
#{
for (title, uml) in examples {
heading(title)
box(
stroke: gray,
inset: 1em,
stack(
dir: ltr,
spacing: 1em,
raw(uml.text, block: true, lang: "plantuml"),
from-plantuml(uml)
)
)
pagebreak(weak: true)
}
}

Binary file not shown.

View File

@ -33,15 +33,38 @@ Alice <-- Bob: Another authentication Response
#chronos.diagram({ #chronos.diagram({
import chronos: * import chronos: *
_seq("Alice", "Bob", comment: "Authentication Request") _seq("Alice", "Bob", comment: "Authentication Request")
_seq("Bob", "Alice", comment: "Authentication Failure")
_grp("My own label", desc: "My own label2", { _alt(
_seq("Alice", "Log", comment: "Log attack start") "successful case", {
_grp("loop", desc: "1000 times", { _seq("Bob", "Alice", comment: "Authentication Accepted")
_seq("Alice", "Bob", comment: "DNS Attack") },
}) "some kind of failure", {
_seq("Alice", "Bob", comment: "Log attack end") _seq("Bob", "Alice", comment: "Authentication Failure")
_grp("My own label", desc: "My own label2", {
_seq("Alice", "Log", comment: "Log attack start")
_loop("1000 times", {
_seq("Alice", "Bob", comment: "DNS Attack")
})
_seq("Alice", "Log", comment: "Log attack end")
})
},
"Another type of failure", {
_seq("Bob", "Alice", comment: "Please repeat")
}
)
})
#chronos.diagram({
import chronos: *
_par("a", display-name: box(width: 1.5em, height: .5em), show-bottom: false)
_par("b", display-name: box(width: 1.5em, height: .5em), show-bottom: false)
_col("a", "b", width: 2cm)
_loop("a<1", min: 1, {
_seq("a", "b", end-tip: ">>")
_seq("b", "a", end-tip: ">>")
}) })
_seq("a", "b", end-tip: ">>")
}) })
#chronos.diagram({ #chronos.diagram({
@ -55,6 +78,15 @@ Alice <-- Bob: Another authentication Response
_seq("Bob", "Alice", comment: "another authentication Response", dashed: true) _seq("Bob", "Alice", comment: "another authentication Response", dashed: true)
}) })
#chronos.diagram({
import chronos: *
_seq("Alice", "Bob", comment: "Authentication Request")
_delay()
_seq("Bob", "Alice", comment: "Authentication Response")
_delay(name: "5 minutes later")
_seq("Bob", "Alice", comment: "Good Bye !")
})
#chronos.diagram({ #chronos.diagram({
import chronos: * import chronos: *
_seq("Alice", "Bob", comment: "message 1") _seq("Alice", "Bob", comment: "message 1")

BIN
gallery/plantuml_test.pdf Normal file

Binary file not shown.

271
gallery/plantuml_test.typ Normal file
View File

@ -0,0 +1,271 @@
#import "../src/lib.typ": from-plantuml
#set page(width: auto, height: auto)
/*
#from-plantuml(```
@startuml
actor User as usr
participant can_message as can
control kartculator as kc
queue XF as xf
entity Drive as drive
entity Steering as steering
usr -\ xf : set message "move"
xf -> can : new value on joystick
== If X axis change value ==
can -> kc : calculate new position
kc -> can : build message
can -> steering : set new position
== If Y axis change value ==
can -> kc : calculate new torque
kc -> can : build message
can -> xf : set message "torque"
xf -> drive : set new torque
@enduml
```)
#pagebreak(weak: true)
#from-plantuml(```
@startuml
actor CAN_BUS as bus
participant interrupt as ISR
queue XF as xf
participant ecan as ecan
participant canInterface as can
control canMessageController as msg
bus -\\ ISR ++ : can message
ISR -> can : newMsg
can -> ecan : read
ecan --> can : message
can -> xf : POST XF
destroy ISR
group TICK XF
xf o-> can : receiveCan()
can -> msg : processIncoming()
msg -> can : create message
can -> xf : POST XF
end
group TICK XF
xf o-> can : sendCan()
can -> ecan : write
ecan -\\ bus : can message
end
@enduml
```)
#pagebreak(weak: true)
*/
/*
#from-plantuml(```
@startuml
participant "Behavior::StateMachine" as sm
participant Dispatcher as d
participant TimeoutManager as tm
entity "Event::Timeout" as t
queue "TimeoutManager::timeouts_" as timeouts
autoactivate off
|||
|||
== Schedule timeout ==
|||
sm -> sm++ : scheduleTimeout
sm -> d ++: getDispatcher
d --> sm --: dispatcher
sm -> d --++ : scheduleTimeout
d -> tm ++: getTimeoutManager
tm --> d --: timeoutManager
d -> tm --++ : scheduleTimeout
tm -> t ** : new
t --> tm
tm -> timeouts --++: insert
|||
|||
== Decrement timeout (and dispatch) ==
|||
loop every tickInterval
?->> tm ++: tick
tm -> timeouts : getFront
timeouts -> t ++
t --> timeouts
timeouts --> tm : timeout
tm -> t --: decrement
end
|||
note left t
When timeout is 0,
dispatch event
end note
t -> timeouts : pop
deactivate timeouts
t ->? --: pushEvent
|||
|||
== Unschedule timeout ==
|||
sm -> sm++ : unscheduleTimeout
sm -> d ++: getDispatcher
d --> sm --: dispatcher
sm -> d --++ : unscheduleTimeout
d -> tm ++: getTimeoutManager
tm --> d --: timeoutManager
d -> tm --++ : unscheduleTimeout
tm -> timeouts --: erase
timeouts -> t !!
@enduml
```)
*/
#pagebreak(weak: true)
#from-plantuml(```
@startuml
participant Behavior as b
participant Dispatcher as d
entity Event as e
participant EventQueue as eq
queue "EventQueue::queue_" as q
== Create an Event ==
|||
?->> b ++ : GEN
b -> e ** : new
b -> b --++ : pushEvent
e -> b : getBehavior
b --> e ++: setBehavior
e --> b --
b -> d ++ : getDispatcher
d --> b
b -> d -- : pushEvent
d ->? -- : push
|||
|||
== Push Event ==
|||
?->> d ++: pushEvent
d -> eq--++: push
eq -> q ++
q --> eq
eq -> q -- : pushEndQueue
|||
|||
== Dispatch ==
|||
?->> d ++: executeOnce
d -> q : getFront
q -> e ++
e --> q
q --> d : event
d -> q : pop
deactivate q
d -> d --++ : dispatchEvent
d -> b ++ : getBehavior
b --> d
d -> b -- : process
b -> b--: processEvent
destroy e
@enduml
```)
#pagebreak(weak: true)
#from-plantuml(```
@startuml
'https://plantuml.com/sequence-diagram
actor User as usr
participant "Pb L" as pbL
participant "Pb R" as pbR
participant "LED L" as ledL
participant "LED R" as ledR
== Single click ==
group Single click left
usr -\ pbL ++: pressButton
usr -\ pbL : releaseButton
pbL -> ledL --++ : blink
usr -\ pbL ++: pressButton
usr -\ pbL : releaseButton
pbL -> ledL -- : endBlink
deactivate ledL
end
group Single click right
usr -\ pbR ++: pressButton
usr -\ pbR : releaseButton
pbR -> ledR --++ : blink
usr -\ pbR ++: pressButton
usr -\ pbR : releaseButton
pbR -> ledR -- : endBlink
deactivate ledR
end
== Double click ==
group Double click left
usr -\ pbL ++: pressButton
usr -\ pbL : releaseButton
usr -\ pbL : pressButton
pbL -> ledL --++ : blink
note right ledL: blink 3x
ledL ->x ledL -- : finished
end
group Double click right
usr -\ pbR ++: pressButton
usr -\ pbR : releaseButton
usr -\ pbR : pressButton
pbR -> ledR --++ : blink
note right ledR: blink 3x
ledR ->x ledR -- : finished
end
== Long click ==
group Long click left
usr -\ pbL ++: pressButton
pbL -> ledR--: blink
activate ledL
activate ledR
usr -\ pbL ++: pressButton
pbL -> ledR -- : endBlink
deactivate ledL
deactivate ledR
end
group Long click right
usr -\ pbR ++: pressButton
pbR -> ledR--: blink
activate ledL
activate ledR
usr -\ pbL ++: pressButton
pbL -> ledR -- : endBlink
deactivate ledL
deactivate ledR
end
@enduml
```)

Binary file not shown.

View File

@ -183,7 +183,7 @@ chronos.diagram({
examples: examples examples: examples
) )
) )
#tidy.show-module(grp-docs, show-outline: false) #tidy.show-module(grp-docs, show-outline: false, sort-functions: none)
#pagebreak(weak: true) #pagebreak(weak: true)

View File

@ -1,6 +1,7 @@
#import "utils.typ": get-group-span, fit-canvas #import "utils.typ": get-group-span, fit-canvas
#import "renderer.typ": render #import "renderer.typ": render
#import "participant.typ" as participant: _par, PAR-SPECIALS #import "participant.typ" as participant: _par, PAR-SPECIALS
#import "sequence.typ": _seq
#let _gap(size: 20) = { #let _gap(size: 20) = {
return (( return ((
@ -38,7 +39,9 @@
let elmts = elements let elmts = elements
let i = 0 let i = 0
// Flatten groups let activation-history = ()
// Flatten groups + convert returns
while i < elmts.len() { while i < elmts.len() {
let elmt = elmts.at(i) let elmt = elmts.at(i)
if elmt.type == "grp" { if elmt.type == "grp" {
@ -62,6 +65,30 @@
),) + ),) +
elmts.slice(i+1) elmts.slice(i+1)
) )
} else if elmt.type == "seq" {
if elmt.enable-dst {
activation-history.push(elmt)
}
} else if elmt.type == "evt" {
if elmt.event == "enable" {
for elmt2 in elmts.slice(0, i).rev() {
if elmt2.type == "seq" {
activation-history.push(elmt2)
break
}
}
}
} else if elmt.type == "ret" {
if activation-history.len() == 0 {
panic("Cannot return if no lifeline is activated")
}
let seq = activation-history.pop()
elmts.at(i) = _seq(
seq.p2, seq.p1,
comment: elmt.comment,
disable-src: true,
dashed: true
).first()
} }
i += 1 i += 1
} }
@ -130,6 +157,21 @@
} else if elmt.side == "right" { } else if elmt.side == "right" {
linked.push("]") linked.push("]")
} }
let pars = none
if type(elmt.pos) == str {
pars = (elmt.pos,)
} else if type(elmt.pos) == array {
pars = elmt.pos
}
if pars != none {
for par in pars {
if not participant._exists(participants, par) {
participants.push(_par(par).first())
}
}
}
last-note = ( last-note = (
elmt: elmt, elmt: elmt,
i: i i: i
@ -185,7 +227,7 @@
// Compute groups spans (horizontal) // Compute groups spans (horizontal)
for (i, elmt) in elmts.enumerate() { for (i, elmt) in elmts.enumerate() {
if elmt.type == "grp" { if elmt.type == "grp" or elmt.type == "alt" {
let (min-i, max-i) = get-group-span(participants, elmt) let (min-i, max-i) = get-group-span(participants, elmt)
elmts.at(i).insert("min-i", min-i) elmts.at(i).insert("min-i", min-i)
elmts.at(i).insert("max-i", max-i) elmts.at(i).insert("max-i", max-i)
@ -202,7 +244,3 @@
let canvas = render(participants, elmts) let canvas = render(participants, elmts)
fit-canvas(canvas, width: width) fit-canvas(canvas, width: width)
} }
#let from-plantuml(code) = {
let code = code.text
}

View File

@ -11,6 +11,36 @@
),) ),)
} }
#let _alt(desc, elmts, ..args) = {
let all-elmts = ()
all-elmts += elmts
let args = args.pos()
for i in range(0, args.len(), step: 2) {
let else-desc = args.at(i)
let else-elmts = args.at(i + 1, default: ())
all-elmts.push((
type: "else",
desc: else-desc
))
all-elmts += else-elmts
}
return _grp("alt", desc: desc, type: "alt", all-elmts)
}
#let _loop(desc, min: none, max: auto, elmts) = {
let name = "loop"
if min != none {
if max == auto {
max = "*"
}
name += "(" + str(min) + "," + str(max) + ")"
}
_grp(name, desc: desc, type: "loop", elmts)
}
#let _opt(desc, elmts) = grp("opt", desc: desc, type: "opt", elmts)
#let _break(desc, elmts) = grp("break", desc: desc, type: "break", elmts)
#let render(x0, x1, y0, y1, group) = { #let render(x0, x1, y0, y1, group) = {
let shapes = () let shapes = ()
let name = text(group.name, weight: "bold") let name = text(group.name, weight: "bold")
@ -52,3 +82,18 @@
return shapes return shapes
} }
#let render-else(x0, x1, y, elmt) = {
let shapes = draw.line(
(x0, y),
(x1, y),
stroke: (dash: (2pt, 1pt), thickness: .5pt)
)
shapes += draw.content(
(x0, y),
text([\[#elmt.desc\]], weight: "bold", size: .8em),
anchor: "north-west",
padding: 3pt
)
return shapes
}

View File

@ -1,9 +1,10 @@
#let version = version(0, 1, 1) #let version = version(0, 1, 1)
#import "diagram.typ": diagram, from-plantuml, _gap, _evt, _col #import "diagram.typ": diagram, _gap, _evt, _col
#import "parser.typ": from-plantuml
#import "sequence.typ": _seq #import "sequence.typ": _seq, _ret
#import "group.typ": _grp #import "group.typ": _grp, _loop, _alt, _opt, _break
#import "participant.typ": _par #import "participant.typ": _par
#import "separator.typ": _sep #import "separator.typ": _sep, _delay
#import "note.typ": _note #import "note.typ": _note
#import "sync.typ": _sync #import "sync.typ": _sync

View File

@ -25,6 +25,9 @@
panic("Aligned notes can only be over a participant (got side '" + side + "')") panic("Aligned notes can only be over a participant (got side '" + side + "')")
} }
} }
if color == auto {
color = COL-NOTE
}
return (( return ((
type: "note", type: "note",
side: side, side: side,

1036
src/parser.typ Normal file

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
"queue", "queue",
"custom" "custom"
) )
#let DEFAULT-COLOR = rgb("#E2E2F0")
#let _par( #let _par(
name, name,
@ -20,11 +21,14 @@
from-start: true, from-start: true,
invisible: false, invisible: false,
shape: "participant", shape: "participant",
color: rgb("#E2E2F0"), color: DEFAULT-COLOR,
custom-image: none, custom-image: none,
show-bottom: true, show-bottom: true,
show-top: true, show-top: true,
) = { ) = {
if color == auto {
color = DEFAULT-COLOR
}
return (( return ((
type: "par", type: "par",
name: name, name: name,

View File

@ -242,6 +242,7 @@
let draw-seq = sequence.render.with(pars-i, x-pos, participants) let draw-seq = sequence.render.with(pars-i, x-pos, participants)
let draw-group = group.render.with() let draw-group = group.render.with()
let draw-else = group.render-else.with()
let draw-sep = separator.render.with(x-pos) let draw-sep = separator.render.with(x-pos)
let draw-par = participant.render.with(x-pos) let draw-par = participant.render.with(x-pos)
let draw-note = note.render.with(pars-i, x-pos) let draw-note = note.render.with(pars-i, x-pos)
@ -283,6 +284,9 @@
if g.at(1).max-i == elmt.max-i { g.at(3) += 1 } if g.at(1).max-i == elmt.max-i { g.at(3) += 1 }
g g
}) })
if elmt.grp-type == "alt" {
elmt.insert("elses", ())
}
groups.push((y, elmt, 0, 0)) groups.push((y, elmt, 0, 0))
y -= m.height / 1pt y -= m.height / 1pt
@ -294,6 +298,21 @@
let x1 = x-pos.at(group.max-i) + end-lvl * 10 + 20 let x1 = x-pos.at(group.max-i) + end-lvl * 10 + 20
shapes += draw-group(x0, x1, start-y, y, group) shapes += draw-group(x0, x1, start-y, y, group)
if group.grp-type == "alt" {
for (else-y, else-elmt) in group.elses {
shapes += draw-else(x0, x1, else-y, else-elmt)
}
}
// Alt's elses -> reserve space for label + store position
} else if elmt.type == "else" {
y -= Y-SPACE
let m = measure(text([\[#elmt.desc\]], weight: "bold", size: .8em))
groups.last().at(1).elses.push((
y, elmt
))
y -= m.height / 1pt
// Separator // Separator
} else if elmt.type == "sep" { } else if elmt.type == "sep" {
let shps let shps
@ -304,6 +323,26 @@
} else if elmt.type == "gap" { } else if elmt.type == "gap" {
y -= elmt.size y -= elmt.size
// Delay
} else if elmt.type == "delay" {
let y0 = y
let y1 = y - elmt.size
for (i, line) in lifelines.enumerate() {
line.lines.push(("delay-start", y0))
line.lines.push(("delay-end", y1))
lifelines.at(i) = line
}
if elmt.name != none {
let x0 = x-pos.first()
let x1 = x-pos.last()
shapes += draw.content(
((x0 + x1) / 2, (y0 + y1) / 2),
anchor: "center",
elmt.name
)
}
y = y1
// Event // Event
} else if elmt.type == "evt" { } else if elmt.type == "evt" {
let par-name = elmt.participant let par-name = elmt.participant
@ -416,6 +455,28 @@
if event == "destroy" { if event == "destroy" {
destructions.push((x + lvl * LIFELINE-W / 2, line.at(1))) destructions.push((x + lvl * LIFELINE-W / 2, line.at(1)))
} }
} else if event == "delay-start" {
draw.line(
(x, last-y),
(x, line.at(1)),
stroke: (
dash: "dashed",
paint: gray.darken(40%),
thickness: .5pt
)
)
last-y = line.at(1)
} else if event == "delay-end" {
draw.line(
(x, last-y),
(x, line.at(1)),
stroke: (
dash: "loosely-dotted",
paint: gray.darken(40%),
thickness: .8pt
)
)
last-y = line.at(1)
} }
} }

View File

@ -8,6 +8,14 @@
),) ),)
} }
#let _delay(name: none, size: 30) = {
return ((
type: "delay",
name: name,
size: size
),)
}
#let render(x-pos, elmt, y) = { #let render(x-pos, elmt, y) = {
let shapes = () let shapes = ()
y -= Y-SPACE y -= Y-SPACE

View File

@ -4,6 +4,9 @@
#import "note.typ" #import "note.typ"
#let get-arrow-marks(sym, color) = { #let get-arrow-marks(sym, color) = {
if sym == none {
return none
}
if type(sym) == array { if type(sym) == array {
return sym.map(s => get-arrow-marks(s, color)) return sym.map(s => get-arrow-marks(s, color))
} }
@ -85,6 +88,13 @@
),) ),)
} }
#let _ret(comment: none) = {
return ((
type: "ret",
comment: comment
),)
}
#let render(pars-i, x-pos, participants, elmt, y, lifelines) = { #let render(pars-i, x-pos, participants, elmt, y, lifelines) = {
let shapes = () let shapes = ()
@ -294,7 +304,7 @@
// Start circle tip // Start circle tip
if is-circle-tip(elmt.start-tip) { if is-circle-tip(elmt.start-tip) {
shapes += draw.circle(pts.first(), radius: CIRCLE-TIP-RADIUS, stroke: elmt.color, fill: none, name: "_circle-start-tip") shapes += draw.circle(pts.first(), radius: CIRCLE-TIP-RADIUS, stroke: none, fill: elmt.color, name: "_circle-start-tip")
pts.at(0) = "_circle-start-tip" pts.at(0) = "_circle-start-tip"
// Start cross tip // Start cross tip
@ -316,7 +326,7 @@
// End circle tip // End circle tip
if is-circle-tip(elmt.end-tip) { if is-circle-tip(elmt.end-tip) {
shapes += draw.circle(pts.last(), radius: 3, stroke: elmt.color, fill: none, name: "_circle-end-tip") shapes += draw.circle(pts.last(), radius: 3, stroke: none, fill: elmt.color, name: "_circle-end-tip")
pts.at(pts.len() - 1) = "_circle-end-tip" pts.at(pts.len() - 1) = "_circle-end-tip"
// End cross tip // End cross tip