etassia 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (727) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +140 -0
  3. data/LICENSE +25 -0
  4. data/README.adoc +436 -0
  5. data/exe/etassia +7 -0
  6. data/lib/etassia/cli.rb +187 -0
  7. data/lib/etassia/commands/batch.rb +210 -0
  8. data/lib/etassia/commands/notation_loader.rb +22 -0
  9. data/lib/etassia/commands/render.rb +98 -0
  10. data/lib/etassia/commands/types.rb +33 -0
  11. data/lib/etassia/commands/version.rb +24 -0
  12. data/lib/etassia/corpus_type_names.rb +52 -0
  13. data/lib/etassia/diagram/architecture.rb +108 -0
  14. data/lib/etassia/diagram/base.rb +61 -0
  15. data/lib/etassia/diagram/block.rb +102 -0
  16. data/lib/etassia/diagram/c4.rb +337 -0
  17. data/lib/etassia/diagram/class_diagram.rb +466 -0
  18. data/lib/etassia/diagram/class_namespace.rb +17 -0
  19. data/lib/etassia/diagram/class_note.rb +17 -0
  20. data/lib/etassia/diagram/containment.rb +107 -0
  21. data/lib/etassia/diagram/er_diagram.rb +317 -0
  22. data/lib/etassia/diagram/error.rb +38 -0
  23. data/lib/etassia/diagram/flowchart.rb +419 -0
  24. data/lib/etassia/diagram/flowchart_label_text.rb +63 -0
  25. data/lib/etassia/diagram/gantt.rb +94 -0
  26. data/lib/etassia/diagram/generic_text.rb +30 -0
  27. data/lib/etassia/diagram/git_graph.rb +57 -0
  28. data/lib/etassia/diagram/info.rb +38 -0
  29. data/lib/etassia/diagram/kanban.rb +198 -0
  30. data/lib/etassia/diagram/mindmap.rb +71 -0
  31. data/lib/etassia/diagram/packet.rb +93 -0
  32. data/lib/etassia/diagram/pie.rb +115 -0
  33. data/lib/etassia/diagram/quadrant.rb +135 -0
  34. data/lib/etassia/diagram/radar.rb +65 -0
  35. data/lib/etassia/diagram/requirement.rb +168 -0
  36. data/lib/etassia/diagram/sankey.rb +217 -0
  37. data/lib/etassia/diagram/sequence.rb +299 -0
  38. data/lib/etassia/diagram/sequence_box.rb +18 -0
  39. data/lib/etassia/diagram/sequence_frame.rb +37 -0
  40. data/lib/etassia/diagram/sequence_frame_section.rb +23 -0
  41. data/lib/etassia/diagram/sequence_text.rb +69 -0
  42. data/lib/etassia/diagram/sequence_wrap.rb +23 -0
  43. data/lib/etassia/diagram/state_diagram.rb +282 -0
  44. data/lib/etassia/diagram/timeline.rb +183 -0
  45. data/lib/etassia/diagram/treemap.rb +95 -0
  46. data/lib/etassia/diagram/user_journey.rb +153 -0
  47. data/lib/etassia/diagram/xy_chart.rb +90 -0
  48. data/lib/etassia/diagram.rb +8 -0
  49. data/lib/etassia/diagram_registry.rb +34 -0
  50. data/lib/etassia/engine.rb +334 -0
  51. data/lib/etassia/error/diagram_type_error.rb +10 -0
  52. data/lib/etassia/error/layout_error.rb +12 -0
  53. data/lib/etassia/error/notation_registration_error.rb +10 -0
  54. data/lib/etassia/error/parse_error.rb +11 -0
  55. data/lib/etassia/error/pipeline_error.rb +10 -0
  56. data/lib/etassia/error/render_error.rb +12 -0
  57. data/lib/etassia/error/score_error.rb +12 -0
  58. data/lib/etassia/error.rb +10 -0
  59. data/lib/etassia/error_report.rb +69 -0
  60. data/lib/etassia/ir/data.rb +39 -0
  61. data/lib/etassia/ir/data_value.rb +35 -0
  62. data/lib/etassia/ir/dimension.rb +10 -0
  63. data/lib/etassia/ir/edge.rb +34 -0
  64. data/lib/etassia/ir/graph.rb +28 -0
  65. data/lib/etassia/ir/integrity.rb +80 -0
  66. data/lib/etassia/ir/item.rb +46 -0
  67. data/lib/etassia/ir/model.rb +56 -0
  68. data/lib/etassia/ir/node.rb +10 -0
  69. data/lib/etassia/ir/placement.rb +47 -0
  70. data/lib/etassia/ir/prepositioned.rb +25 -0
  71. data/lib/etassia/ir/prepositioned_item.rb +26 -0
  72. data/lib/etassia/ir/property_set.rb +43 -0
  73. data/lib/etassia/ir/scalar.rb +25 -0
  74. data/lib/etassia/ir/series.rb +10 -0
  75. data/lib/etassia/ir.rb +17 -0
  76. data/lib/etassia/js_math.rb +511 -0
  77. data/lib/etassia/js_number.rb +64 -0
  78. data/lib/etassia/layout/architecture.rb +1472 -0
  79. data/lib/etassia/layout/base.rb +276 -0
  80. data/lib/etassia/layout/block.rb +738 -0
  81. data/lib/etassia/layout/c4.rb +876 -0
  82. data/lib/etassia/layout/c4_boundary_metrics.rb +35 -0
  83. data/lib/etassia/layout/c4_bounds.rb +106 -0
  84. data/lib/etassia/layout/c4_intersection.rb +77 -0
  85. data/lib/etassia/layout/c4_placement.rb +135 -0
  86. data/lib/etassia/layout/c4_shape_metrics.rb +97 -0
  87. data/lib/etassia/layout/c4_shape_outline.rb +67 -0
  88. data/lib/etassia/layout/c4_stereotype.rb +30 -0
  89. data/lib/etassia/layout/c4_text.rb +54 -0
  90. data/lib/etassia/layout/class_cell_placement.rb +148 -0
  91. data/lib/etassia/layout/class_diagram.rb +1116 -0
  92. data/lib/etassia/layout/class_diagram_namespaces.rb +26 -0
  93. data/lib/etassia/layout/class_diagram_notes.rb +26 -0
  94. data/lib/etassia/layout/class_name_advances.rb +31 -0
  95. data/lib/etassia/layout/class_namespace_box.rb +21 -0
  96. data/lib/etassia/layout/class_namespace_nodes.rb +197 -0
  97. data/lib/etassia/layout/class_namespace_placement.rb +71 -0
  98. data/lib/etassia/layout/class_note.rb +44 -0
  99. data/lib/etassia/layout/class_note_nodes.rb +84 -0
  100. data/lib/etassia/layout/elk_notices.rb +48 -0
  101. data/lib/etassia/layout/elk_placement.rb +197 -0
  102. data/lib/etassia/layout/er_diagram.rb +1002 -0
  103. data/lib/etassia/layout/er_diagram_adjacency.rb +20 -0
  104. data/lib/etassia/layout/er_diagram_alignment.rb +72 -0
  105. data/lib/etassia/layout/er_diagram_compaction.rb +85 -0
  106. data/lib/etassia/layout/er_diagram_conflicts.rb +70 -0
  107. data/lib/etassia/layout/er_diagram_coordinates.rb +104 -0
  108. data/lib/etassia/layout/er_diagram_entity_size.rb +82 -0
  109. data/lib/etassia/layout/er_diagram_layering.rb +109 -0
  110. data/lib/etassia/layout/er_diagram_ordering.rb +143 -0
  111. data/lib/etassia/layout/er_diagram_placement.rb +163 -0
  112. data/lib/etassia/layout/er_diagram_placement_node.rb +22 -0
  113. data/lib/etassia/layout/error.rb +133 -0
  114. data/lib/etassia/layout/flowchart/brandes_koepf.rb +208 -0
  115. data/lib/etassia/layout/flowchart/cluster_reach.rb +70 -0
  116. data/lib/etassia/layout/flowchart/component_packing.rb +129 -0
  117. data/lib/etassia/layout/flowchart/directed_placement.rb +428 -0
  118. data/lib/etassia/layout/flowchart/edge_chains.rb +111 -0
  119. data/lib/etassia/layout/flowchart/edge_order.rb +34 -0
  120. data/lib/etassia/layout/flowchart/edge_router.rb +533 -0
  121. data/lib/etassia/layout/flowchart/label_backdrop_geometry.rb +62 -0
  122. data/lib/etassia/layout/flowchart/layer_spacing.rb +85 -0
  123. data/lib/etassia/layout/flowchart/level_edges.rb +74 -0
  124. data/lib/etassia/layout/flowchart/level_frame.rb +47 -0
  125. data/lib/etassia/layout/flowchart/minimum_rank_spacing.rb +74 -0
  126. data/lib/etassia/layout/flowchart/row_centering.rb +229 -0
  127. data/lib/etassia/layout/flowchart.rb +1290 -0
  128. data/lib/etassia/layout/gantt.rb +814 -0
  129. data/lib/etassia/layout/gantt_duration.rb +49 -0
  130. data/lib/etassia/layout/gantt_exclusions.rb +74 -0
  131. data/lib/etassia/layout/gantt_ticks.rb +165 -0
  132. data/lib/etassia/layout/git_graph.rb +1075 -0
  133. data/lib/etassia/layout/grid.rb +165 -0
  134. data/lib/etassia/layout/info.rb +113 -0
  135. data/lib/etassia/layout/kanban.rb +452 -0
  136. data/lib/etassia/layout/kanban_card_text.rb +95 -0
  137. data/lib/etassia/layout/mindmap.rb +425 -0
  138. data/lib/etassia/layout/mindmap_cose.rb +81 -0
  139. data/lib/etassia/layout/mindmap_cose_body.rb +93 -0
  140. data/lib/etassia/layout/mindmap_cose_geometry.rb +190 -0
  141. data/lib/etassia/layout/mindmap_cose_grid.rb +96 -0
  142. data/lib/etassia/layout/mindmap_cose_link.rb +42 -0
  143. data/lib/etassia/layout/mindmap_cose_relaxation.rb +166 -0
  144. data/lib/etassia/layout/mindmap_cose_seed.rb +93 -0
  145. data/lib/etassia/layout/mindmap_cose_wedge.rb +37 -0
  146. data/lib/etassia/layout/mindmap_node_size.rb +130 -0
  147. data/lib/etassia/layout/packet.rb +285 -0
  148. data/lib/etassia/layout/pie.rb +305 -0
  149. data/lib/etassia/layout/pie_legend.rb +64 -0
  150. data/lib/etassia/layout/pie_legend_entry.rb +21 -0
  151. data/lib/etassia/layout/quadrant.rb +402 -0
  152. data/lib/etassia/layout/radar.rb +442 -0
  153. data/lib/etassia/layout/requirement.rb +765 -0
  154. data/lib/etassia/layout/sankey.rb +501 -0
  155. data/lib/etassia/layout/scene.rb +18 -0
  156. data/lib/etassia/layout/sequence/actor_gaps.rb +151 -0
  157. data/lib/etassia/layout/sequence/box_shape.rb +23 -0
  158. data/lib/etassia/layout/sequence/creations.rb +73 -0
  159. data/lib/etassia/layout/sequence/frame_divider.rb +22 -0
  160. data/lib/etassia/layout/sequence/frame_placement.rb +424 -0
  161. data/lib/etassia/layout/sequence/frame_reader.rb +75 -0
  162. data/lib/etassia/layout/sequence/frame_shape.rb +45 -0
  163. data/lib/etassia/layout/sequence/geometry.rb +24 -0
  164. data/lib/etassia/layout/sequence/message_rows.rb +165 -0
  165. data/lib/etassia/layout/sequence/note.rb +16 -0
  166. data/lib/etassia/layout/sequence/note_box.rb +114 -0
  167. data/lib/etassia/layout/sequence/note_placement.rb +223 -0
  168. data/lib/etassia/layout/sequence/text_width.rb +69 -0
  169. data/lib/etassia/layout/sequence/text_wrap.rb +93 -0
  170. data/lib/etassia/layout/sequence.rb +936 -0
  171. data/lib/etassia/layout/state_diagram.rb +1277 -0
  172. data/lib/etassia/layout/timeline.rb +165 -0
  173. data/lib/etassia/layout/timeline_card.rb +142 -0
  174. data/lib/etassia/layout/timeline_flow.rb +164 -0
  175. data/lib/etassia/layout/timeline_line.rb +33 -0
  176. data/lib/etassia/layout/timeline_rounding.rb +19 -0
  177. data/lib/etassia/layout/timeline_text.rb +105 -0
  178. data/lib/etassia/layout/treemap.rb +303 -0
  179. data/lib/etassia/layout/treemap_placement.rb +52 -0
  180. data/lib/etassia/layout/treemap_squarify.rb +108 -0
  181. data/lib/etassia/layout/user_journey.rb +300 -0
  182. data/lib/etassia/layout/user_journey_face.rb +33 -0
  183. data/lib/etassia/layout/user_journey_face_geometry.rb +80 -0
  184. data/lib/etassia/layout/user_journey_geometry.rb +336 -0
  185. data/lib/etassia/layout/user_journey_legend.rb +84 -0
  186. data/lib/etassia/layout/xy_chart.rb +620 -0
  187. data/lib/etassia/layout.rb +19 -0
  188. data/lib/etassia/lint_debt_scoreboard.rb +219 -0
  189. data/lib/etassia/markdown_text/emphasis_simulator.rb +373 -0
  190. data/lib/etassia/markdown_text.rb +393 -0
  191. data/lib/etassia/notation/builtin.rb +39 -0
  192. data/lib/etassia/notation/entry.rb +15 -0
  193. data/lib/etassia/notation/mermaid/class_diagram_config.rb +42 -0
  194. data/lib/etassia/notation/mermaid/git_graph_config.rb +46 -0
  195. data/lib/etassia/notation/mermaid/ir_adapter.rb +27 -0
  196. data/lib/etassia/notation/mermaid/ir_adapters/architecture.rb +196 -0
  197. data/lib/etassia/notation/mermaid/ir_adapters/block.rb +138 -0
  198. data/lib/etassia/notation/mermaid/ir_adapters/c4.rb +258 -0
  199. data/lib/etassia/notation/mermaid/ir_adapters/class_diagram.rb +257 -0
  200. data/lib/etassia/notation/mermaid/ir_adapters/er_diagram.rb +220 -0
  201. data/lib/etassia/notation/mermaid/ir_adapters/error.rb +32 -0
  202. data/lib/etassia/notation/mermaid/ir_adapters/flowchart.rb +208 -0
  203. data/lib/etassia/notation/mermaid/ir_adapters/gantt.rb +236 -0
  204. data/lib/etassia/notation/mermaid/ir_adapters/git_graph.rb +186 -0
  205. data/lib/etassia/notation/mermaid/ir_adapters/info.rb +34 -0
  206. data/lib/etassia/notation/mermaid/ir_adapters/kanban.rb +110 -0
  207. data/lib/etassia/notation/mermaid/ir_adapters/mindmap.rb +76 -0
  208. data/lib/etassia/notation/mermaid/ir_adapters/packet.rb +51 -0
  209. data/lib/etassia/notation/mermaid/ir_adapters/pie.rb +71 -0
  210. data/lib/etassia/notation/mermaid/ir_adapters/quadrant.rb +116 -0
  211. data/lib/etassia/notation/mermaid/ir_adapters/radar.rb +142 -0
  212. data/lib/etassia/notation/mermaid/ir_adapters/requirement.rb +237 -0
  213. data/lib/etassia/notation/mermaid/ir_adapters/sankey.rb +74 -0
  214. data/lib/etassia/notation/mermaid/ir_adapters/sequence.rb +304 -0
  215. data/lib/etassia/notation/mermaid/ir_adapters/state_diagram.rb +225 -0
  216. data/lib/etassia/notation/mermaid/ir_adapters/timeline.rb +93 -0
  217. data/lib/etassia/notation/mermaid/ir_adapters/treemap.rb +143 -0
  218. data/lib/etassia/notation/mermaid/ir_adapters/user_journey.rb +127 -0
  219. data/lib/etassia/notation/mermaid/ir_adapters/xychart.rb +131 -0
  220. data/lib/etassia/notation/mermaid.rb +399 -0
  221. data/lib/etassia/notation/parsed.rb +31 -0
  222. data/lib/etassia/notation/plantuml/activity/arrow_head.rb +45 -0
  223. data/lib/etassia/notation/plantuml/activity/arrow_style.rb +53 -0
  224. data/lib/etassia/notation/plantuml/activity/bar_columns.rb +75 -0
  225. data/lib/etassia/notation/plantuml/activity/box.rb +60 -0
  226. data/lib/etassia/notation/plantuml/activity/builder.rb +77 -0
  227. data/lib/etassia/notation/plantuml/activity/canvas.rb +117 -0
  228. data/lib/etassia/notation/plantuml/activity/column_centres.rb +151 -0
  229. data/lib/etassia/notation/plantuml/activity/columns.rb +224 -0
  230. data/lib/etassia/notation/plantuml/activity/diagram.rb +35 -0
  231. data/lib/etassia/notation/plantuml/activity/edge.rb +35 -0
  232. data/lib/etassia/notation/plantuml/activity/edge_marks.rb +68 -0
  233. data/lib/etassia/notation/plantuml/activity/exits.rb +61 -0
  234. data/lib/etassia/notation/plantuml/activity/frame_marks.rb +116 -0
  235. data/lib/etassia/notation/plantuml/activity/lane_steps.rb +82 -0
  236. data/lib/etassia/notation/plantuml/activity/lane_titles.rb +46 -0
  237. data/lib/etassia/notation/plantuml/activity/layout.rb +50 -0
  238. data/lib/etassia/notation/plantuml/activity/legacy_parser.rb +181 -0
  239. data/lib/etassia/notation/plantuml/activity/legacy_row_gaps.rb +65 -0
  240. data/lib/etassia/notation/plantuml/activity/mark.rb +23 -0
  241. data/lib/etassia/notation/plantuml/activity/marks.rb +62 -0
  242. data/lib/etassia/notation/plantuml/activity/metrics.rb +169 -0
  243. data/lib/etassia/notation/plantuml/activity/new_parser/blocks.rb +166 -0
  244. data/lib/etassia/notation/plantuml/activity/new_parser.rb +177 -0
  245. data/lib/etassia/notation/plantuml/activity/node.rb +50 -0
  246. data/lib/etassia/notation/plantuml/activity/node_marks.rb +174 -0
  247. data/lib/etassia/notation/plantuml/activity/note.rb +27 -0
  248. data/lib/etassia/notation/plantuml/activity/parser.rb +51 -0
  249. data/lib/etassia/notation/plantuml/activity/placement.rb +327 -0
  250. data/lib/etassia/notation/plantuml/activity/ranking.rb +142 -0
  251. data/lib/etassia/notation/plantuml/activity/reach.rb +58 -0
  252. data/lib/etassia/notation/plantuml/activity/renderer.rb +220 -0
  253. data/lib/etassia/notation/plantuml/activity/route.rb +22 -0
  254. data/lib/etassia/notation/plantuml/activity/router.rb +308 -0
  255. data/lib/etassia/notation/plantuml/activity/row_gaps.rb +105 -0
  256. data/lib/etassia/notation/plantuml/activity/row_spread.rb +59 -0
  257. data/lib/etassia/notation/plantuml/activity/scene.rb +20 -0
  258. data/lib/etassia/notation/plantuml/activity/text.rb +36 -0
  259. data/lib/etassia/notation/plantuml/activity.rb +50 -0
  260. data/lib/etassia/notation/plantuml/arrow.rb +135 -0
  261. data/lib/etassia/notation/plantuml/caption.rb +50 -0
  262. data/lib/etassia/notation/plantuml/caption_layout.rb +97 -0
  263. data/lib/etassia/notation/plantuml/caption_rows.rb +67 -0
  264. data/lib/etassia/notation/plantuml/class_header.rb +178 -0
  265. data/lib/etassia/notation/plantuml/class_name.rb +51 -0
  266. data/lib/etassia/notation/plantuml/component.rb +37 -0
  267. data/lib/etassia/notation/plantuml/creole.rb +278 -0
  268. data/lib/etassia/notation/plantuml/creole_icons.rb +33 -0
  269. data/lib/etassia/notation/plantuml/diagram.rb +60 -0
  270. data/lib/etassia/notation/plantuml/diagram_builder.rb +442 -0
  271. data/lib/etassia/notation/plantuml/diagram_name.rb +51 -0
  272. data/lib/etassia/notation/plantuml/directives.rb +33 -0
  273. data/lib/etassia/notation/plantuml/drawing/box.rb +28 -0
  274. data/lib/etassia/notation/plantuml/drawing/face.rb +39 -0
  275. data/lib/etassia/notation/plantuml/drawing/pen.rb +105 -0
  276. data/lib/etassia/notation/plantuml/drawing/renderer.rb +36 -0
  277. data/lib/etassia/notation/plantuml/drawing/scene.rb +28 -0
  278. data/lib/etassia/notation/plantuml/drawing.rb +19 -0
  279. data/lib/etassia/notation/plantuml/ebnf/blocks.rb +272 -0
  280. data/lib/etassia/notation/plantuml/ebnf/layout.rb +117 -0
  281. data/lib/etassia/notation/plantuml/ebnf/model.rb +22 -0
  282. data/lib/etassia/notation/plantuml/ebnf/reader.rb +131 -0
  283. data/lib/etassia/notation/plantuml/ebnf.rb +34 -0
  284. data/lib/etassia/notation/plantuml/end_texts.rb +78 -0
  285. data/lib/etassia/notation/plantuml/entity.rb +54 -0
  286. data/lib/etassia/notation/plantuml/further.rb +38 -0
  287. data/lib/etassia/notation/plantuml/gantt/layout.rb +269 -0
  288. data/lib/etassia/notation/plantuml/gantt/reader.rb +210 -0
  289. data/lib/etassia/notation/plantuml/gantt/renderer.rb +81 -0
  290. data/lib/etassia/notation/plantuml/gantt.rb +34 -0
  291. data/lib/etassia/notation/plantuml/include/blocks.rb +66 -0
  292. data/lib/etassia/notation/plantuml/include/finder.rb +61 -0
  293. data/lib/etassia/notation/plantuml/include.rb +78 -0
  294. data/lib/etassia/notation/plantuml/ir_adapter/annotations.rb +40 -0
  295. data/lib/etassia/notation/plantuml/ir_adapter/links.rb +91 -0
  296. data/lib/etassia/notation/plantuml/ir_adapter/sink.rb +49 -0
  297. data/lib/etassia/notation/plantuml/ir_adapter/structure.rb +86 -0
  298. data/lib/etassia/notation/plantuml/ir_adapter.rb +32 -0
  299. data/lib/etassia/notation/plantuml/ir_reader/annotations.rb +51 -0
  300. data/lib/etassia/notation/plantuml/ir_reader/index.rb +49 -0
  301. data/lib/etassia/notation/plantuml/ir_reader/links.rb +133 -0
  302. data/lib/etassia/notation/plantuml/ir_reader/structure.rb +83 -0
  303. data/lib/etassia/notation/plantuml/ir_reader.rb +42 -0
  304. data/lib/etassia/notation/plantuml/junction.rb +20 -0
  305. data/lib/etassia/notation/plantuml/klass.rb +41 -0
  306. data/lib/etassia/notation/plantuml/label_clearance.rb +67 -0
  307. data/lib/etassia/notation/plantuml/layout.rb +2806 -0
  308. data/lib/etassia/notation/plantuml/member.rb +37 -0
  309. data/lib/etassia/notation/plantuml/member_icon.rb +87 -0
  310. data/lib/etassia/notation/plantuml/member_icon_elements.rb +91 -0
  311. data/lib/etassia/notation/plantuml/member_pitch.rb +76 -0
  312. data/lib/etassia/notation/plantuml/member_rule.rb +48 -0
  313. data/lib/etassia/notation/plantuml/network/grid.rb +170 -0
  314. data/lib/etassia/notation/plantuml/network/layout.rb +158 -0
  315. data/lib/etassia/notation/plantuml/network/model.rb +22 -0
  316. data/lib/etassia/notation/plantuml/network/placement.rb +64 -0
  317. data/lib/etassia/notation/plantuml/network/reader.rb +126 -0
  318. data/lib/etassia/notation/plantuml/network/shapes.rb +109 -0
  319. data/lib/etassia/notation/plantuml/network.rb +37 -0
  320. data/lib/etassia/notation/plantuml/node_graph/archimate.rb +49 -0
  321. data/lib/etassia/notation/plantuml/node_graph/box_reader.rb +86 -0
  322. data/lib/etassia/notation/plantuml/node_graph/cloud.rb +73 -0
  323. data/lib/etassia/notation/plantuml/node_graph/crows_foot.rb +85 -0
  324. data/lib/etassia/notation/plantuml/node_graph/curve.rb +66 -0
  325. data/lib/etassia/notation/plantuml/node_graph/data_reader.rb +77 -0
  326. data/lib/etassia/notation/plantuml/node_graph/data_shape.rb +39 -0
  327. data/lib/etassia/notation/plantuml/node_graph/detour.rb +98 -0
  328. data/lib/etassia/notation/plantuml/node_graph/edge.rb +26 -0
  329. data/lib/etassia/notation/plantuml/node_graph/entity_metrics.rb +49 -0
  330. data/lib/etassia/notation/plantuml/node_graph/entity_reader.rb +65 -0
  331. data/lib/etassia/notation/plantuml/node_graph/entity_shape.rb +111 -0
  332. data/lib/etassia/notation/plantuml/node_graph/figures.rb +92 -0
  333. data/lib/etassia/notation/plantuml/node_graph/frame_ports.rb +73 -0
  334. data/lib/etassia/notation/plantuml/node_graph/graph.rb +40 -0
  335. data/lib/etassia/notation/plantuml/node_graph/heads.rb +89 -0
  336. data/lib/etassia/notation/plantuml/node_graph/label_fan.rb +76 -0
  337. data/lib/etassia/notation/plantuml/node_graph/label_placer.rb +310 -0
  338. data/lib/etassia/notation/plantuml/node_graph/label_waypoint.rb +47 -0
  339. data/lib/etassia/notation/plantuml/node_graph/layout.rb +293 -0
  340. data/lib/etassia/notation/plantuml/node_graph/link.rb +78 -0
  341. data/lib/etassia/notation/plantuml/node_graph/macros.rb +39 -0
  342. data/lib/etassia/notation/plantuml/node_graph/metrics.rb +113 -0
  343. data/lib/etassia/notation/plantuml/node_graph/node.rb +44 -0
  344. data/lib/etassia/notation/plantuml/node_graph/note_tail.rb +58 -0
  345. data/lib/etassia/notation/plantuml/node_graph/packer.rb +126 -0
  346. data/lib/etassia/notation/plantuml/node_graph/parser.rb +309 -0
  347. data/lib/etassia/notation/plantuml/node_graph/paths.rb +48 -0
  348. data/lib/etassia/notation/plantuml/node_graph/ranking.rb +231 -0
  349. data/lib/etassia/notation/plantuml/node_graph/reader.rb +53 -0
  350. data/lib/etassia/notation/plantuml/node_graph/references.rb +38 -0
  351. data/lib/etassia/notation/plantuml/node_graph/renderer.rb +160 -0
  352. data/lib/etassia/notation/plantuml/node_graph/scene.rb +22 -0
  353. data/lib/etassia/notation/plantuml/node_graph/shapes.rb +302 -0
  354. data/lib/etassia/notation/plantuml/node_graph/skipped_ranks.rb +61 -0
  355. data/lib/etassia/notation/plantuml/node_graph/tab.rb +68 -0
  356. data/lib/etassia/notation/plantuml/node_graph/usecase_ellipse.rb +100 -0
  357. data/lib/etassia/notation/plantuml/node_graph.rb +38 -0
  358. data/lib/etassia/notation/plantuml/note.rb +27 -0
  359. data/lib/etassia/notation/plantuml/note_layout.rb +257 -0
  360. data/lib/etassia/notation/plantuml/note_outline.rb +68 -0
  361. data/lib/etassia/notation/plantuml/objects/map_rows.rb +46 -0
  362. data/lib/etassia/notation/plantuml/objects/reader.rb +208 -0
  363. data/lib/etassia/notation/plantuml/objects.rb +39 -0
  364. data/lib/etassia/notation/plantuml/package.rb +45 -0
  365. data/lib/etassia/notation/plantuml/package_color.rb +32 -0
  366. data/lib/etassia/notation/plantuml/package_frames.rb +178 -0
  367. data/lib/etassia/notation/plantuml/parser.rb +622 -0
  368. data/lib/etassia/notation/plantuml/portion_visibility.rb +91 -0
  369. data/lib/etassia/notation/plantuml/qualified_association_layout.rb +332 -0
  370. data/lib/etassia/notation/plantuml/qualifier_box.rb +78 -0
  371. data/lib/etassia/notation/plantuml/rank_chain.rb +527 -0
  372. data/lib/etassia/notation/plantuml/rank_hierarchy.rb +347 -0
  373. data/lib/etassia/notation/plantuml/relation.rb +103 -0
  374. data/lib/etassia/notation/plantuml/relation_label_separator.rb +199 -0
  375. data/lib/etassia/notation/plantuml/relation_path_points.rb +84 -0
  376. data/lib/etassia/notation/plantuml/renderer.rb +490 -0
  377. data/lib/etassia/notation/plantuml/scene.rb +134 -0
  378. data/lib/etassia/notation/plantuml/segment_box.rb +85 -0
  379. data/lib/etassia/notation/plantuml/sequence/activation.rb +28 -0
  380. data/lib/etassia/notation/plantuml/sequence/appearance.rb +71 -0
  381. data/lib/etassia/notation/plantuml/sequence/arrow_end.rb +23 -0
  382. data/lib/etassia/notation/plantuml/sequence/arrow_marks.rb +111 -0
  383. data/lib/etassia/notation/plantuml/sequence/arrow_style.rb +42 -0
  384. data/lib/etassia/notation/plantuml/sequence/arrow_syntax.rb +122 -0
  385. data/lib/etassia/notation/plantuml/sequence/bar_tracker.rb +59 -0
  386. data/lib/etassia/notation/plantuml/sequence/box.rb +23 -0
  387. data/lib/etassia/notation/plantuml/sequence/chrome.rb +60 -0
  388. data/lib/etassia/notation/plantuml/sequence/chrome_rows.rb +184 -0
  389. data/lib/etassia/notation/plantuml/sequence/create.rb +34 -0
  390. data/lib/etassia/notation/plantuml/sequence/delay.rb +19 -0
  391. data/lib/etassia/notation/plantuml/sequence/destroy.rb +20 -0
  392. data/lib/etassia/notation/plantuml/sequence/diagram.rb +86 -0
  393. data/lib/etassia/notation/plantuml/sequence/divider.rb +19 -0
  394. data/lib/etassia/notation/plantuml/sequence/edge.rb +53 -0
  395. data/lib/etassia/notation/plantuml/sequence/embedded.rb +77 -0
  396. data/lib/etassia/notation/plantuml/sequence/fill.rb +56 -0
  397. data/lib/etassia/notation/plantuml/sequence/fragment.rb +28 -0
  398. data/lib/etassia/notation/plantuml/sequence/fragment_shape.rb +138 -0
  399. data/lib/etassia/notation/plantuml/sequence/head_style.rb +43 -0
  400. data/lib/etassia/notation/plantuml/sequence/ir_adapter/blocks.rb +73 -0
  401. data/lib/etassia/notation/plantuml/sequence/ir_adapter/fills.rb +24 -0
  402. data/lib/etassia/notation/plantuml/sequence/ir_adapter/items.rb +48 -0
  403. data/lib/etassia/notation/plantuml/sequence/ir_adapter/messages.rb +62 -0
  404. data/lib/etassia/notation/plantuml/sequence/ir_adapter/participants.rb +42 -0
  405. data/lib/etassia/notation/plantuml/sequence/ir_adapter/settings.rb +54 -0
  406. data/lib/etassia/notation/plantuml/sequence/ir_adapter.rb +38 -0
  407. data/lib/etassia/notation/plantuml/sequence/ir_reader/blocks.rb +77 -0
  408. data/lib/etassia/notation/plantuml/sequence/ir_reader/fills.rb +27 -0
  409. data/lib/etassia/notation/plantuml/sequence/ir_reader/index.rb +36 -0
  410. data/lib/etassia/notation/plantuml/sequence/ir_reader/items.rb +35 -0
  411. data/lib/etassia/notation/plantuml/sequence/ir_reader/messages.rb +70 -0
  412. data/lib/etassia/notation/plantuml/sequence/ir_reader/participants.rb +42 -0
  413. data/lib/etassia/notation/plantuml/sequence/ir_reader/settings.rb +74 -0
  414. data/lib/etassia/notation/plantuml/sequence/ir_reader.rb +38 -0
  415. data/lib/etassia/notation/plantuml/sequence/keyword_defaults.rb +28 -0
  416. data/lib/etassia/notation/plantuml/sequence/layout.rb +1567 -0
  417. data/lib/etassia/notation/plantuml/sequence/message.rb +97 -0
  418. data/lib/etassia/notation/plantuml/sequence/message_wrap.rb +77 -0
  419. data/lib/etassia/notation/plantuml/sequence/note.rb +61 -0
  420. data/lib/etassia/notation/plantuml/sequence/note_fill.rb +35 -0
  421. data/lib/etassia/notation/plantuml/sequence/note_geometry.rb +126 -0
  422. data/lib/etassia/notation/plantuml/sequence/note_shape.rb +51 -0
  423. data/lib/etassia/notation/plantuml/sequence/numbered_label.rb +57 -0
  424. data/lib/etassia/notation/plantuml/sequence/numbering.rb +49 -0
  425. data/lib/etassia/notation/plantuml/sequence/outline.rb +204 -0
  426. data/lib/etassia/notation/plantuml/sequence/page_break.rb +17 -0
  427. data/lib/etassia/notation/plantuml/sequence/paint.rb +79 -0
  428. data/lib/etassia/notation/plantuml/sequence/parallel_message.rb +19 -0
  429. data/lib/etassia/notation/plantuml/sequence/parser.rb +844 -0
  430. data/lib/etassia/notation/plantuml/sequence/participant.rb +27 -0
  431. data/lib/etassia/notation/plantuml/sequence/picture.rb +29 -0
  432. data/lib/etassia/notation/plantuml/sequence/ref.rb +24 -0
  433. data/lib/etassia/notation/plantuml/sequence/ref_shape.rb +74 -0
  434. data/lib/etassia/notation/plantuml/sequence/refusals.rb +47 -0
  435. data/lib/etassia/notation/plantuml/sequence/renderer.rb +501 -0
  436. data/lib/etassia/notation/plantuml/sequence/scene.rb +200 -0
  437. data/lib/etassia/notation/plantuml/sequence/skin.rb +76 -0
  438. data/lib/etassia/notation/plantuml/sequence/spacing.rb +28 -0
  439. data/lib/etassia/notation/plantuml/sequence/style.rb +151 -0
  440. data/lib/etassia/notation/plantuml/sequence/tab_size.rb +42 -0
  441. data/lib/etassia/notation/plantuml/sequence/walker.rb +1190 -0
  442. data/lib/etassia/notation/plantuml/sequence.rb +47 -0
  443. data/lib/etassia/notation/plantuml/source_lines.rb +33 -0
  444. data/lib/etassia/notation/plantuml/state/axis_solver.rb +272 -0
  445. data/lib/etassia/notation/plantuml/state/cluster_padding.rb +75 -0
  446. data/lib/etassia/notation/plantuml/state/curve.rb +73 -0
  447. data/lib/etassia/notation/plantuml/state/dot_axis.rb +248 -0
  448. data/lib/etassia/notation/plantuml/state/extent.rb +42 -0
  449. data/lib/etassia/notation/plantuml/state/hierarchy.rb +355 -0
  450. data/lib/etassia/notation/plantuml/state/label_clearance.rb +148 -0
  451. data/lib/etassia/notation/plantuml/state/label_reach.rb +139 -0
  452. data/lib/etassia/notation/plantuml/state/layout.rb +173 -0
  453. data/lib/etassia/notation/plantuml/state/level_edges.rb +113 -0
  454. data/lib/etassia/notation/plantuml/state/look.rb +36 -0
  455. data/lib/etassia/notation/plantuml/state/note_shape.rb +73 -0
  456. data/lib/etassia/notation/plantuml/state/plane.rb +85 -0
  457. data/lib/etassia/notation/plantuml/state/ranking.rb +181 -0
  458. data/lib/etassia/notation/plantuml/state/reader.rb +310 -0
  459. data/lib/etassia/notation/plantuml/state/renderer.rb +253 -0
  460. data/lib/etassia/notation/plantuml/state/route_detour.rb +115 -0
  461. data/lib/etassia/notation/plantuml/state/route_geometry.rb +186 -0
  462. data/lib/etassia/notation/plantuml/state/routes.rb +196 -0
  463. data/lib/etassia/notation/plantuml/state/row.rb +88 -0
  464. data/lib/etassia/notation/plantuml/state/rows.rb +50 -0
  465. data/lib/etassia/notation/plantuml/state/stretch.rb +71 -0
  466. data/lib/etassia/notation/plantuml/state.rb +62 -0
  467. data/lib/etassia/notation/plantuml/stereotype_head.rb +58 -0
  468. data/lib/etassia/notation/plantuml/style/bound.rb +34 -0
  469. data/lib/etassia/notation/plantuml/style/colour.rb +41 -0
  470. data/lib/etassia/notation/plantuml/style/colour_names.rb +89 -0
  471. data/lib/etassia/notation/plantuml/style/defaults.rb +33 -0
  472. data/lib/etassia/notation/plantuml/style/inline.rb +56 -0
  473. data/lib/etassia/notation/plantuml/style/reader.rb +242 -0
  474. data/lib/etassia/notation/plantuml/style/sheet.rb +103 -0
  475. data/lib/etassia/notation/plantuml/style.rb +32 -0
  476. data/lib/etassia/notation/plantuml/style_reader.rb +160 -0
  477. data/lib/etassia/notation/plantuml/style_sheet.rb +27 -0
  478. data/lib/etassia/notation/plantuml/styled_relation_curve.rb +71 -0
  479. data/lib/etassia/notation/plantuml/timing/arrows.rb +81 -0
  480. data/lib/etassia/notation/plantuml/timing/axis.rb +100 -0
  481. data/lib/etassia/notation/plantuml/timing/layout.rb +172 -0
  482. data/lib/etassia/notation/plantuml/timing/model.rb +34 -0
  483. data/lib/etassia/notation/plantuml/timing/reader.rb +173 -0
  484. data/lib/etassia/notation/plantuml/timing/signals.rb +328 -0
  485. data/lib/etassia/notation/plantuml/timing.rb +38 -0
  486. data/lib/etassia/notation/plantuml/tree/layout.rb +276 -0
  487. data/lib/etassia/notation/plantuml/tree/mindmap_layout.rb +157 -0
  488. data/lib/etassia/notation/plantuml/tree/reader.rb +186 -0
  489. data/lib/etassia/notation/plantuml/tree/renderer.rb +67 -0
  490. data/lib/etassia/notation/plantuml/tree/role.rb +57 -0
  491. data/lib/etassia/notation/plantuml/tree/rules.rb +156 -0
  492. data/lib/etassia/notation/plantuml/tree/wbs_layout.rb +164 -0
  493. data/lib/etassia/notation/plantuml/tree.rb +41 -0
  494. data/lib/etassia/notation/plantuml/unsupported_construct_error.rb +39 -0
  495. data/lib/etassia/notation/plantuml/unsupported_constructs.rb +103 -0
  496. data/lib/etassia/notation/plantuml/usecase.rb +36 -0
  497. data/lib/etassia/notation/plantuml.rb +132 -0
  498. data/lib/etassia/notation/plantuml_data/boxes/frames.rb +101 -0
  499. data/lib/etassia/notation/plantuml_data/boxes/graph_layout.rb +348 -0
  500. data/lib/etassia/notation/plantuml_data/boxes/heads.rb +65 -0
  501. data/lib/etassia/notation/plantuml_data/boxes/highlights.rb +70 -0
  502. data/lib/etassia/notation/plantuml_data/boxes/inline_rows.rb +99 -0
  503. data/lib/etassia/notation/plantuml_data/boxes/json_reader.rb +24 -0
  504. data/lib/etassia/notation/plantuml_data/boxes/link_text.rb +94 -0
  505. data/lib/etassia/notation/plantuml_data/boxes/marks.rb +76 -0
  506. data/lib/etassia/notation/plantuml_data/boxes/ornaments.rb +130 -0
  507. data/lib/etassia/notation/plantuml_data/boxes/renderer.rb +95 -0
  508. data/lib/etassia/notation/plantuml_data/boxes/row_alignment.rb +58 -0
  509. data/lib/etassia/notation/plantuml_data/boxes/row_bend.rb +43 -0
  510. data/lib/etassia/notation/plantuml_data/boxes/scene.rb +27 -0
  511. data/lib/etassia/notation/plantuml_data/boxes/table.rb +56 -0
  512. data/lib/etassia/notation/plantuml_data/boxes/table_box.rb +355 -0
  513. data/lib/etassia/notation/plantuml_data/boxes/text.rb +101 -0
  514. data/lib/etassia/notation/plantuml_data/boxes/tree_builder.rb +82 -0
  515. data/lib/etassia/notation/plantuml_data/boxes/tree_layout.rb +82 -0
  516. data/lib/etassia/notation/plantuml_data/boxes/yaml_reader.rb +105 -0
  517. data/lib/etassia/notation/plantuml_data/openiconic.LICENSE +6 -0
  518. data/lib/etassia/notation/plantuml_data/openiconic.json +1 -0
  519. data/lib/etassia/notation/plantuml_data.rb +84 -0
  520. data/lib/etassia/notation/plantuml_salt/block.rb +80 -0
  521. data/lib/etassia/notation/plantuml_salt/box.rb +30 -0
  522. data/lib/etassia/notation/plantuml_salt/font_metrics.rb +28 -0
  523. data/lib/etassia/notation/plantuml_salt/grid_marks.rb +66 -0
  524. data/lib/etassia/notation/plantuml_salt/group_marks.rb +77 -0
  525. data/lib/etassia/notation/plantuml_salt/layout.rb +206 -0
  526. data/lib/etassia/notation/plantuml_salt/mark.rb +36 -0
  527. data/lib/etassia/notation/plantuml_salt/markup.rb +57 -0
  528. data/lib/etassia/notation/plantuml_salt/parser.rb +239 -0
  529. data/lib/etassia/notation/plantuml_salt/renderer.rb +138 -0
  530. data/lib/etassia/notation/plantuml_salt/row.rb +19 -0
  531. data/lib/etassia/notation/plantuml_salt/run.rb +34 -0
  532. data/lib/etassia/notation/plantuml_salt/scene.rb +23 -0
  533. data/lib/etassia/notation/plantuml_salt/scroll_marks.rb +94 -0
  534. data/lib/etassia/notation/plantuml_salt/tab_marks.rb +74 -0
  535. data/lib/etassia/notation/plantuml_salt/tree_marks.rb +127 -0
  536. data/lib/etassia/notation/plantuml_salt/widget.rb +40 -0
  537. data/lib/etassia/notation/plantuml_salt/widget_marks.rb +319 -0
  538. data/lib/etassia/notation/plantuml_salt.rb +66 -0
  539. data/lib/etassia/notation/plugin_failure.rb +42 -0
  540. data/lib/etassia/notation.rb +289 -0
  541. data/lib/etassia/parser/architecture.rb +25 -0
  542. data/lib/etassia/parser/atoms/delimited_run.rb +82 -0
  543. data/lib/etassia/parser/atoms/greedy_run.rb +116 -0
  544. data/lib/etassia/parser/atoms/joined.rb +47 -0
  545. data/lib/etassia/parser/atoms/trimmed_run.rb +47 -0
  546. data/lib/etassia/parser/base.rb +258 -0
  547. data/lib/etassia/parser/block.rb +32 -0
  548. data/lib/etassia/parser/builders/architecture.rb +135 -0
  549. data/lib/etassia/parser/builders/block.rb +288 -0
  550. data/lib/etassia/parser/builders/c4.rb +275 -0
  551. data/lib/etassia/parser/builders/capture_string.rb +17 -0
  552. data/lib/etassia/parser/builders/class_diagram.rb +539 -0
  553. data/lib/etassia/parser/builders/er_diagram.rb +308 -0
  554. data/lib/etassia/parser/builders/error.rb +51 -0
  555. data/lib/etassia/parser/builders/flowchart.rb +1150 -0
  556. data/lib/etassia/parser/builders/gantt.rb +267 -0
  557. data/lib/etassia/parser/builders/git_graph.rb +301 -0
  558. data/lib/etassia/parser/builders/info.rb +57 -0
  559. data/lib/etassia/parser/builders/kanban.rb +411 -0
  560. data/lib/etassia/parser/builders/mindmap.rb +271 -0
  561. data/lib/etassia/parser/builders/packet.rb +85 -0
  562. data/lib/etassia/parser/builders/pie.rb +105 -0
  563. data/lib/etassia/parser/builders/quadrant.rb +165 -0
  564. data/lib/etassia/parser/builders/radar.rb +157 -0
  565. data/lib/etassia/parser/builders/requirement.rb +230 -0
  566. data/lib/etassia/parser/builders/sankey.rb +142 -0
  567. data/lib/etassia/parser/builders/sequence.rb +431 -0
  568. data/lib/etassia/parser/builders/sequence_blocks.rb +117 -0
  569. data/lib/etassia/parser/builders/sequence_box_title.rb +57 -0
  570. data/lib/etassia/parser/builders/state_diagram.rb +364 -0
  571. data/lib/etassia/parser/builders/timeline.rb +133 -0
  572. data/lib/etassia/parser/builders/treemap.rb +138 -0
  573. data/lib/etassia/parser/builders/xy_chart.rb +148 -0
  574. data/lib/etassia/parser/c4.rb +37 -0
  575. data/lib/etassia/parser/class_diagram.rb +58 -0
  576. data/lib/etassia/parser/class_namespaces.rb +62 -0
  577. data/lib/etassia/parser/class_notes.rb +54 -0
  578. data/lib/etassia/parser/er_diagram.rb +28 -0
  579. data/lib/etassia/parser/error.rb +30 -0
  580. data/lib/etassia/parser/flowchart.rb +87 -0
  581. data/lib/etassia/parser/gantt.rb +43 -0
  582. data/lib/etassia/parser/git_graph.rb +106 -0
  583. data/lib/etassia/parser/grammars/architecture.rb +165 -0
  584. data/lib/etassia/parser/grammars/block.rb +190 -0
  585. data/lib/etassia/parser/grammars/c4.rb +229 -0
  586. data/lib/etassia/parser/grammars/class_diagram.rb +606 -0
  587. data/lib/etassia/parser/grammars/common.rb +107 -0
  588. data/lib/etassia/parser/grammars/er_diagram.rb +232 -0
  589. data/lib/etassia/parser/grammars/error.rb +32 -0
  590. data/lib/etassia/parser/grammars/flowchart.rb +1722 -0
  591. data/lib/etassia/parser/grammars/gantt.rb +198 -0
  592. data/lib/etassia/parser/grammars/git_graph.rb +234 -0
  593. data/lib/etassia/parser/grammars/info.rb +53 -0
  594. data/lib/etassia/parser/grammars/kanban.rb +417 -0
  595. data/lib/etassia/parser/grammars/mermaid_unicode_text.rb +108 -0
  596. data/lib/etassia/parser/grammars/mindmap.rb +220 -0
  597. data/lib/etassia/parser/grammars/packet.rb +71 -0
  598. data/lib/etassia/parser/grammars/pie.rb +128 -0
  599. data/lib/etassia/parser/grammars/quadrant.rb +202 -0
  600. data/lib/etassia/parser/grammars/radar.rb +158 -0
  601. data/lib/etassia/parser/grammars/requirement.rb +258 -0
  602. data/lib/etassia/parser/grammars/sankey.rb +104 -0
  603. data/lib/etassia/parser/grammars/sequence.rb +940 -0
  604. data/lib/etassia/parser/grammars/state_diagram.rb +382 -0
  605. data/lib/etassia/parser/grammars/timeline.rb +153 -0
  606. data/lib/etassia/parser/grammars/treemap.rb +122 -0
  607. data/lib/etassia/parser/grammars/xy_chart.rb +119 -0
  608. data/lib/etassia/parser/info.rb +32 -0
  609. data/lib/etassia/parser/kanban.rb +131 -0
  610. data/lib/etassia/parser/mermaid_shapes.rb +106 -0
  611. data/lib/etassia/parser/metadata_yaml.rb +452 -0
  612. data/lib/etassia/parser/mindmap.rb +92 -0
  613. data/lib/etassia/parser/native/builders/architecture.rb +103 -0
  614. data/lib/etassia/parser/native/builders/block.rb +68 -0
  615. data/lib/etassia/parser/native/builders/c4.rb +16 -0
  616. data/lib/etassia/parser/native/builders/class_diagram.rb +63 -0
  617. data/lib/etassia/parser/native/builders/er_diagram.rb +47 -0
  618. data/lib/etassia/parser/native/builders/flowchart.rb +58 -0
  619. data/lib/etassia/parser/native/builders/gantt.rb +35 -0
  620. data/lib/etassia/parser/native/builders/gitgraph.rb +35 -0
  621. data/lib/etassia/parser/native/builders/info.rb +30 -0
  622. data/lib/etassia/parser/native/builders/kanban.rb +37 -0
  623. data/lib/etassia/parser/native/builders/mindmap.rb +26 -0
  624. data/lib/etassia/parser/native/builders/packet.rb +63 -0
  625. data/lib/etassia/parser/native/builders/pie.rb +82 -0
  626. data/lib/etassia/parser/native/builders/quadrant.rb +27 -0
  627. data/lib/etassia/parser/native/builders/radar.rb +84 -0
  628. data/lib/etassia/parser/native/builders/requirement.rb +25 -0
  629. data/lib/etassia/parser/native/builders/sankey.rb +18 -0
  630. data/lib/etassia/parser/native/builders/sequence.rb +48 -0
  631. data/lib/etassia/parser/native/builders/state_diagram.rb +61 -0
  632. data/lib/etassia/parser/native/builders/timeline.rb +46 -0
  633. data/lib/etassia/parser/native/builders/treemap.rb +60 -0
  634. data/lib/etassia/parser/native/builders/user_journey.rb +83 -0
  635. data/lib/etassia/parser/native/builders/xy_chart.rb +94 -0
  636. data/lib/etassia/parser/native/declined.rb +12 -0
  637. data/lib/etassia/parser/native/mermaid.json +1 -0
  638. data/lib/etassia/parser/native/text.rb +31 -0
  639. data/lib/etassia/parser/native.rb +99 -0
  640. data/lib/etassia/parser/packet.rb +56 -0
  641. data/lib/etassia/parser/pie.rb +32 -0
  642. data/lib/etassia/parser/quadrant.rb +77 -0
  643. data/lib/etassia/parser/radar.rb +99 -0
  644. data/lib/etassia/parser/requirement.rb +32 -0
  645. data/lib/etassia/parser/sankey.rb +47 -0
  646. data/lib/etassia/parser/sequence.rb +34 -0
  647. data/lib/etassia/parser/state_diagram.rb +29 -0
  648. data/lib/etassia/parser/timeline.rb +40 -0
  649. data/lib/etassia/parser/treemap.rb +25 -0
  650. data/lib/etassia/parser/user_journey.rb +253 -0
  651. data/lib/etassia/parser/xy_chart.rb +98 -0
  652. data/lib/etassia/parser.rb +18 -0
  653. data/lib/etassia/renderer/architecture.rb +232 -0
  654. data/lib/etassia/renderer/architecture_edge_router.rb +379 -0
  655. data/lib/etassia/renderer/base.rb +295 -0
  656. data/lib/etassia/renderer/block.rb +157 -0
  657. data/lib/etassia/renderer/c4.rb +294 -0
  658. data/lib/etassia/renderer/class_diagram.rb +671 -0
  659. data/lib/etassia/renderer/er_diagram.rb +842 -0
  660. data/lib/etassia/renderer/error.rb +131 -0
  661. data/lib/etassia/renderer/flowchart.rb +315 -0
  662. data/lib/etassia/renderer/gantt.rb +177 -0
  663. data/lib/etassia/renderer/git_graph.rb +224 -0
  664. data/lib/etassia/renderer/info.rb +59 -0
  665. data/lib/etassia/renderer/kanban.rb +157 -0
  666. data/lib/etassia/renderer/line_break_text.rb +56 -0
  667. data/lib/etassia/renderer/markdown_text.rb +106 -0
  668. data/lib/etassia/renderer/mindmap.rb +290 -0
  669. data/lib/etassia/renderer/packet.rb +87 -0
  670. data/lib/etassia/renderer/pie.rb +113 -0
  671. data/lib/etassia/renderer/quadrant.rb +189 -0
  672. data/lib/etassia/renderer/radar.rb +191 -0
  673. data/lib/etassia/renderer/requirement.rb +209 -0
  674. data/lib/etassia/renderer/sankey.rb +114 -0
  675. data/lib/etassia/renderer/sequence/frame_drawing.rb +169 -0
  676. data/lib/etassia/renderer/sequence/note_drawing.rb +69 -0
  677. data/lib/etassia/renderer/sequence.rb +490 -0
  678. data/lib/etassia/renderer/state_diagram.rb +202 -0
  679. data/lib/etassia/renderer/timeline.rb +134 -0
  680. data/lib/etassia/renderer/timeline_palette.rb +18 -0
  681. data/lib/etassia/renderer/treemap/section_total.rb +33 -0
  682. data/lib/etassia/renderer/treemap.rb +128 -0
  683. data/lib/etassia/renderer/user_journey.rb +209 -0
  684. data/lib/etassia/renderer/xy_chart.rb +180 -0
  685. data/lib/etassia/renderer.rb +22 -0
  686. data/lib/etassia/runtime.rb +125 -0
  687. data/lib/etassia/source/frontmatter.rb +501 -0
  688. data/lib/etassia/source.rb +224 -0
  689. data/lib/etassia/svg/arrowhead.rb +205 -0
  690. data/lib/etassia/svg/circle.rb +19 -0
  691. data/lib/etassia/svg/document.rb +106 -0
  692. data/lib/etassia/svg/element.rb +209 -0
  693. data/lib/etassia/svg/ellipse.rb +23 -0
  694. data/lib/etassia/svg/escaping.rb +149 -0
  695. data/lib/etassia/svg/group.rb +64 -0
  696. data/lib/etassia/svg/line.rb +21 -0
  697. data/lib/etassia/svg/numbers.rb +53 -0
  698. data/lib/etassia/svg/path.rb +67 -0
  699. data/lib/etassia/svg/path_geometry.rb +378 -0
  700. data/lib/etassia/svg/polygon.rb +25 -0
  701. data/lib/etassia/svg/polyline.rb +28 -0
  702. data/lib/etassia/svg/rect.rb +34 -0
  703. data/lib/etassia/svg/style.rb +50 -0
  704. data/lib/etassia/svg/text.rb +181 -0
  705. data/lib/etassia/svg/tspan.rb +48 -0
  706. data/lib/etassia/svg.rb +38 -0
  707. data/lib/etassia/text_measurement/arial_advances.rb +122 -0
  708. data/lib/etassia/text_measurement/dejavu_advances.rb +111 -0
  709. data/lib/etassia/text_measurement/times_advances.rb +64 -0
  710. data/lib/etassia/text_measurement/trebuchet_advances.rb +53 -0
  711. data/lib/etassia/text_measurement/trebuchet_kerning.rb +29 -0
  712. data/lib/etassia/text_measurement.rb +198 -0
  713. data/lib/etassia/theme/builtin/dark.yml +229 -0
  714. data/lib/etassia/theme/builtin/default.yml +239 -0
  715. data/lib/etassia/theme/builtin/high_contrast.yml +231 -0
  716. data/lib/etassia/theme/builtin/light.yml +229 -0
  717. data/lib/etassia/theme/color_palette.rb +97 -0
  718. data/lib/etassia/theme/completion.rb +50 -0
  719. data/lib/etassia/theme/effect_styles.rb +34 -0
  720. data/lib/etassia/theme/registry.rb +45 -0
  721. data/lib/etassia/theme/shape_styles.rb +34 -0
  722. data/lib/etassia/theme/spacing_config.rb +30 -0
  723. data/lib/etassia/theme/typography.rb +40 -0
  724. data/lib/etassia/theme.rb +73 -0
  725. data/lib/etassia/version.rb +5 -0
  726. data/lib/etassia.rb +40 -0
  727. metadata +865 -0
@@ -0,0 +1,844 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../error"
4
+ require_relative "../../../error/diagram_type_error"
5
+ require_relative "../../../error/parse_error"
6
+ require_relative "../caption"
7
+ require_relative "chrome"
8
+ require_relative "../unsupported_construct_error"
9
+ require_relative "appearance"
10
+ require_relative "arrow_syntax"
11
+ require_relative "box"
12
+ require_relative "diagram"
13
+ require_relative "embedded"
14
+ require_relative "edge"
15
+ require_relative "fill"
16
+ require_relative "message"
17
+ require_relative "note"
18
+ require_relative "note_fill"
19
+ require_relative "outline"
20
+ require_relative "parallel_message"
21
+ require_relative "create"
22
+ require_relative "paint"
23
+ require_relative "participant"
24
+ require_relative "ref"
25
+ require_relative "refusals"
26
+ require_relative "style"
27
+
28
+ module Etassia
29
+ module Notation
30
+ module PlantUML
31
+ module Sequence
32
+ # Reads the PlantUML sequence subset: participant declarations,
33
+ # messages, notes, `return`, dividers and alt/loop/group blocks. A
34
+ # line outside it raises {UnsupportedConstructError}; never return a
35
+ # diagram with a line skipped.
36
+ class Parser
37
+ NAME = /[A-Za-z_][A-Za-z0-9_]*/
38
+ QUOTED = /"[^"\n]+"/
39
+ KINDS = %w[participant actor boundary control entity database
40
+ collections queue].freeze
41
+
42
+ COLOUR = /\#(\h{6}|\h{3}|red|green|blue|yellow|orange|purple|gray|
43
+ grey|cyan|magenta|lime|navy|teal|olive|maroon|silver|
44
+ aqua|fuchsia|pink|brown|black|white)(?!\w)/xi
45
+
46
+ DECLARATION = /\A(#{KINDS.join('|')})[ \t]+(#{QUOTED}|#{NAME})
47
+ (?:[ \t]+as[ \t]+(#{NAME}))?
48
+ (?:[ \t]+<<[ \t]*([^<>\n]+?)[ \t]*>>)?
49
+ (?:[ \t]+order[ \t]+(-?\d+))?
50
+ (?:[ \t]+(\#\w+))?\z/xio
51
+ SKINPARAM_WIDTH = /\Askinparam[ \t]+MinClassWidth[ \t]+(\d+)\z/i
52
+ MAXMESSAGE = /maxmessagesize[ \t]+(\d+)\z/i
53
+ SKINPARAM_MAXMESSAGE = /\Askinparam[ \t]+#{MAXMESSAGE}/i
54
+ SKINPARAM_LINE = /\A#{MAXMESSAGE}/i
55
+ SKINPARAM_PAINT = /\Askinparam[ \t]+(\w+)[ \t]+(\S+)\z/i
56
+ SKINPARAM_PAINT_LINE = /\A(\w+)[ \t]+(\S+)\z/
57
+ SKINPARAM_OPEN = /\Askinparam[ \t]*\{\z/i
58
+ HIDE_FOOTBOX = /\Ahide[ \t]+footbox\z/i
59
+ AUTOACTIVATE = /\Aautoactivate[ \t]+(on|off)\z/i
60
+ TITLE = /\A(?:title|header|footer|caption|legend)[ \t]+
61
+ (?!\S*(?:--|\.\.|->|<-))\S/xi
62
+ CHROME_OPEN = /\A(?:(title|header|footer|caption)|
63
+ legend(?:[ \t]+(top|bottom))?
64
+ (?:[ \t]+(left|center|right))?)\z/xi
65
+ CHROME_CLOSE = /\Aend[ \t]*(title|header|footer|caption|legend)\z/i
66
+ STYLE_OPEN = /\A<style>\z/i
67
+ STYLE_CLOSE = /\A<\/style>\z/i
68
+ MESSAGE = /\A(#{QUOTED}|#{NAME}|[\[?](?=[-<\\\/oxOX]))[ \t]*
69
+ (#{ArrowSyntax::SOURCE})
70
+ [ \t]*(#{QUOTED}|#{NAME}|(?<![ \t])[\]?])[ \t]*
71
+ (--\+\+|\+\+--|\+\+|--|!!)?[ \t]*(#{COLOUR})?
72
+ [ \t]*(?::[ \t]*(.*))?\z/xo
73
+ ACTIVATION = /\A(activate|deactivate)[ \t]+(#{QUOTED}|#{NAME})
74
+ (?:[ \t]+(#{COLOUR}))?\z/xio
75
+ CREATE = /\Acreate[ \t]+(.+)\z/i
76
+ DESTROY = /\Adestroy[ \t]+(#{QUOTED}|#{NAME})\z/io
77
+ MARKS = { "++" => [:on], "--" => [:off], "!!" => [:destroy],
78
+ "--++" => %i[off on], "++--" => %i[on off] }.freeze
79
+ TARGET = /(?:#{QUOTED}|#{NAME})/
80
+ NOTE = /\A(note|hnote|rnote)[ \t]+(left|right|over|across|top|bottom)
81
+ (?:[ \t]+of)?
82
+ (?:[ \t]+(#{TARGET}(?:[ \t]*,[ \t]*#{TARGET})*))?
83
+ (?:[ \t]+(\#\w+))?[ \t]*(?::[ \t]*(.*))?\z/xio
84
+ REF = /\Aref[ \t]+over[ \t]+(#{TARGET}(?:[ \t]*,[ \t]*#{TARGET})*)
85
+ [ \t]*:[ \t]*(\S.*)\z/xio
86
+ END_NOTE = /\Aend[ \t]*[hr]?note\z/i
87
+ BLOCK = /\A(alt|opt|loop|par|critical|break|group)
88
+ (?:[ \t]+(?:\#\w+[ \t]*)?(.*))?\z/xi
89
+ BRANCH = /\Aelse(?:[ \t]+(.*))?\z/i
90
+ RETURN = /\Areturn(?:[ \t]+(.*))?\z/i
91
+ DIVIDER = /\A==[ \t]*(.*?)[ \t]*==\z/
92
+ SPACING = /\A\|\|(?:(\d+)\|\||\|\|?)\z/
93
+ DELAY = /\A\.\.\.(?:[ \t]*(\S.*?)[ \t]*\.\.\.)?\z/
94
+ BOX = /\Abox(?:[ \t]+(#{QUOTED}))?(?:[ \t]+(\#\w+))?\z/io
95
+ END_BOX = /\A(?:endbox|end[ \t]+box)\z/i
96
+ PRAGMA = /\A!pragma[ \t]+(teoz|svginteractive)[ \t]+true\z/i
97
+ STARTUML = /\A@startuml(?![A-Za-z0-9_])/
98
+ LINE_END = /\r\n|\r|\n/
99
+ PARALLEL = /\A&[ \t]*(.*)\z/
100
+ NEWPAGE = /\Anewpage\z/i
101
+ AUTONUMBER = /\Aautonumber(?:[ \t]+(\d+)(?:[ \t]+(\d+))?)?
102
+ (?:[ \t]+"([^"]*)")?\z/xi
103
+ AUTONUMBER_PAUSE = /\Aautonumber[ \t]+(stop|resume)\z/i
104
+
105
+ PARALLEL_KINDS = [MESSAGE, NOTE, BLOCK].freeze
106
+ TIMELINE = [[MESSAGE, :message], [NOTE, :note], [BLOCK, :block],
107
+ [REF, :ref], [BRANCH, :branch], [ACTIVATION, :activation],
108
+ [RETURN, :reply], [DIVIDER, :divider], [DELAY, :delay],
109
+ [SPACING, :spacing],
110
+ [DESTROY, :destroy], [CREATE, :create],
111
+ [NEWPAGE, :newpage],
112
+ [AUTONUMBER, :autonumber],
113
+ [AUTONUMBER_PAUSE, :counting]].freeze
114
+ SETTINGS = [[SKINPARAM_WIDTH, :min_width],
115
+ [SKINPARAM_MAXMESSAGE, :max_message],
116
+ [SKINPARAM_PAINT, :skinparam_paint],
117
+ [SKINPARAM_OPEN, :open_skinparam],
118
+ [HIDE_FOOTBOX, :hide_footbox],
119
+ [AUTOACTIVATE, :autoactivate],
120
+ [CHROME_OPEN, :open_chrome],
121
+ [TITLE, :title],
122
+ [STYLE_OPEN, :open_style]].freeze
123
+
124
+ private_constant :TIMELINE, :SETTINGS, :NAME, :QUOTED, :KINDS,
125
+ :DECLARATION, :MESSAGE, :BOX, :END_BOX, :STARTUML,
126
+ :LINE_END, :PRAGMA, :ACTIVATION, :MARKS, :TARGET,
127
+ :NOTE, :END_NOTE, :REF, :BLOCK, :BRANCH, :RETURN,
128
+ :DIVIDER, :DELAY, :SPACING, :DESTROY, :CREATE,
129
+ :COLOUR,
130
+ :SKINPARAM_WIDTH, :STYLE_OPEN, :STYLE_CLOSE,
131
+ :PARALLEL, :SKINPARAM_PAINT, :SKINPARAM_PAINT_LINE,
132
+ :PARALLEL_KINDS, :HIDE_FOOTBOX, :NEWPAGE,
133
+ :AUTOACTIVATE, :AUTONUMBER,
134
+ :AUTONUMBER_PAUSE, :TITLE,
135
+ :CHROME_OPEN, :CHROME_CLOSE
136
+
137
+ # @param source [String] PlantUML source
138
+ # @return [Diagram] the frozen diagram
139
+ # @raise [UnsupportedConstructError] on the first line outside the
140
+ # subset
141
+ # @raise [Etassia::Parser::ParseError] when the source is not valid
142
+ # UTF-8 or @enduml is missing
143
+ def parse(source)
144
+ start_empty
145
+ phase = :before
146
+ lines_of(source).each_with_index do |line, index|
147
+ phase = step(phase, line.strip, index + 1)
148
+ end
149
+ finish(phase)
150
+ end
151
+
152
+ private
153
+
154
+ def start_empty
155
+ @participants = {}
156
+ @orders = {}
157
+ @outline = Outline.new
158
+ @boxes = []
159
+ @open_boxes = []
160
+ @pending_note = nil
161
+ @pending_style = nil
162
+ @skin_open = nil
163
+ start_settings
164
+ end
165
+
166
+ def start_settings
167
+ @appearance = Appearance.new
168
+ @teoz = false
169
+ @parallel = false
170
+ @footbox = true
171
+ @autoactivate = false
172
+ @title = nil
173
+ @chrome = Chrome::NONE
174
+ @pending_chrome = nil
175
+ @warnings = []
176
+ end
177
+
178
+ def lines_of(source)
179
+ text = source.dup.force_encoding(Encoding::UTF_8)
180
+ unless text.valid_encoding?
181
+ raise Etassia::Parser::ParseError,
182
+ "Parse error: source is not valid UTF-8"
183
+ end
184
+
185
+ text.delete_prefix("").split(LINE_END)
186
+ end
187
+
188
+ def step(phase, text, number)
189
+ @line = number
190
+ return phase if skippable?(text)
191
+
192
+ case phase
193
+ when :before then before(text, number)
194
+ when :statements then statement(text, number)
195
+ else raise refusal(text, number, "content after @enduml")
196
+ end
197
+ end
198
+
199
+ def skippable?(text)
200
+ return false if @pending_note || @pending_chrome
201
+
202
+ text.empty? || text.start_with?("'")
203
+ end
204
+
205
+ def before(text, _number)
206
+ # A name after @startuml only names PlantUML's output file.
207
+ return :statements if STARTUML.match?(text)
208
+
209
+ raise Etassia::Engine::DiagramTypeError,
210
+ "Unable to detect diagram type from source. " \
211
+ "Source must start with one of: @startuml"
212
+ end
213
+
214
+ def statement(text, number)
215
+ return collect_pending(text, number) if collecting?
216
+ return collect_skinparam(text, number) if @skin_open
217
+ return end_of_diagram(text, number) if text == "@enduml"
218
+ return pragma(PRAGMA.match(text)) if PRAGMA.match?(text)
219
+
220
+ read(text, number)
221
+ :statements
222
+ end
223
+
224
+ def collecting?
225
+ @pending_note || @pending_chrome || @pending_style
226
+ end
227
+
228
+ def collect_pending(text, number)
229
+ return collect_note(text) if @pending_note
230
+ return collect_chrome(text, number) if @pending_chrome
231
+
232
+ collect_style(text)
233
+ end
234
+
235
+ def pragma(match)
236
+ @teoz ||= match[1].casecmp?("teoz")
237
+ :statements
238
+ end
239
+
240
+ def read(text, number)
241
+ if (match = DECLARATION.match(text)) && declaration?(match)
242
+ declare(match)
243
+ elsif (match = BOX.match(text))
244
+ open_box(match)
245
+ else
246
+ read_setting(text, number)
247
+ end
248
+ end
249
+
250
+ def read_setting(text, number)
251
+ SETTINGS.each do |pattern, reader|
252
+ match = pattern.match(text)
253
+ return send(reader, match, number, text) if match
254
+ end
255
+ read_structure(text, number)
256
+ end
257
+
258
+ def min_width(match, *)
259
+ set(min_width: match[1].to_i)
260
+ end
261
+
262
+ def max_message(match, *)
263
+ set(max_message: match[1].to_i)
264
+ end
265
+
266
+ def skinparam_paint(match, number, text)
267
+ painted(match[1], match[2], text, number)
268
+ end
269
+
270
+ def painted(key, value, text, number)
271
+ read = Paint.read(key, value) or raise refusal(text, number)
272
+
273
+ @appearance = @appearance.merge(read)
274
+ end
275
+
276
+ def open_skinparam(_match, number, _text)
277
+ @skin_open = number
278
+ end
279
+
280
+ def hide_footbox(*)
281
+ @footbox = false
282
+ end
283
+
284
+ def autoactivate(match, *)
285
+ @autoactivate = match[1].casecmp?("on")
286
+ end
287
+
288
+ # Only plain text, as {Caption} reads it; markup stays refused.
289
+ def title(_match, number, text)
290
+ caption = Caption.read(text) or raise refusal(text, number)
291
+
292
+ keep(caption.kind, caption.text)
293
+ end
294
+
295
+ def keep(kind, text, place = nil)
296
+ if kind == :title
297
+ @title = text
298
+ else
299
+ @chrome = @chrome.with(kind, text, place || @chrome.legend_place)
300
+ end
301
+ end
302
+
303
+ def open_chrome(match, number, text)
304
+ kind = (match[1] || "legend").downcase.to_sym
305
+ place = [match[2] || "bottom", match[3] || "center"].join(" ")
306
+ @pending_chrome = { kind: kind, place: place.downcase,
307
+ line: number, text: text, lines: [] }
308
+ end
309
+
310
+ def collect_chrome(text, number)
311
+ raise unclosed_chrome if text == "@enduml"
312
+
313
+ if closes_chrome?(text)
314
+ close_chrome
315
+ else
316
+ chrome_line(text, number)
317
+ end
318
+ :statements
319
+ end
320
+
321
+ def closes_chrome?(text)
322
+ close = CHROME_CLOSE.match(text)
323
+ close && close[1].casecmp?(@pending_chrome[:kind].to_s)
324
+ end
325
+
326
+ def chrome_line(text, number)
327
+ kind = @pending_chrome[:kind].to_s
328
+ raise refusal(text, number, kind) unless Caption.plain?(text)
329
+
330
+ @pending_chrome[:lines] << text
331
+ end
332
+
333
+ def close_chrome
334
+ pending = @pending_chrome
335
+ @pending_chrome = nil
336
+ raise refusal(pending[:text], pending[:line]) if
337
+ pending[:lines].empty?
338
+
339
+ place = pending[:place] if pending[:kind] == :legend
340
+ keep(pending[:kind], pending[:lines].join("\n"), place)
341
+ end
342
+
343
+ def open_style(_match, number, text)
344
+ @pending_style = { line: number, text: text, lines: [] }
345
+ end
346
+
347
+ # A stereotype is drawn above the label, where the other kinds
348
+ # already draw their name. Only a participant takes a fill, in the
349
+ # forms {Fill} reads.
350
+ def declaration?(match)
351
+ participant = match[1].casecmp?("participant")
352
+ (match[4].nil? || participant) &&
353
+ (match[6].nil? || (participant && Fill.read(match[6])))
354
+ end
355
+
356
+ def collect_style(text)
357
+ if STYLE_CLOSE.match?(text)
358
+ close_style
359
+ else
360
+ @pending_style[:lines] << text
361
+ end
362
+ :statements
363
+ end
364
+
365
+ # Inside `skinparam {`, a line is `Maxmessagesize N`; anything
366
+ # else leaves the block unread.
367
+ def collect_skinparam(text, number)
368
+ if text == "}"
369
+ @skin_open = nil
370
+ elsif (match = SKINPARAM_LINE.match(text))
371
+ set(max_message: match[1].to_i)
372
+ else
373
+ block_paint(text, number)
374
+ end
375
+ :statements
376
+ end
377
+
378
+ def block_paint(text, number)
379
+ match = SKINPARAM_PAINT_LINE.match(text) or
380
+ raise refusal(text, number)
381
+
382
+ painted(match[1], match[2], text, number)
383
+ end
384
+
385
+ def close_style
386
+ style = @pending_style
387
+ @pending_style = nil
388
+ read = Style.read(style[:lines].join("\n"))
389
+ raise refusal(style[:text], style[:line]) unless read
390
+
391
+ @appearance = @appearance.merge(read)
392
+ end
393
+
394
+ def set(**settings)
395
+ @appearance = @appearance.merge(Appearance.new(**settings))
396
+ end
397
+
398
+ def read_structure(text, number)
399
+ if END_BOX.match?(text) && @open_boxes.any?
400
+ close_box
401
+ elsif @open_boxes.any? || !timeline(text)
402
+ raise refusal(text, number)
403
+ end
404
+ end
405
+
406
+ # Reads one line that belongs in the item sequence; false when it
407
+ # is not one, or when it is out of place.
408
+ def timeline(text)
409
+ parallel = PARALLEL.match(text)
410
+ return parallel_line(parallel[1]) if parallel
411
+
412
+ read_timeline(text)
413
+ end
414
+
415
+ # Only teoz draws `&` on the row above; without it PlantUML
416
+ # stacks the line as an ordinary one, so it stays refused.
417
+ def parallel_line(text)
418
+ return false unless @teoz && @outline.anchored?
419
+ return false unless PARALLEL_KINDS.any? { |kind| kind.match?(text) }
420
+
421
+ @parallel = true
422
+ read_timeline(text)
423
+ ensure
424
+ @parallel = false
425
+ end
426
+
427
+ def read_timeline(text)
428
+ TIMELINE.each do |pattern, reader|
429
+ match = pattern.match(text)
430
+ return send(reader, match) if match
431
+ end
432
+ text.casecmp?("end") && @outline.close_block
433
+ end
434
+
435
+ def block(match)
436
+ @outline.open_block(match[1].downcase, match[2].to_s.strip,
437
+ parallel: @parallel)
438
+ end
439
+
440
+ # A label with a line break is the multi-line form; not drawn.
441
+ def ref(match)
442
+ return false if match[2].include?("\\n")
443
+
444
+ @outline.ref(Ref.new(targets: targets_of(match[1]),
445
+ label: match[2].strip))
446
+ end
447
+
448
+ def branch(match)
449
+ @outline.branch(match[1])
450
+ end
451
+
452
+ def reply(match)
453
+ @outline.reply(match[1])
454
+ end
455
+
456
+ def activation(match)
457
+ phase = match[1].casecmp?("activate") ? :on : :off
458
+ return false if match[3] && phase == :off
459
+
460
+ @outline.activation(phase, mention(match[2]), colour(match[4]))
461
+ end
462
+
463
+ def destroy(match)
464
+ @outline.destroy(mention(match[1]))
465
+ end
466
+
467
+ # `create X` or `create participant X`: the head appears where the
468
+ # next message to it is.
469
+ def create(match)
470
+ declared = DECLARATION.match(match[1])
471
+ if declared && declaration?(declared)
472
+ declare(declared)
473
+ return @outline.create(declared[3] || unquote(declared[2]))
474
+ end
475
+ return false unless /\A(?:#{QUOTED}|#{NAME})\z/o.match?(match[1])
476
+
477
+ @outline.create(mention(match[1]))
478
+ end
479
+
480
+ def autonumber(match)
481
+ numbering = Numbering.read(start: (match[1] || 1).to_i,
482
+ step: (match[2] || 1).to_i,
483
+ format: match[3])
484
+ numbering && @outline.autonumber(numbering)
485
+ end
486
+
487
+ def counting(match)
488
+ @outline.counting(match[1].casecmp?("resume"))
489
+ end
490
+
491
+ def newpage(_match)
492
+ @outline.newpage
493
+ end
494
+
495
+ def divider(match)
496
+ @outline.divider(match[1])
497
+ end
498
+
499
+ def spacing(match)
500
+ @outline.spacing(match[1]&.to_f)
501
+ end
502
+
503
+ def delay(match)
504
+ @outline.delay(match[1].to_s)
505
+ end
506
+
507
+ def note(match)
508
+ pending = pending_note_from(match) or return false
509
+ return positioned_note(pending, match[5]) if misplaced?(pending)
510
+ return false if hanging?(pending) && !hangs_from_something?
511
+
512
+ @pending_note = pending
513
+ match[5] ? collect_note(match[5].strip, inline: true) : true
514
+ end
515
+
516
+ # PlantUML has no top or bottom note. After a message the note is
517
+ # drawn as a `left` one; after a ref it is dropped and a warning
518
+ # is drawn at the top instead. Only the one-line form is read.
519
+ def misplaced?(pending)
520
+ %i[top bottom].include?(pending[:side])
521
+ end
522
+
523
+ def positioned_note(pending, text)
524
+ return false unless text && pending[:targets].empty?
525
+ return false if @parallel
526
+
527
+ if @outline.after_message?
528
+ @pending_note = pending.merge(side: :left)
529
+ collect_note(text.strip, inline: true)
530
+ elsif @outline.after_ref?
531
+ @warnings << "This position is ignored: #{pending[:side].upcase}"
532
+ end
533
+ end
534
+
535
+ def hangs_from_something?
536
+ @outline.after_message? || @outline.after_block?
537
+ end
538
+
539
+ # nil when the colour is not one PlantUML is known to draw.
540
+ def pending_note_from(match)
541
+ fill = NoteFill.read(match[4])
542
+ return if match[4] && !fill
543
+
544
+ { shape: match[1].downcase.to_sym, side: match[2].downcase.to_sym,
545
+ targets: targets_of(match[3]), lines: [], parallel: @parallel,
546
+ fill: fill, depth: 0, line: @line }
547
+ end
548
+
549
+ def targets_of(list)
550
+ list.to_s.split(",").map { |target| mention(target.strip) }
551
+ end
552
+
553
+ def hanging?(pending)
554
+ pending[:targets].empty? && %i[left right].include?(pending[:side])
555
+ end
556
+
557
+ def collect_note(text, inline: false)
558
+ if inline || closes_note?(text)
559
+ finish_note(inline ? text : @pending_note[:lines].join("\n"))
560
+ else
561
+ track_embedded(text)
562
+ @pending_note[:lines] << text
563
+ end
564
+ :statements
565
+ end
566
+
567
+ # An `end note` inside `{{ }}` belongs to the embedded diagram.
568
+ def closes_note?(text)
569
+ END_NOTE.match?(text) && @pending_note[:depth].zero?
570
+ end
571
+
572
+ def track_embedded(text)
573
+ pending = @pending_note
574
+ pending[:depth] += 1 if Embedded.opens?(text)
575
+ return unless Embedded.closes?(text) && pending[:depth].positive?
576
+
577
+ pending[:depth] -= 1
578
+ end
579
+
580
+ # A body with a `{{` line is one embedded diagram and nothing
581
+ # else; its own lines are read by a parser of their own.
582
+ def check_embedded(text, line)
583
+ return unless text.lines.any? { |row| Embedded.opens?(row) }
584
+
585
+ embedded = Embedded.read(text)
586
+ unless embedded
587
+ raise UnsupportedConstructError.new(
588
+ construct: "embedded diagram beside other note text",
589
+ line: line, text: "{{"
590
+ )
591
+ end
592
+ parse_embedded(embedded, line)
593
+ end
594
+
595
+ def parse_embedded(embedded, line)
596
+ Parser.new.parse(embedded.document)
597
+ rescue UnsupportedConstructError => e
598
+ raise UnsupportedConstructError.new(
599
+ construct: "#{e.construct} in an embedded diagram",
600
+ line: line, text: "{{"
601
+ )
602
+ end
603
+
604
+ def finish_note(text)
605
+ pending = @pending_note
606
+ @pending_note = nil
607
+ check_embedded(text, pending[:line])
608
+ @outline.note(Note.new(shape: pending[:shape],
609
+ side: pending[:side],
610
+ targets: pending[:targets], text: text,
611
+ parallel: pending[:parallel],
612
+ fill: pending[:fill]))
613
+ end
614
+
615
+ # PlantUML does not draw a box inside a box: the inner one is drawn
616
+ # beside the outer one's own members, and an outer box with none is
617
+ # not drawn at all.
618
+ def open_box(match)
619
+ @open_boxes.last&.store(:nested, true)
620
+ @open_boxes << { title: unquote(match[1].to_s), members: [],
621
+ fill: Fill.read(match[2]) }
622
+ end
623
+
624
+ def close_box
625
+ box = @open_boxes.pop
626
+ return if box[:nested] && box[:members].empty?
627
+
628
+ @boxes << Box.new(title: box[:title], members: box[:members],
629
+ fill: box[:fill])
630
+ end
631
+
632
+ def end_of_diagram(text, number)
633
+ raise refusal(text, number, "empty diagram") if @participants.empty?
634
+ raise unclosed_box if @open_boxes.any?
635
+ raise unclosed_block if @outline.open_block?
636
+
637
+ :after
638
+ end
639
+
640
+ def declare(match)
641
+ display = unquote(match[2])
642
+ id = match[3] || display
643
+ @participants[id] ||= participant(match, id, display)
644
+ @orders[id] ||= match[5].to_i
645
+ @open_boxes.last[:members] << id if @open_boxes.any?
646
+ end
647
+
648
+ def participant(match, id, display)
649
+ Participant.new(
650
+ id: id, label: display, kind: match[1].downcase.to_sym,
651
+ stereotype: match[4], fill: Fill.read(match[6])
652
+ )
653
+ end
654
+
655
+ def message(match)
656
+ message = build_message(match) or return false
657
+ return false if unplaceable?(message, match[4])
658
+
659
+ marks = marks_of(match, message)
660
+ return false if match[5] && marks.none? { |phase,| phase == :on }
661
+
662
+ @outline.message(message, marks)
663
+ end
664
+
665
+ def build_message(match)
666
+ style, reversed = ArrowSyntax.read(match[2])
667
+ ends = [endpoint(match[1], :left), endpoint(match[3], :right)]
668
+ return unless style && edges_readable?(ends, reversed)
669
+
670
+ from, to = reversed ? ends.reverse : ends
671
+ kind = @parallel ? ParallelMessage : Message
672
+ kind.new(from: from, to: to, label: display_label(match[7]),
673
+ style: style)
674
+ end
675
+
676
+ # PlantUML treats doubled quotes around a message label as display
677
+ # delimiters. Backslashes inside that form use pair escaping; a
678
+ # single backslash and escaped quote remain content. Explicit line
679
+ # breaks must be decoded before deciding whether quotes enclose one
680
+ # display line.
681
+ def display_label(label)
682
+ family = "monospace" if label&.start_with?('""')
683
+ Message::Label.new(content: message_label(label),
684
+ font_family: family)
685
+ end
686
+
687
+ def message_label(label)
688
+ doubled_quotes = label&.start_with?('""') && label.end_with?('""')
689
+ return label unless doubled_quotes
690
+ return label if label.include?("\\n")
691
+
692
+ label[2...-2].strip.gsub("\\\\", "\\")
693
+ end
694
+
695
+ def endpoint(token, side)
696
+ Edge.read(token, side) || mention(token)
697
+ end
698
+
699
+ # At most one end is missing, and a diagram-wide edge only for the
700
+ # forms measured against PlantUML: written `[->` or `->]`.
701
+ def edges_readable?(ends, reversed)
702
+ edges = ends.grep(Edge)
703
+ edges.size < 2 && edges.none? { |edge| reversed && edge.global? }
704
+ end
705
+
706
+ # A `?` end has no ring in the slice, and a missing end cannot be
707
+ # activated or destroyed.
708
+ def unplaceable?(message, suffix)
709
+ edge = message.edge or return false
710
+
711
+ @autoactivate || !suffix.nil? ||
712
+ (edge.local? && message.edge_end.circle)
713
+ end
714
+
715
+ # `--` deactivates the sender, `++` activates the receiver and `!!`
716
+ # destroys it; a colour is the fill of the bar `++` opens.
717
+ def marks(suffix, message, colour)
718
+ MARKS.fetch(suffix, []).map do |phase|
719
+ [phase, phase == :off ? message.from : message.to,
720
+ phase == :on ? colour : nil]
721
+ end
722
+ end
723
+
724
+ def marks_of(match, message)
725
+ if @autoactivate && match[4].nil? && match[5].nil?
726
+ return automatic_marks(message)
727
+ end
728
+
729
+ marks(match[4], message, colour(match[6]))
730
+ end
731
+
732
+ # After `autoactivate on` a call activates its receiver and a
733
+ # dashed answer deactivates its sender, when it is active. A lost
734
+ # message (written `x`) does neither. A message to the edge is
735
+ # refused: the slice cannot activate the participant it names.
736
+ def automatic_marks(message)
737
+ return [] if message.style.head.glyph == :cross
738
+ return [[:on, message.to, nil]] unless message.dashed
739
+
740
+ active = @outline.active?(message.from)
741
+ active ? [[:off, message.from, nil]] : []
742
+ end
743
+
744
+ def colour(token)
745
+ return unless token
746
+
747
+ token.match?(/\A\h+\z/) ? "##{token}" : token.downcase
748
+ end
749
+
750
+ def mention(name)
751
+ id = unquote(name)
752
+ @participants[id] ||= Participant.new(id: id)
753
+ id
754
+ end
755
+
756
+ def unquote(name)
757
+ name.delete_prefix('"').delete_suffix('"')
758
+ end
759
+
760
+ def finish(phase)
761
+ if phase == :before
762
+ raise Etassia::Engine::DiagramTypeError,
763
+ "Unable to detect diagram type from source. " \
764
+ "Source must start with one of: @startuml"
765
+ end
766
+ return diagram if phase == :after
767
+
768
+ raise Etassia::Parser::ParseError,
769
+ "Parse error: missing @enduml before the end of the source"
770
+ end
771
+
772
+ def diagram
773
+ ordered_participants
774
+ check_boxes
775
+ check_numbering
776
+ Diagram.new(participants: @participants.values.freeze,
777
+ items: @outline.items.freeze, boxes: @boxes.freeze,
778
+ appearance: @appearance, footbox: @footbox,
779
+ warnings: @warnings.freeze, title: @title,
780
+ chrome: @chrome, teoz: @teoz)
781
+ end
782
+
783
+ # `order N` seats a participant left to right by N; one with none
784
+ # counts as 0, and equal numbers keep the order they were met in.
785
+ def ordered_participants
786
+ ranked = @participants.each_with_index.sort_by do |(id, _), index|
787
+ [@orders.fetch(id, 0), index]
788
+ end
789
+ @participants = ranked.to_h(&:first)
790
+ end
791
+
792
+ # The number of a wrapped label would sit on one of its lines,
793
+ # which has not been measured.
794
+ def check_numbering
795
+ return unless @appearance.max_message
796
+ return unless @outline.items.grep(Message).any?(&:number)
797
+
798
+ raise Etassia::Parser::ParseError,
799
+ "Parse error: autonumber with Maxmessagesize is not supported"
800
+ end
801
+
802
+ def unclosed_block
803
+ Etassia::Parser::ParseError.new(
804
+ "Parse error: a block is never closed with end",
805
+ )
806
+ end
807
+
808
+ def unclosed_chrome
809
+ kind = @pending_chrome[:kind]
810
+ Etassia::Parser::ParseError.new(
811
+ "Parse error: #{kind} is never closed with end #{kind}",
812
+ )
813
+ end
814
+
815
+ def unclosed_box
816
+ Etassia::Parser::ParseError.new(
817
+ "Parse error: box #{@open_boxes.last[:title].inspect} is never " \
818
+ "closed with endbox",
819
+ )
820
+ end
821
+
822
+ def check_boxes
823
+ ids = @participants.keys
824
+ @boxes.each do |box|
825
+ places = box.members.map { |id| ids.index(id) }.sort
826
+ next if places.each_cons(2).all? { |a, b| b == a + 1 }
827
+
828
+ raise Etassia::Parser::ParseError,
829
+ "Parse error: participants of box #{box.title.inspect} " \
830
+ "are not neighbours, which is not supported"
831
+ end
832
+ end
833
+
834
+ def refusal(text, number, construct = nil)
835
+ UnsupportedConstructError.new(
836
+ construct: construct || Refusals.name_for(text),
837
+ line: number, text: text
838
+ )
839
+ end
840
+ end
841
+ end
842
+ end
843
+ end
844
+ end