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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +140 -0
- data/LICENSE +25 -0
- data/README.adoc +436 -0
- data/exe/etassia +7 -0
- data/lib/etassia/cli.rb +187 -0
- data/lib/etassia/commands/batch.rb +210 -0
- data/lib/etassia/commands/notation_loader.rb +22 -0
- data/lib/etassia/commands/render.rb +98 -0
- data/lib/etassia/commands/types.rb +33 -0
- data/lib/etassia/commands/version.rb +24 -0
- data/lib/etassia/corpus_type_names.rb +52 -0
- data/lib/etassia/diagram/architecture.rb +108 -0
- data/lib/etassia/diagram/base.rb +61 -0
- data/lib/etassia/diagram/block.rb +102 -0
- data/lib/etassia/diagram/c4.rb +337 -0
- data/lib/etassia/diagram/class_diagram.rb +466 -0
- data/lib/etassia/diagram/class_namespace.rb +17 -0
- data/lib/etassia/diagram/class_note.rb +17 -0
- data/lib/etassia/diagram/containment.rb +107 -0
- data/lib/etassia/diagram/er_diagram.rb +317 -0
- data/lib/etassia/diagram/error.rb +38 -0
- data/lib/etassia/diagram/flowchart.rb +419 -0
- data/lib/etassia/diagram/flowchart_label_text.rb +63 -0
- data/lib/etassia/diagram/gantt.rb +94 -0
- data/lib/etassia/diagram/generic_text.rb +30 -0
- data/lib/etassia/diagram/git_graph.rb +57 -0
- data/lib/etassia/diagram/info.rb +38 -0
- data/lib/etassia/diagram/kanban.rb +198 -0
- data/lib/etassia/diagram/mindmap.rb +71 -0
- data/lib/etassia/diagram/packet.rb +93 -0
- data/lib/etassia/diagram/pie.rb +115 -0
- data/lib/etassia/diagram/quadrant.rb +135 -0
- data/lib/etassia/diagram/radar.rb +65 -0
- data/lib/etassia/diagram/requirement.rb +168 -0
- data/lib/etassia/diagram/sankey.rb +217 -0
- data/lib/etassia/diagram/sequence.rb +299 -0
- data/lib/etassia/diagram/sequence_box.rb +18 -0
- data/lib/etassia/diagram/sequence_frame.rb +37 -0
- data/lib/etassia/diagram/sequence_frame_section.rb +23 -0
- data/lib/etassia/diagram/sequence_text.rb +69 -0
- data/lib/etassia/diagram/sequence_wrap.rb +23 -0
- data/lib/etassia/diagram/state_diagram.rb +282 -0
- data/lib/etassia/diagram/timeline.rb +183 -0
- data/lib/etassia/diagram/treemap.rb +95 -0
- data/lib/etassia/diagram/user_journey.rb +153 -0
- data/lib/etassia/diagram/xy_chart.rb +90 -0
- data/lib/etassia/diagram.rb +8 -0
- data/lib/etassia/diagram_registry.rb +34 -0
- data/lib/etassia/engine.rb +334 -0
- data/lib/etassia/error/diagram_type_error.rb +10 -0
- data/lib/etassia/error/layout_error.rb +12 -0
- data/lib/etassia/error/notation_registration_error.rb +10 -0
- data/lib/etassia/error/parse_error.rb +11 -0
- data/lib/etassia/error/pipeline_error.rb +10 -0
- data/lib/etassia/error/render_error.rb +12 -0
- data/lib/etassia/error/score_error.rb +12 -0
- data/lib/etassia/error.rb +10 -0
- data/lib/etassia/error_report.rb +69 -0
- data/lib/etassia/ir/data.rb +39 -0
- data/lib/etassia/ir/data_value.rb +35 -0
- data/lib/etassia/ir/dimension.rb +10 -0
- data/lib/etassia/ir/edge.rb +34 -0
- data/lib/etassia/ir/graph.rb +28 -0
- data/lib/etassia/ir/integrity.rb +80 -0
- data/lib/etassia/ir/item.rb +46 -0
- data/lib/etassia/ir/model.rb +56 -0
- data/lib/etassia/ir/node.rb +10 -0
- data/lib/etassia/ir/placement.rb +47 -0
- data/lib/etassia/ir/prepositioned.rb +25 -0
- data/lib/etassia/ir/prepositioned_item.rb +26 -0
- data/lib/etassia/ir/property_set.rb +43 -0
- data/lib/etassia/ir/scalar.rb +25 -0
- data/lib/etassia/ir/series.rb +10 -0
- data/lib/etassia/ir.rb +17 -0
- data/lib/etassia/js_math.rb +511 -0
- data/lib/etassia/js_number.rb +64 -0
- data/lib/etassia/layout/architecture.rb +1472 -0
- data/lib/etassia/layout/base.rb +276 -0
- data/lib/etassia/layout/block.rb +738 -0
- data/lib/etassia/layout/c4.rb +876 -0
- data/lib/etassia/layout/c4_boundary_metrics.rb +35 -0
- data/lib/etassia/layout/c4_bounds.rb +106 -0
- data/lib/etassia/layout/c4_intersection.rb +77 -0
- data/lib/etassia/layout/c4_placement.rb +135 -0
- data/lib/etassia/layout/c4_shape_metrics.rb +97 -0
- data/lib/etassia/layout/c4_shape_outline.rb +67 -0
- data/lib/etassia/layout/c4_stereotype.rb +30 -0
- data/lib/etassia/layout/c4_text.rb +54 -0
- data/lib/etassia/layout/class_cell_placement.rb +148 -0
- data/lib/etassia/layout/class_diagram.rb +1116 -0
- data/lib/etassia/layout/class_diagram_namespaces.rb +26 -0
- data/lib/etassia/layout/class_diagram_notes.rb +26 -0
- data/lib/etassia/layout/class_name_advances.rb +31 -0
- data/lib/etassia/layout/class_namespace_box.rb +21 -0
- data/lib/etassia/layout/class_namespace_nodes.rb +197 -0
- data/lib/etassia/layout/class_namespace_placement.rb +71 -0
- data/lib/etassia/layout/class_note.rb +44 -0
- data/lib/etassia/layout/class_note_nodes.rb +84 -0
- data/lib/etassia/layout/elk_notices.rb +48 -0
- data/lib/etassia/layout/elk_placement.rb +197 -0
- data/lib/etassia/layout/er_diagram.rb +1002 -0
- data/lib/etassia/layout/er_diagram_adjacency.rb +20 -0
- data/lib/etassia/layout/er_diagram_alignment.rb +72 -0
- data/lib/etassia/layout/er_diagram_compaction.rb +85 -0
- data/lib/etassia/layout/er_diagram_conflicts.rb +70 -0
- data/lib/etassia/layout/er_diagram_coordinates.rb +104 -0
- data/lib/etassia/layout/er_diagram_entity_size.rb +82 -0
- data/lib/etassia/layout/er_diagram_layering.rb +109 -0
- data/lib/etassia/layout/er_diagram_ordering.rb +143 -0
- data/lib/etassia/layout/er_diagram_placement.rb +163 -0
- data/lib/etassia/layout/er_diagram_placement_node.rb +22 -0
- data/lib/etassia/layout/error.rb +133 -0
- data/lib/etassia/layout/flowchart/brandes_koepf.rb +208 -0
- data/lib/etassia/layout/flowchart/cluster_reach.rb +70 -0
- data/lib/etassia/layout/flowchart/component_packing.rb +129 -0
- data/lib/etassia/layout/flowchart/directed_placement.rb +428 -0
- data/lib/etassia/layout/flowchart/edge_chains.rb +111 -0
- data/lib/etassia/layout/flowchart/edge_order.rb +34 -0
- data/lib/etassia/layout/flowchart/edge_router.rb +533 -0
- data/lib/etassia/layout/flowchart/label_backdrop_geometry.rb +62 -0
- data/lib/etassia/layout/flowchart/layer_spacing.rb +85 -0
- data/lib/etassia/layout/flowchart/level_edges.rb +74 -0
- data/lib/etassia/layout/flowchart/level_frame.rb +47 -0
- data/lib/etassia/layout/flowchart/minimum_rank_spacing.rb +74 -0
- data/lib/etassia/layout/flowchart/row_centering.rb +229 -0
- data/lib/etassia/layout/flowchart.rb +1290 -0
- data/lib/etassia/layout/gantt.rb +814 -0
- data/lib/etassia/layout/gantt_duration.rb +49 -0
- data/lib/etassia/layout/gantt_exclusions.rb +74 -0
- data/lib/etassia/layout/gantt_ticks.rb +165 -0
- data/lib/etassia/layout/git_graph.rb +1075 -0
- data/lib/etassia/layout/grid.rb +165 -0
- data/lib/etassia/layout/info.rb +113 -0
- data/lib/etassia/layout/kanban.rb +452 -0
- data/lib/etassia/layout/kanban_card_text.rb +95 -0
- data/lib/etassia/layout/mindmap.rb +425 -0
- data/lib/etassia/layout/mindmap_cose.rb +81 -0
- data/lib/etassia/layout/mindmap_cose_body.rb +93 -0
- data/lib/etassia/layout/mindmap_cose_geometry.rb +190 -0
- data/lib/etassia/layout/mindmap_cose_grid.rb +96 -0
- data/lib/etassia/layout/mindmap_cose_link.rb +42 -0
- data/lib/etassia/layout/mindmap_cose_relaxation.rb +166 -0
- data/lib/etassia/layout/mindmap_cose_seed.rb +93 -0
- data/lib/etassia/layout/mindmap_cose_wedge.rb +37 -0
- data/lib/etassia/layout/mindmap_node_size.rb +130 -0
- data/lib/etassia/layout/packet.rb +285 -0
- data/lib/etassia/layout/pie.rb +305 -0
- data/lib/etassia/layout/pie_legend.rb +64 -0
- data/lib/etassia/layout/pie_legend_entry.rb +21 -0
- data/lib/etassia/layout/quadrant.rb +402 -0
- data/lib/etassia/layout/radar.rb +442 -0
- data/lib/etassia/layout/requirement.rb +765 -0
- data/lib/etassia/layout/sankey.rb +501 -0
- data/lib/etassia/layout/scene.rb +18 -0
- data/lib/etassia/layout/sequence/actor_gaps.rb +151 -0
- data/lib/etassia/layout/sequence/box_shape.rb +23 -0
- data/lib/etassia/layout/sequence/creations.rb +73 -0
- data/lib/etassia/layout/sequence/frame_divider.rb +22 -0
- data/lib/etassia/layout/sequence/frame_placement.rb +424 -0
- data/lib/etassia/layout/sequence/frame_reader.rb +75 -0
- data/lib/etassia/layout/sequence/frame_shape.rb +45 -0
- data/lib/etassia/layout/sequence/geometry.rb +24 -0
- data/lib/etassia/layout/sequence/message_rows.rb +165 -0
- data/lib/etassia/layout/sequence/note.rb +16 -0
- data/lib/etassia/layout/sequence/note_box.rb +114 -0
- data/lib/etassia/layout/sequence/note_placement.rb +223 -0
- data/lib/etassia/layout/sequence/text_width.rb +69 -0
- data/lib/etassia/layout/sequence/text_wrap.rb +93 -0
- data/lib/etassia/layout/sequence.rb +936 -0
- data/lib/etassia/layout/state_diagram.rb +1277 -0
- data/lib/etassia/layout/timeline.rb +165 -0
- data/lib/etassia/layout/timeline_card.rb +142 -0
- data/lib/etassia/layout/timeline_flow.rb +164 -0
- data/lib/etassia/layout/timeline_line.rb +33 -0
- data/lib/etassia/layout/timeline_rounding.rb +19 -0
- data/lib/etassia/layout/timeline_text.rb +105 -0
- data/lib/etassia/layout/treemap.rb +303 -0
- data/lib/etassia/layout/treemap_placement.rb +52 -0
- data/lib/etassia/layout/treemap_squarify.rb +108 -0
- data/lib/etassia/layout/user_journey.rb +300 -0
- data/lib/etassia/layout/user_journey_face.rb +33 -0
- data/lib/etassia/layout/user_journey_face_geometry.rb +80 -0
- data/lib/etassia/layout/user_journey_geometry.rb +336 -0
- data/lib/etassia/layout/user_journey_legend.rb +84 -0
- data/lib/etassia/layout/xy_chart.rb +620 -0
- data/lib/etassia/layout.rb +19 -0
- data/lib/etassia/lint_debt_scoreboard.rb +219 -0
- data/lib/etassia/markdown_text/emphasis_simulator.rb +373 -0
- data/lib/etassia/markdown_text.rb +393 -0
- data/lib/etassia/notation/builtin.rb +39 -0
- data/lib/etassia/notation/entry.rb +15 -0
- data/lib/etassia/notation/mermaid/class_diagram_config.rb +42 -0
- data/lib/etassia/notation/mermaid/git_graph_config.rb +46 -0
- data/lib/etassia/notation/mermaid/ir_adapter.rb +27 -0
- data/lib/etassia/notation/mermaid/ir_adapters/architecture.rb +196 -0
- data/lib/etassia/notation/mermaid/ir_adapters/block.rb +138 -0
- data/lib/etassia/notation/mermaid/ir_adapters/c4.rb +258 -0
- data/lib/etassia/notation/mermaid/ir_adapters/class_diagram.rb +257 -0
- data/lib/etassia/notation/mermaid/ir_adapters/er_diagram.rb +220 -0
- data/lib/etassia/notation/mermaid/ir_adapters/error.rb +32 -0
- data/lib/etassia/notation/mermaid/ir_adapters/flowchart.rb +208 -0
- data/lib/etassia/notation/mermaid/ir_adapters/gantt.rb +236 -0
- data/lib/etassia/notation/mermaid/ir_adapters/git_graph.rb +186 -0
- data/lib/etassia/notation/mermaid/ir_adapters/info.rb +34 -0
- data/lib/etassia/notation/mermaid/ir_adapters/kanban.rb +110 -0
- data/lib/etassia/notation/mermaid/ir_adapters/mindmap.rb +76 -0
- data/lib/etassia/notation/mermaid/ir_adapters/packet.rb +51 -0
- data/lib/etassia/notation/mermaid/ir_adapters/pie.rb +71 -0
- data/lib/etassia/notation/mermaid/ir_adapters/quadrant.rb +116 -0
- data/lib/etassia/notation/mermaid/ir_adapters/radar.rb +142 -0
- data/lib/etassia/notation/mermaid/ir_adapters/requirement.rb +237 -0
- data/lib/etassia/notation/mermaid/ir_adapters/sankey.rb +74 -0
- data/lib/etassia/notation/mermaid/ir_adapters/sequence.rb +304 -0
- data/lib/etassia/notation/mermaid/ir_adapters/state_diagram.rb +225 -0
- data/lib/etassia/notation/mermaid/ir_adapters/timeline.rb +93 -0
- data/lib/etassia/notation/mermaid/ir_adapters/treemap.rb +143 -0
- data/lib/etassia/notation/mermaid/ir_adapters/user_journey.rb +127 -0
- data/lib/etassia/notation/mermaid/ir_adapters/xychart.rb +131 -0
- data/lib/etassia/notation/mermaid.rb +399 -0
- data/lib/etassia/notation/parsed.rb +31 -0
- data/lib/etassia/notation/plantuml/activity/arrow_head.rb +45 -0
- data/lib/etassia/notation/plantuml/activity/arrow_style.rb +53 -0
- data/lib/etassia/notation/plantuml/activity/bar_columns.rb +75 -0
- data/lib/etassia/notation/plantuml/activity/box.rb +60 -0
- data/lib/etassia/notation/plantuml/activity/builder.rb +77 -0
- data/lib/etassia/notation/plantuml/activity/canvas.rb +117 -0
- data/lib/etassia/notation/plantuml/activity/column_centres.rb +151 -0
- data/lib/etassia/notation/plantuml/activity/columns.rb +224 -0
- data/lib/etassia/notation/plantuml/activity/diagram.rb +35 -0
- data/lib/etassia/notation/plantuml/activity/edge.rb +35 -0
- data/lib/etassia/notation/plantuml/activity/edge_marks.rb +68 -0
- data/lib/etassia/notation/plantuml/activity/exits.rb +61 -0
- data/lib/etassia/notation/plantuml/activity/frame_marks.rb +116 -0
- data/lib/etassia/notation/plantuml/activity/lane_steps.rb +82 -0
- data/lib/etassia/notation/plantuml/activity/lane_titles.rb +46 -0
- data/lib/etassia/notation/plantuml/activity/layout.rb +50 -0
- data/lib/etassia/notation/plantuml/activity/legacy_parser.rb +181 -0
- data/lib/etassia/notation/plantuml/activity/legacy_row_gaps.rb +65 -0
- data/lib/etassia/notation/plantuml/activity/mark.rb +23 -0
- data/lib/etassia/notation/plantuml/activity/marks.rb +62 -0
- data/lib/etassia/notation/plantuml/activity/metrics.rb +169 -0
- data/lib/etassia/notation/plantuml/activity/new_parser/blocks.rb +166 -0
- data/lib/etassia/notation/plantuml/activity/new_parser.rb +177 -0
- data/lib/etassia/notation/plantuml/activity/node.rb +50 -0
- data/lib/etassia/notation/plantuml/activity/node_marks.rb +174 -0
- data/lib/etassia/notation/plantuml/activity/note.rb +27 -0
- data/lib/etassia/notation/plantuml/activity/parser.rb +51 -0
- data/lib/etassia/notation/plantuml/activity/placement.rb +327 -0
- data/lib/etassia/notation/plantuml/activity/ranking.rb +142 -0
- data/lib/etassia/notation/plantuml/activity/reach.rb +58 -0
- data/lib/etassia/notation/plantuml/activity/renderer.rb +220 -0
- data/lib/etassia/notation/plantuml/activity/route.rb +22 -0
- data/lib/etassia/notation/plantuml/activity/router.rb +308 -0
- data/lib/etassia/notation/plantuml/activity/row_gaps.rb +105 -0
- data/lib/etassia/notation/plantuml/activity/row_spread.rb +59 -0
- data/lib/etassia/notation/plantuml/activity/scene.rb +20 -0
- data/lib/etassia/notation/plantuml/activity/text.rb +36 -0
- data/lib/etassia/notation/plantuml/activity.rb +50 -0
- data/lib/etassia/notation/plantuml/arrow.rb +135 -0
- data/lib/etassia/notation/plantuml/caption.rb +50 -0
- data/lib/etassia/notation/plantuml/caption_layout.rb +97 -0
- data/lib/etassia/notation/plantuml/caption_rows.rb +67 -0
- data/lib/etassia/notation/plantuml/class_header.rb +178 -0
- data/lib/etassia/notation/plantuml/class_name.rb +51 -0
- data/lib/etassia/notation/plantuml/component.rb +37 -0
- data/lib/etassia/notation/plantuml/creole.rb +278 -0
- data/lib/etassia/notation/plantuml/creole_icons.rb +33 -0
- data/lib/etassia/notation/plantuml/diagram.rb +60 -0
- data/lib/etassia/notation/plantuml/diagram_builder.rb +442 -0
- data/lib/etassia/notation/plantuml/diagram_name.rb +51 -0
- data/lib/etassia/notation/plantuml/directives.rb +33 -0
- data/lib/etassia/notation/plantuml/drawing/box.rb +28 -0
- data/lib/etassia/notation/plantuml/drawing/face.rb +39 -0
- data/lib/etassia/notation/plantuml/drawing/pen.rb +105 -0
- data/lib/etassia/notation/plantuml/drawing/renderer.rb +36 -0
- data/lib/etassia/notation/plantuml/drawing/scene.rb +28 -0
- data/lib/etassia/notation/plantuml/drawing.rb +19 -0
- data/lib/etassia/notation/plantuml/ebnf/blocks.rb +272 -0
- data/lib/etassia/notation/plantuml/ebnf/layout.rb +117 -0
- data/lib/etassia/notation/plantuml/ebnf/model.rb +22 -0
- data/lib/etassia/notation/plantuml/ebnf/reader.rb +131 -0
- data/lib/etassia/notation/plantuml/ebnf.rb +34 -0
- data/lib/etassia/notation/plantuml/end_texts.rb +78 -0
- data/lib/etassia/notation/plantuml/entity.rb +54 -0
- data/lib/etassia/notation/plantuml/further.rb +38 -0
- data/lib/etassia/notation/plantuml/gantt/layout.rb +269 -0
- data/lib/etassia/notation/plantuml/gantt/reader.rb +210 -0
- data/lib/etassia/notation/plantuml/gantt/renderer.rb +81 -0
- data/lib/etassia/notation/plantuml/gantt.rb +34 -0
- data/lib/etassia/notation/plantuml/include/blocks.rb +66 -0
- data/lib/etassia/notation/plantuml/include/finder.rb +61 -0
- data/lib/etassia/notation/plantuml/include.rb +78 -0
- data/lib/etassia/notation/plantuml/ir_adapter/annotations.rb +40 -0
- data/lib/etassia/notation/plantuml/ir_adapter/links.rb +91 -0
- data/lib/etassia/notation/plantuml/ir_adapter/sink.rb +49 -0
- data/lib/etassia/notation/plantuml/ir_adapter/structure.rb +86 -0
- data/lib/etassia/notation/plantuml/ir_adapter.rb +32 -0
- data/lib/etassia/notation/plantuml/ir_reader/annotations.rb +51 -0
- data/lib/etassia/notation/plantuml/ir_reader/index.rb +49 -0
- data/lib/etassia/notation/plantuml/ir_reader/links.rb +133 -0
- data/lib/etassia/notation/plantuml/ir_reader/structure.rb +83 -0
- data/lib/etassia/notation/plantuml/ir_reader.rb +42 -0
- data/lib/etassia/notation/plantuml/junction.rb +20 -0
- data/lib/etassia/notation/plantuml/klass.rb +41 -0
- data/lib/etassia/notation/plantuml/label_clearance.rb +67 -0
- data/lib/etassia/notation/plantuml/layout.rb +2806 -0
- data/lib/etassia/notation/plantuml/member.rb +37 -0
- data/lib/etassia/notation/plantuml/member_icon.rb +87 -0
- data/lib/etassia/notation/plantuml/member_icon_elements.rb +91 -0
- data/lib/etassia/notation/plantuml/member_pitch.rb +76 -0
- data/lib/etassia/notation/plantuml/member_rule.rb +48 -0
- data/lib/etassia/notation/plantuml/network/grid.rb +170 -0
- data/lib/etassia/notation/plantuml/network/layout.rb +158 -0
- data/lib/etassia/notation/plantuml/network/model.rb +22 -0
- data/lib/etassia/notation/plantuml/network/placement.rb +64 -0
- data/lib/etassia/notation/plantuml/network/reader.rb +126 -0
- data/lib/etassia/notation/plantuml/network/shapes.rb +109 -0
- data/lib/etassia/notation/plantuml/network.rb +37 -0
- data/lib/etassia/notation/plantuml/node_graph/archimate.rb +49 -0
- data/lib/etassia/notation/plantuml/node_graph/box_reader.rb +86 -0
- data/lib/etassia/notation/plantuml/node_graph/cloud.rb +73 -0
- data/lib/etassia/notation/plantuml/node_graph/crows_foot.rb +85 -0
- data/lib/etassia/notation/plantuml/node_graph/curve.rb +66 -0
- data/lib/etassia/notation/plantuml/node_graph/data_reader.rb +77 -0
- data/lib/etassia/notation/plantuml/node_graph/data_shape.rb +39 -0
- data/lib/etassia/notation/plantuml/node_graph/detour.rb +98 -0
- data/lib/etassia/notation/plantuml/node_graph/edge.rb +26 -0
- data/lib/etassia/notation/plantuml/node_graph/entity_metrics.rb +49 -0
- data/lib/etassia/notation/plantuml/node_graph/entity_reader.rb +65 -0
- data/lib/etassia/notation/plantuml/node_graph/entity_shape.rb +111 -0
- data/lib/etassia/notation/plantuml/node_graph/figures.rb +92 -0
- data/lib/etassia/notation/plantuml/node_graph/frame_ports.rb +73 -0
- data/lib/etassia/notation/plantuml/node_graph/graph.rb +40 -0
- data/lib/etassia/notation/plantuml/node_graph/heads.rb +89 -0
- data/lib/etassia/notation/plantuml/node_graph/label_fan.rb +76 -0
- data/lib/etassia/notation/plantuml/node_graph/label_placer.rb +310 -0
- data/lib/etassia/notation/plantuml/node_graph/label_waypoint.rb +47 -0
- data/lib/etassia/notation/plantuml/node_graph/layout.rb +293 -0
- data/lib/etassia/notation/plantuml/node_graph/link.rb +78 -0
- data/lib/etassia/notation/plantuml/node_graph/macros.rb +39 -0
- data/lib/etassia/notation/plantuml/node_graph/metrics.rb +113 -0
- data/lib/etassia/notation/plantuml/node_graph/node.rb +44 -0
- data/lib/etassia/notation/plantuml/node_graph/note_tail.rb +58 -0
- data/lib/etassia/notation/plantuml/node_graph/packer.rb +126 -0
- data/lib/etassia/notation/plantuml/node_graph/parser.rb +309 -0
- data/lib/etassia/notation/plantuml/node_graph/paths.rb +48 -0
- data/lib/etassia/notation/plantuml/node_graph/ranking.rb +231 -0
- data/lib/etassia/notation/plantuml/node_graph/reader.rb +53 -0
- data/lib/etassia/notation/plantuml/node_graph/references.rb +38 -0
- data/lib/etassia/notation/plantuml/node_graph/renderer.rb +160 -0
- data/lib/etassia/notation/plantuml/node_graph/scene.rb +22 -0
- data/lib/etassia/notation/plantuml/node_graph/shapes.rb +302 -0
- data/lib/etassia/notation/plantuml/node_graph/skipped_ranks.rb +61 -0
- data/lib/etassia/notation/plantuml/node_graph/tab.rb +68 -0
- data/lib/etassia/notation/plantuml/node_graph/usecase_ellipse.rb +100 -0
- data/lib/etassia/notation/plantuml/node_graph.rb +38 -0
- data/lib/etassia/notation/plantuml/note.rb +27 -0
- data/lib/etassia/notation/plantuml/note_layout.rb +257 -0
- data/lib/etassia/notation/plantuml/note_outline.rb +68 -0
- data/lib/etassia/notation/plantuml/objects/map_rows.rb +46 -0
- data/lib/etassia/notation/plantuml/objects/reader.rb +208 -0
- data/lib/etassia/notation/plantuml/objects.rb +39 -0
- data/lib/etassia/notation/plantuml/package.rb +45 -0
- data/lib/etassia/notation/plantuml/package_color.rb +32 -0
- data/lib/etassia/notation/plantuml/package_frames.rb +178 -0
- data/lib/etassia/notation/plantuml/parser.rb +622 -0
- data/lib/etassia/notation/plantuml/portion_visibility.rb +91 -0
- data/lib/etassia/notation/plantuml/qualified_association_layout.rb +332 -0
- data/lib/etassia/notation/plantuml/qualifier_box.rb +78 -0
- data/lib/etassia/notation/plantuml/rank_chain.rb +527 -0
- data/lib/etassia/notation/plantuml/rank_hierarchy.rb +347 -0
- data/lib/etassia/notation/plantuml/relation.rb +103 -0
- data/lib/etassia/notation/plantuml/relation_label_separator.rb +199 -0
- data/lib/etassia/notation/plantuml/relation_path_points.rb +84 -0
- data/lib/etassia/notation/plantuml/renderer.rb +490 -0
- data/lib/etassia/notation/plantuml/scene.rb +134 -0
- data/lib/etassia/notation/plantuml/segment_box.rb +85 -0
- data/lib/etassia/notation/plantuml/sequence/activation.rb +28 -0
- data/lib/etassia/notation/plantuml/sequence/appearance.rb +71 -0
- data/lib/etassia/notation/plantuml/sequence/arrow_end.rb +23 -0
- data/lib/etassia/notation/plantuml/sequence/arrow_marks.rb +111 -0
- data/lib/etassia/notation/plantuml/sequence/arrow_style.rb +42 -0
- data/lib/etassia/notation/plantuml/sequence/arrow_syntax.rb +122 -0
- data/lib/etassia/notation/plantuml/sequence/bar_tracker.rb +59 -0
- data/lib/etassia/notation/plantuml/sequence/box.rb +23 -0
- data/lib/etassia/notation/plantuml/sequence/chrome.rb +60 -0
- data/lib/etassia/notation/plantuml/sequence/chrome_rows.rb +184 -0
- data/lib/etassia/notation/plantuml/sequence/create.rb +34 -0
- data/lib/etassia/notation/plantuml/sequence/delay.rb +19 -0
- data/lib/etassia/notation/plantuml/sequence/destroy.rb +20 -0
- data/lib/etassia/notation/plantuml/sequence/diagram.rb +86 -0
- data/lib/etassia/notation/plantuml/sequence/divider.rb +19 -0
- data/lib/etassia/notation/plantuml/sequence/edge.rb +53 -0
- data/lib/etassia/notation/plantuml/sequence/embedded.rb +77 -0
- data/lib/etassia/notation/plantuml/sequence/fill.rb +56 -0
- data/lib/etassia/notation/plantuml/sequence/fragment.rb +28 -0
- data/lib/etassia/notation/plantuml/sequence/fragment_shape.rb +138 -0
- data/lib/etassia/notation/plantuml/sequence/head_style.rb +43 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/blocks.rb +73 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/fills.rb +24 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/items.rb +48 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/messages.rb +62 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/participants.rb +42 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter/settings.rb +54 -0
- data/lib/etassia/notation/plantuml/sequence/ir_adapter.rb +38 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/blocks.rb +77 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/fills.rb +27 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/index.rb +36 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/items.rb +35 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/messages.rb +70 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/participants.rb +42 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader/settings.rb +74 -0
- data/lib/etassia/notation/plantuml/sequence/ir_reader.rb +38 -0
- data/lib/etassia/notation/plantuml/sequence/keyword_defaults.rb +28 -0
- data/lib/etassia/notation/plantuml/sequence/layout.rb +1567 -0
- data/lib/etassia/notation/plantuml/sequence/message.rb +97 -0
- data/lib/etassia/notation/plantuml/sequence/message_wrap.rb +77 -0
- data/lib/etassia/notation/plantuml/sequence/note.rb +61 -0
- data/lib/etassia/notation/plantuml/sequence/note_fill.rb +35 -0
- data/lib/etassia/notation/plantuml/sequence/note_geometry.rb +126 -0
- data/lib/etassia/notation/plantuml/sequence/note_shape.rb +51 -0
- data/lib/etassia/notation/plantuml/sequence/numbered_label.rb +57 -0
- data/lib/etassia/notation/plantuml/sequence/numbering.rb +49 -0
- data/lib/etassia/notation/plantuml/sequence/outline.rb +204 -0
- data/lib/etassia/notation/plantuml/sequence/page_break.rb +17 -0
- data/lib/etassia/notation/plantuml/sequence/paint.rb +79 -0
- data/lib/etassia/notation/plantuml/sequence/parallel_message.rb +19 -0
- data/lib/etassia/notation/plantuml/sequence/parser.rb +844 -0
- data/lib/etassia/notation/plantuml/sequence/participant.rb +27 -0
- data/lib/etassia/notation/plantuml/sequence/picture.rb +29 -0
- data/lib/etassia/notation/plantuml/sequence/ref.rb +24 -0
- data/lib/etassia/notation/plantuml/sequence/ref_shape.rb +74 -0
- data/lib/etassia/notation/plantuml/sequence/refusals.rb +47 -0
- data/lib/etassia/notation/plantuml/sequence/renderer.rb +501 -0
- data/lib/etassia/notation/plantuml/sequence/scene.rb +200 -0
- data/lib/etassia/notation/plantuml/sequence/skin.rb +76 -0
- data/lib/etassia/notation/plantuml/sequence/spacing.rb +28 -0
- data/lib/etassia/notation/plantuml/sequence/style.rb +151 -0
- data/lib/etassia/notation/plantuml/sequence/tab_size.rb +42 -0
- data/lib/etassia/notation/plantuml/sequence/walker.rb +1190 -0
- data/lib/etassia/notation/plantuml/sequence.rb +47 -0
- data/lib/etassia/notation/plantuml/source_lines.rb +33 -0
- data/lib/etassia/notation/plantuml/state/axis_solver.rb +272 -0
- data/lib/etassia/notation/plantuml/state/cluster_padding.rb +75 -0
- data/lib/etassia/notation/plantuml/state/curve.rb +73 -0
- data/lib/etassia/notation/plantuml/state/dot_axis.rb +248 -0
- data/lib/etassia/notation/plantuml/state/extent.rb +42 -0
- data/lib/etassia/notation/plantuml/state/hierarchy.rb +355 -0
- data/lib/etassia/notation/plantuml/state/label_clearance.rb +148 -0
- data/lib/etassia/notation/plantuml/state/label_reach.rb +139 -0
- data/lib/etassia/notation/plantuml/state/layout.rb +173 -0
- data/lib/etassia/notation/plantuml/state/level_edges.rb +113 -0
- data/lib/etassia/notation/plantuml/state/look.rb +36 -0
- data/lib/etassia/notation/plantuml/state/note_shape.rb +73 -0
- data/lib/etassia/notation/plantuml/state/plane.rb +85 -0
- data/lib/etassia/notation/plantuml/state/ranking.rb +181 -0
- data/lib/etassia/notation/plantuml/state/reader.rb +310 -0
- data/lib/etassia/notation/plantuml/state/renderer.rb +253 -0
- data/lib/etassia/notation/plantuml/state/route_detour.rb +115 -0
- data/lib/etassia/notation/plantuml/state/route_geometry.rb +186 -0
- data/lib/etassia/notation/plantuml/state/routes.rb +196 -0
- data/lib/etassia/notation/plantuml/state/row.rb +88 -0
- data/lib/etassia/notation/plantuml/state/rows.rb +50 -0
- data/lib/etassia/notation/plantuml/state/stretch.rb +71 -0
- data/lib/etassia/notation/plantuml/state.rb +62 -0
- data/lib/etassia/notation/plantuml/stereotype_head.rb +58 -0
- data/lib/etassia/notation/plantuml/style/bound.rb +34 -0
- data/lib/etassia/notation/plantuml/style/colour.rb +41 -0
- data/lib/etassia/notation/plantuml/style/colour_names.rb +89 -0
- data/lib/etassia/notation/plantuml/style/defaults.rb +33 -0
- data/lib/etassia/notation/plantuml/style/inline.rb +56 -0
- data/lib/etassia/notation/plantuml/style/reader.rb +242 -0
- data/lib/etassia/notation/plantuml/style/sheet.rb +103 -0
- data/lib/etassia/notation/plantuml/style.rb +32 -0
- data/lib/etassia/notation/plantuml/style_reader.rb +160 -0
- data/lib/etassia/notation/plantuml/style_sheet.rb +27 -0
- data/lib/etassia/notation/plantuml/styled_relation_curve.rb +71 -0
- data/lib/etassia/notation/plantuml/timing/arrows.rb +81 -0
- data/lib/etassia/notation/plantuml/timing/axis.rb +100 -0
- data/lib/etassia/notation/plantuml/timing/layout.rb +172 -0
- data/lib/etassia/notation/plantuml/timing/model.rb +34 -0
- data/lib/etassia/notation/plantuml/timing/reader.rb +173 -0
- data/lib/etassia/notation/plantuml/timing/signals.rb +328 -0
- data/lib/etassia/notation/plantuml/timing.rb +38 -0
- data/lib/etassia/notation/plantuml/tree/layout.rb +276 -0
- data/lib/etassia/notation/plantuml/tree/mindmap_layout.rb +157 -0
- data/lib/etassia/notation/plantuml/tree/reader.rb +186 -0
- data/lib/etassia/notation/plantuml/tree/renderer.rb +67 -0
- data/lib/etassia/notation/plantuml/tree/role.rb +57 -0
- data/lib/etassia/notation/plantuml/tree/rules.rb +156 -0
- data/lib/etassia/notation/plantuml/tree/wbs_layout.rb +164 -0
- data/lib/etassia/notation/plantuml/tree.rb +41 -0
- data/lib/etassia/notation/plantuml/unsupported_construct_error.rb +39 -0
- data/lib/etassia/notation/plantuml/unsupported_constructs.rb +103 -0
- data/lib/etassia/notation/plantuml/usecase.rb +36 -0
- data/lib/etassia/notation/plantuml.rb +132 -0
- data/lib/etassia/notation/plantuml_data/boxes/frames.rb +101 -0
- data/lib/etassia/notation/plantuml_data/boxes/graph_layout.rb +348 -0
- data/lib/etassia/notation/plantuml_data/boxes/heads.rb +65 -0
- data/lib/etassia/notation/plantuml_data/boxes/highlights.rb +70 -0
- data/lib/etassia/notation/plantuml_data/boxes/inline_rows.rb +99 -0
- data/lib/etassia/notation/plantuml_data/boxes/json_reader.rb +24 -0
- data/lib/etassia/notation/plantuml_data/boxes/link_text.rb +94 -0
- data/lib/etassia/notation/plantuml_data/boxes/marks.rb +76 -0
- data/lib/etassia/notation/plantuml_data/boxes/ornaments.rb +130 -0
- data/lib/etassia/notation/plantuml_data/boxes/renderer.rb +95 -0
- data/lib/etassia/notation/plantuml_data/boxes/row_alignment.rb +58 -0
- data/lib/etassia/notation/plantuml_data/boxes/row_bend.rb +43 -0
- data/lib/etassia/notation/plantuml_data/boxes/scene.rb +27 -0
- data/lib/etassia/notation/plantuml_data/boxes/table.rb +56 -0
- data/lib/etassia/notation/plantuml_data/boxes/table_box.rb +355 -0
- data/lib/etassia/notation/plantuml_data/boxes/text.rb +101 -0
- data/lib/etassia/notation/plantuml_data/boxes/tree_builder.rb +82 -0
- data/lib/etassia/notation/plantuml_data/boxes/tree_layout.rb +82 -0
- data/lib/etassia/notation/plantuml_data/boxes/yaml_reader.rb +105 -0
- data/lib/etassia/notation/plantuml_data/openiconic.LICENSE +6 -0
- data/lib/etassia/notation/plantuml_data/openiconic.json +1 -0
- data/lib/etassia/notation/plantuml_data.rb +84 -0
- data/lib/etassia/notation/plantuml_salt/block.rb +80 -0
- data/lib/etassia/notation/plantuml_salt/box.rb +30 -0
- data/lib/etassia/notation/plantuml_salt/font_metrics.rb +28 -0
- data/lib/etassia/notation/plantuml_salt/grid_marks.rb +66 -0
- data/lib/etassia/notation/plantuml_salt/group_marks.rb +77 -0
- data/lib/etassia/notation/plantuml_salt/layout.rb +206 -0
- data/lib/etassia/notation/plantuml_salt/mark.rb +36 -0
- data/lib/etassia/notation/plantuml_salt/markup.rb +57 -0
- data/lib/etassia/notation/plantuml_salt/parser.rb +239 -0
- data/lib/etassia/notation/plantuml_salt/renderer.rb +138 -0
- data/lib/etassia/notation/plantuml_salt/row.rb +19 -0
- data/lib/etassia/notation/plantuml_salt/run.rb +34 -0
- data/lib/etassia/notation/plantuml_salt/scene.rb +23 -0
- data/lib/etassia/notation/plantuml_salt/scroll_marks.rb +94 -0
- data/lib/etassia/notation/plantuml_salt/tab_marks.rb +74 -0
- data/lib/etassia/notation/plantuml_salt/tree_marks.rb +127 -0
- data/lib/etassia/notation/plantuml_salt/widget.rb +40 -0
- data/lib/etassia/notation/plantuml_salt/widget_marks.rb +319 -0
- data/lib/etassia/notation/plantuml_salt.rb +66 -0
- data/lib/etassia/notation/plugin_failure.rb +42 -0
- data/lib/etassia/notation.rb +289 -0
- data/lib/etassia/parser/architecture.rb +25 -0
- data/lib/etassia/parser/atoms/delimited_run.rb +82 -0
- data/lib/etassia/parser/atoms/greedy_run.rb +116 -0
- data/lib/etassia/parser/atoms/joined.rb +47 -0
- data/lib/etassia/parser/atoms/trimmed_run.rb +47 -0
- data/lib/etassia/parser/base.rb +258 -0
- data/lib/etassia/parser/block.rb +32 -0
- data/lib/etassia/parser/builders/architecture.rb +135 -0
- data/lib/etassia/parser/builders/block.rb +288 -0
- data/lib/etassia/parser/builders/c4.rb +275 -0
- data/lib/etassia/parser/builders/capture_string.rb +17 -0
- data/lib/etassia/parser/builders/class_diagram.rb +539 -0
- data/lib/etassia/parser/builders/er_diagram.rb +308 -0
- data/lib/etassia/parser/builders/error.rb +51 -0
- data/lib/etassia/parser/builders/flowchart.rb +1150 -0
- data/lib/etassia/parser/builders/gantt.rb +267 -0
- data/lib/etassia/parser/builders/git_graph.rb +301 -0
- data/lib/etassia/parser/builders/info.rb +57 -0
- data/lib/etassia/parser/builders/kanban.rb +411 -0
- data/lib/etassia/parser/builders/mindmap.rb +271 -0
- data/lib/etassia/parser/builders/packet.rb +85 -0
- data/lib/etassia/parser/builders/pie.rb +105 -0
- data/lib/etassia/parser/builders/quadrant.rb +165 -0
- data/lib/etassia/parser/builders/radar.rb +157 -0
- data/lib/etassia/parser/builders/requirement.rb +230 -0
- data/lib/etassia/parser/builders/sankey.rb +142 -0
- data/lib/etassia/parser/builders/sequence.rb +431 -0
- data/lib/etassia/parser/builders/sequence_blocks.rb +117 -0
- data/lib/etassia/parser/builders/sequence_box_title.rb +57 -0
- data/lib/etassia/parser/builders/state_diagram.rb +364 -0
- data/lib/etassia/parser/builders/timeline.rb +133 -0
- data/lib/etassia/parser/builders/treemap.rb +138 -0
- data/lib/etassia/parser/builders/xy_chart.rb +148 -0
- data/lib/etassia/parser/c4.rb +37 -0
- data/lib/etassia/parser/class_diagram.rb +58 -0
- data/lib/etassia/parser/class_namespaces.rb +62 -0
- data/lib/etassia/parser/class_notes.rb +54 -0
- data/lib/etassia/parser/er_diagram.rb +28 -0
- data/lib/etassia/parser/error.rb +30 -0
- data/lib/etassia/parser/flowchart.rb +87 -0
- data/lib/etassia/parser/gantt.rb +43 -0
- data/lib/etassia/parser/git_graph.rb +106 -0
- data/lib/etassia/parser/grammars/architecture.rb +165 -0
- data/lib/etassia/parser/grammars/block.rb +190 -0
- data/lib/etassia/parser/grammars/c4.rb +229 -0
- data/lib/etassia/parser/grammars/class_diagram.rb +606 -0
- data/lib/etassia/parser/grammars/common.rb +107 -0
- data/lib/etassia/parser/grammars/er_diagram.rb +232 -0
- data/lib/etassia/parser/grammars/error.rb +32 -0
- data/lib/etassia/parser/grammars/flowchart.rb +1722 -0
- data/lib/etassia/parser/grammars/gantt.rb +198 -0
- data/lib/etassia/parser/grammars/git_graph.rb +234 -0
- data/lib/etassia/parser/grammars/info.rb +53 -0
- data/lib/etassia/parser/grammars/kanban.rb +417 -0
- data/lib/etassia/parser/grammars/mermaid_unicode_text.rb +108 -0
- data/lib/etassia/parser/grammars/mindmap.rb +220 -0
- data/lib/etassia/parser/grammars/packet.rb +71 -0
- data/lib/etassia/parser/grammars/pie.rb +128 -0
- data/lib/etassia/parser/grammars/quadrant.rb +202 -0
- data/lib/etassia/parser/grammars/radar.rb +158 -0
- data/lib/etassia/parser/grammars/requirement.rb +258 -0
- data/lib/etassia/parser/grammars/sankey.rb +104 -0
- data/lib/etassia/parser/grammars/sequence.rb +940 -0
- data/lib/etassia/parser/grammars/state_diagram.rb +382 -0
- data/lib/etassia/parser/grammars/timeline.rb +153 -0
- data/lib/etassia/parser/grammars/treemap.rb +122 -0
- data/lib/etassia/parser/grammars/xy_chart.rb +119 -0
- data/lib/etassia/parser/info.rb +32 -0
- data/lib/etassia/parser/kanban.rb +131 -0
- data/lib/etassia/parser/mermaid_shapes.rb +106 -0
- data/lib/etassia/parser/metadata_yaml.rb +452 -0
- data/lib/etassia/parser/mindmap.rb +92 -0
- data/lib/etassia/parser/native/builders/architecture.rb +103 -0
- data/lib/etassia/parser/native/builders/block.rb +68 -0
- data/lib/etassia/parser/native/builders/c4.rb +16 -0
- data/lib/etassia/parser/native/builders/class_diagram.rb +63 -0
- data/lib/etassia/parser/native/builders/er_diagram.rb +47 -0
- data/lib/etassia/parser/native/builders/flowchart.rb +58 -0
- data/lib/etassia/parser/native/builders/gantt.rb +35 -0
- data/lib/etassia/parser/native/builders/gitgraph.rb +35 -0
- data/lib/etassia/parser/native/builders/info.rb +30 -0
- data/lib/etassia/parser/native/builders/kanban.rb +37 -0
- data/lib/etassia/parser/native/builders/mindmap.rb +26 -0
- data/lib/etassia/parser/native/builders/packet.rb +63 -0
- data/lib/etassia/parser/native/builders/pie.rb +82 -0
- data/lib/etassia/parser/native/builders/quadrant.rb +27 -0
- data/lib/etassia/parser/native/builders/radar.rb +84 -0
- data/lib/etassia/parser/native/builders/requirement.rb +25 -0
- data/lib/etassia/parser/native/builders/sankey.rb +18 -0
- data/lib/etassia/parser/native/builders/sequence.rb +48 -0
- data/lib/etassia/parser/native/builders/state_diagram.rb +61 -0
- data/lib/etassia/parser/native/builders/timeline.rb +46 -0
- data/lib/etassia/parser/native/builders/treemap.rb +60 -0
- data/lib/etassia/parser/native/builders/user_journey.rb +83 -0
- data/lib/etassia/parser/native/builders/xy_chart.rb +94 -0
- data/lib/etassia/parser/native/declined.rb +12 -0
- data/lib/etassia/parser/native/mermaid.json +1 -0
- data/lib/etassia/parser/native/text.rb +31 -0
- data/lib/etassia/parser/native.rb +99 -0
- data/lib/etassia/parser/packet.rb +56 -0
- data/lib/etassia/parser/pie.rb +32 -0
- data/lib/etassia/parser/quadrant.rb +77 -0
- data/lib/etassia/parser/radar.rb +99 -0
- data/lib/etassia/parser/requirement.rb +32 -0
- data/lib/etassia/parser/sankey.rb +47 -0
- data/lib/etassia/parser/sequence.rb +34 -0
- data/lib/etassia/parser/state_diagram.rb +29 -0
- data/lib/etassia/parser/timeline.rb +40 -0
- data/lib/etassia/parser/treemap.rb +25 -0
- data/lib/etassia/parser/user_journey.rb +253 -0
- data/lib/etassia/parser/xy_chart.rb +98 -0
- data/lib/etassia/parser.rb +18 -0
- data/lib/etassia/renderer/architecture.rb +232 -0
- data/lib/etassia/renderer/architecture_edge_router.rb +379 -0
- data/lib/etassia/renderer/base.rb +295 -0
- data/lib/etassia/renderer/block.rb +157 -0
- data/lib/etassia/renderer/c4.rb +294 -0
- data/lib/etassia/renderer/class_diagram.rb +671 -0
- data/lib/etassia/renderer/er_diagram.rb +842 -0
- data/lib/etassia/renderer/error.rb +131 -0
- data/lib/etassia/renderer/flowchart.rb +315 -0
- data/lib/etassia/renderer/gantt.rb +177 -0
- data/lib/etassia/renderer/git_graph.rb +224 -0
- data/lib/etassia/renderer/info.rb +59 -0
- data/lib/etassia/renderer/kanban.rb +157 -0
- data/lib/etassia/renderer/line_break_text.rb +56 -0
- data/lib/etassia/renderer/markdown_text.rb +106 -0
- data/lib/etassia/renderer/mindmap.rb +290 -0
- data/lib/etassia/renderer/packet.rb +87 -0
- data/lib/etassia/renderer/pie.rb +113 -0
- data/lib/etassia/renderer/quadrant.rb +189 -0
- data/lib/etassia/renderer/radar.rb +191 -0
- data/lib/etassia/renderer/requirement.rb +209 -0
- data/lib/etassia/renderer/sankey.rb +114 -0
- data/lib/etassia/renderer/sequence/frame_drawing.rb +169 -0
- data/lib/etassia/renderer/sequence/note_drawing.rb +69 -0
- data/lib/etassia/renderer/sequence.rb +490 -0
- data/lib/etassia/renderer/state_diagram.rb +202 -0
- data/lib/etassia/renderer/timeline.rb +134 -0
- data/lib/etassia/renderer/timeline_palette.rb +18 -0
- data/lib/etassia/renderer/treemap/section_total.rb +33 -0
- data/lib/etassia/renderer/treemap.rb +128 -0
- data/lib/etassia/renderer/user_journey.rb +209 -0
- data/lib/etassia/renderer/xy_chart.rb +180 -0
- data/lib/etassia/renderer.rb +22 -0
- data/lib/etassia/runtime.rb +125 -0
- data/lib/etassia/source/frontmatter.rb +501 -0
- data/lib/etassia/source.rb +224 -0
- data/lib/etassia/svg/arrowhead.rb +205 -0
- data/lib/etassia/svg/circle.rb +19 -0
- data/lib/etassia/svg/document.rb +106 -0
- data/lib/etassia/svg/element.rb +209 -0
- data/lib/etassia/svg/ellipse.rb +23 -0
- data/lib/etassia/svg/escaping.rb +149 -0
- data/lib/etassia/svg/group.rb +64 -0
- data/lib/etassia/svg/line.rb +21 -0
- data/lib/etassia/svg/numbers.rb +53 -0
- data/lib/etassia/svg/path.rb +67 -0
- data/lib/etassia/svg/path_geometry.rb +378 -0
- data/lib/etassia/svg/polygon.rb +25 -0
- data/lib/etassia/svg/polyline.rb +28 -0
- data/lib/etassia/svg/rect.rb +34 -0
- data/lib/etassia/svg/style.rb +50 -0
- data/lib/etassia/svg/text.rb +181 -0
- data/lib/etassia/svg/tspan.rb +48 -0
- data/lib/etassia/svg.rb +38 -0
- data/lib/etassia/text_measurement/arial_advances.rb +122 -0
- data/lib/etassia/text_measurement/dejavu_advances.rb +111 -0
- data/lib/etassia/text_measurement/times_advances.rb +64 -0
- data/lib/etassia/text_measurement/trebuchet_advances.rb +53 -0
- data/lib/etassia/text_measurement/trebuchet_kerning.rb +29 -0
- data/lib/etassia/text_measurement.rb +198 -0
- data/lib/etassia/theme/builtin/dark.yml +229 -0
- data/lib/etassia/theme/builtin/default.yml +239 -0
- data/lib/etassia/theme/builtin/high_contrast.yml +231 -0
- data/lib/etassia/theme/builtin/light.yml +229 -0
- data/lib/etassia/theme/color_palette.rb +97 -0
- data/lib/etassia/theme/completion.rb +50 -0
- data/lib/etassia/theme/effect_styles.rb +34 -0
- data/lib/etassia/theme/registry.rb +45 -0
- data/lib/etassia/theme/shape_styles.rb +34 -0
- data/lib/etassia/theme/spacing_config.rb +30 -0
- data/lib/etassia/theme/typography.rb +40 -0
- data/lib/etassia/theme.rb +73 -0
- data/lib/etassia/version.rb +5 -0
- data/lib/etassia.rb +40 -0
- metadata +865 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "parslet"
|
|
4
|
+
|
|
5
|
+
require_relative "../metadata_yaml"
|
|
6
|
+
|
|
7
|
+
module Etassia
|
|
8
|
+
module Parser
|
|
9
|
+
module Builders
|
|
10
|
+
# Transform for Kanban diagrams
|
|
11
|
+
class Kanban < Parslet::Transform
|
|
12
|
+
# Helper class to build kanban board from indented lines
|
|
13
|
+
class BoardBuilder
|
|
14
|
+
attr_reader :columns
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
@columns = []
|
|
18
|
+
@current_column = nil
|
|
19
|
+
@items = []
|
|
20
|
+
@anonymous_index = 0
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add_line(line_data)
|
|
24
|
+
return apply_modifier_line(line_data) if modifier?(line_data)
|
|
25
|
+
|
|
26
|
+
@items << item_from(line_data)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def modifier?(line_data)
|
|
30
|
+
line_data[:icon] || line_data[:classes]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def apply_modifier_line(line_data)
|
|
34
|
+
if line_data[:icon]
|
|
35
|
+
return apply_modifier(:icon, line_data[:icon].to_s)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
classes = line_data[:classes].to_s.strip.split(/\s+/)
|
|
39
|
+
apply_modifier(:classes, classes)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# An item with no bracket label displays its id. Metadata is parsed
|
|
43
|
+
# here so both add_card and add_column see it. An absent label is
|
|
44
|
+
# nil, so `|| id` safely falls back without hiding an empty string.
|
|
45
|
+
def item_from(line_data)
|
|
46
|
+
id = resolve_id(line_data[:id])
|
|
47
|
+
{
|
|
48
|
+
id: id,
|
|
49
|
+
text: line_data[:text]&.to_s || id,
|
|
50
|
+
indent: get_indent_size(line_data[:indent]),
|
|
51
|
+
metadata: parse_metadata(line_data[:metadata]),
|
|
52
|
+
}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def finalize
|
|
56
|
+
return if @items.empty?
|
|
57
|
+
|
|
58
|
+
# The first item's indent is the column level, as in mermaid's
|
|
59
|
+
# getSection. An item at that indent is a column; any other
|
|
60
|
+
# indent is a card of the latest column, including one LESS
|
|
61
|
+
# indented than the first. Mermaid rejects a less-indented item
|
|
62
|
+
# only once another item follows it.
|
|
63
|
+
build_columns(@items.first[:indent])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def build_columns(column_indent)
|
|
69
|
+
shallower = nil
|
|
70
|
+
@items.each do |item|
|
|
71
|
+
refuse_item_after(shallower) if shallower
|
|
72
|
+
shallower = item if item[:indent] < column_indent
|
|
73
|
+
item[:indent] == column_indent ? add_column(item) : add_card(item)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def refuse_item_after(item)
|
|
78
|
+
raise Parser::ParseError,
|
|
79
|
+
"Items without section detected, " \
|
|
80
|
+
"found section (\"#{column_title(item)}\")."
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# A shape with no id (`(text)`) never captures `:id`. Mermaid
|
|
84
|
+
# still draws it - it auto-assigns one - so this does too,
|
|
85
|
+
# deterministically: parsing the same source twice must produce
|
|
86
|
+
# the same diagram, and a random id would break that on every
|
|
87
|
+
# call (see the `kanban-1` / `kanban-2` assertions in
|
|
88
|
+
# spec/etassia/parser/kanban_spec.rb). Unlike
|
|
89
|
+
# Builders::Block.anonymous_id, this id is never displayed -
|
|
90
|
+
# `unlabelled_shaped_item` always captures its own `:text`, so
|
|
91
|
+
# `resolve_id`'s output never reaches TextMeasurement and its
|
|
92
|
+
# length has no effect on layout (measured: extending it by
|
|
93
|
+
# 1,000 characters left the rendered SVG byte-identical). The
|
|
94
|
+
# hyphen keeps generated ids out of the author's namespace,
|
|
95
|
+
# since identifier_char is `[a-zA-Z0-9_]` and can never spell
|
|
96
|
+
# one.
|
|
97
|
+
def resolve_id(id_slice)
|
|
98
|
+
return id_slice.to_s if id_slice
|
|
99
|
+
|
|
100
|
+
@anonymous_index += 1
|
|
101
|
+
"kanban-#{@anonymous_index}"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def add_column(item)
|
|
105
|
+
column = {
|
|
106
|
+
id: item[:id],
|
|
107
|
+
title: column_title(item),
|
|
108
|
+
icon: item[:metadata][:icon],
|
|
109
|
+
classes: normalize_classes(item[:metadata][:classes]),
|
|
110
|
+
cards: [],
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@columns << column
|
|
114
|
+
@current_column = column
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# `::icon(...)`/`:::` lines modify whichever item was added last;
|
|
118
|
+
# raise rather than silently drop when there is none, matching
|
|
119
|
+
# mmdc 11.12.0 (which rejects that shape). Both keys are
|
|
120
|
+
# last-write-wins, matching mermaid's `decorateNode`, which
|
|
121
|
+
# plainly assigns `node.cssClasses = ...` rather than merging.
|
|
122
|
+
def apply_modifier(key, value)
|
|
123
|
+
unless @items.last
|
|
124
|
+
marker = key == :icon ? "::icon" : ":::"
|
|
125
|
+
raise Parser::ParseError, "#{marker} with no preceding item."
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
@items.last[:metadata][key] = value
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# `classes` arrives here as an Array from a `:::a b` directive line
|
|
132
|
+
# (parse_metadata drops a scalar `classes:` key from `@{ ... }`
|
|
133
|
+
# metadata entirely - mermaid does not read one). `else` also covers a
|
|
134
|
+
# nil/absent value, normalized to an empty Array. The single point
|
|
135
|
+
# both add_column and add_card read from.
|
|
136
|
+
def normalize_classes(value)
|
|
137
|
+
case value
|
|
138
|
+
when Array then value
|
|
139
|
+
else value.to_s.strip.split(/\s+/)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# The card's own `id` and `text` beat a metadata entry of the same
|
|
144
|
+
# name, because mermaid ignores `@{ id: ... }` and `@{ text: ... }`.
|
|
145
|
+
# Merging the metadata FIRST makes that true by construction, with
|
|
146
|
+
# no exclusion list to keep in step as fields are added.
|
|
147
|
+
#
|
|
148
|
+
# A `label:` on a card is kept as its own attribute and drawn as a
|
|
149
|
+
# separate "Label:" row. That diverges from mermaid, which renders
|
|
150
|
+
# the label as the card's primary text and draws no such row. The
|
|
151
|
+
# divergence is pre-existing; correcting it belongs to the
|
|
152
|
+
# card-conformance bucket.
|
|
153
|
+
def add_card(item)
|
|
154
|
+
@current_column[:cards] << item[:metadata].merge(
|
|
155
|
+
id: item[:id],
|
|
156
|
+
text: item[:text],
|
|
157
|
+
classes: normalize_classes(item[:metadata][:classes]),
|
|
158
|
+
)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# A `label:` replaces a COLUMN's display text, which is what mermaid
|
|
162
|
+
# renders. A label mermaid would treat as unset is already gone by
|
|
163
|
+
# here - parse_metadata drops it - so falling back to the bracket
|
|
164
|
+
# text, or to the id, is a plain `||`.
|
|
165
|
+
def column_title(item)
|
|
166
|
+
item[:metadata][:label] || item[:text]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# `Kanban.metadata_fields` does the real work - a real YAML engine
|
|
170
|
+
# for structure and resolution - so this is left only to route each
|
|
171
|
+
# resolved key to the card/column field mermaid's `addNode` reads
|
|
172
|
+
# it into.
|
|
173
|
+
# Mermaid reads no `classes` key from metadata; only a `:::`
|
|
174
|
+
# modifier may set it. Every other resolved key is stored as-is.
|
|
175
|
+
def parse_metadata(metadata_data)
|
|
176
|
+
return {} if metadata_data.nil?
|
|
177
|
+
|
|
178
|
+
fields = Kanban.metadata_fields(metadata_data)
|
|
179
|
+
fields.each_with_object({}) do |(key, value), result|
|
|
180
|
+
next if key == "classes"
|
|
181
|
+
|
|
182
|
+
result[key.to_sym] = value
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# The grammar captures the indent as a string, "" when absent.
|
|
187
|
+
def get_indent_size(indent_data)
|
|
188
|
+
indent_data.to_s.length
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# The `@{ ... }` body, resolved by a real YAML engine
|
|
193
|
+
# (`MetadataYaml`) the way mermaid's js-yaml does: flow vs block is
|
|
194
|
+
# decided by whether the body contains a newline anywhere. Do not
|
|
195
|
+
# re-add hand-written grammar rules for YAML legality - duplicate
|
|
196
|
+
# keys and tab indentation are refused by the engine.
|
|
197
|
+
#
|
|
198
|
+
# @raise [Parser::ParseError] on YAML mermaid would also refuse
|
|
199
|
+
def self.metadata_fields(metadata_data)
|
|
200
|
+
document, unshield = shield_foreign_breaks(
|
|
201
|
+
metadata_document(metadata_data),
|
|
202
|
+
)
|
|
203
|
+
resolved = MetadataYaml.value(document)
|
|
204
|
+
raise Parser::ParseError, "Empty metadata." if resolved.nil?
|
|
205
|
+
return {} unless resolved.is_a?(Hash)
|
|
206
|
+
|
|
207
|
+
stored_fields(resolved)
|
|
208
|
+
.to_h { |key, text| [unshield.call(key), unshield.call(text)] }
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Mermaid's own wrapping: a single-line body becomes a flow
|
|
212
|
+
# mapping between braces, a multiline one is used as written -
|
|
213
|
+
# see Builders::Flowchart#metadata_document, the same rule.
|
|
214
|
+
def self.metadata_document(metadata_data)
|
|
215
|
+
text = metadata_body_text(metadata_data)
|
|
216
|
+
body = break_quoted_newlines(strip_metadata_comments(text))
|
|
217
|
+
body.include?("\n") ? "#{body}\n" : "{\n#{body}\n}"
|
|
218
|
+
end
|
|
219
|
+
private_class_method :metadata_document
|
|
220
|
+
|
|
221
|
+
# NEL, LS and PS are line breaks to Psych but ordinary characters
|
|
222
|
+
# to js-yaml, and `MetadataYaml` refuses them rather than read them
|
|
223
|
+
# the wrong way. Mermaid keeps them as text, so they are swapped for
|
|
224
|
+
# unused private-use characters while the engine reads the body, and
|
|
225
|
+
# swapped back in the stored fields.
|
|
226
|
+
#
|
|
227
|
+
# @return [Array(String, #call)] the shielded body and the function
|
|
228
|
+
# that restores a stored string
|
|
229
|
+
def self.shield_foreign_breaks(document)
|
|
230
|
+
present = FOREIGN_BREAKS.select { |char| document.include?(char) }
|
|
231
|
+
return [document, :itself.to_proc] if present.empty?
|
|
232
|
+
|
|
233
|
+
shields = present.zip(spare_characters(document, present.size)).to_h
|
|
234
|
+
[document.gsub(Regexp.union(present), shields), unshielder(shields)]
|
|
235
|
+
end
|
|
236
|
+
private_class_method :shield_foreign_breaks
|
|
237
|
+
|
|
238
|
+
def self.unshielder(shields)
|
|
239
|
+
restore = shields.invert
|
|
240
|
+
pattern = Regexp.union(restore.keys)
|
|
241
|
+
->(text) { text.gsub(pattern, restore) }
|
|
242
|
+
end
|
|
243
|
+
private_class_method :unshielder
|
|
244
|
+
|
|
245
|
+
# A character is spare when the body neither contains it nor spells
|
|
246
|
+
# it as a `\xHH`, `\uHHHH` or `\UHHHHHHHH` escape - an escape
|
|
247
|
+
# would otherwise resolve to it and be swapped back by mistake.
|
|
248
|
+
def self.spare_characters(document, count)
|
|
249
|
+
taken = taken_code_points(document)
|
|
250
|
+
free = (0xE000..0xF8FF).lazy.reject { |code| taken.include?(code) }
|
|
251
|
+
spare = free.first(count).map { |code| code.chr(Encoding::UTF_8) }
|
|
252
|
+
return spare if spare.size == count
|
|
253
|
+
|
|
254
|
+
raise Parser::ParseError, "Unreadable line break."
|
|
255
|
+
end
|
|
256
|
+
private_class_method :spare_characters
|
|
257
|
+
|
|
258
|
+
def self.taken_code_points(document)
|
|
259
|
+
literal = document.scan(/[\ue000-\uf8ff]/).map(&:ord)
|
|
260
|
+
escaped = document.scan(YAML_CODE_ESCAPE).flatten.compact
|
|
261
|
+
literal + escaped.map { |hex| hex.to_i(16) }
|
|
262
|
+
end
|
|
263
|
+
private_class_method :taken_code_points
|
|
264
|
+
|
|
265
|
+
# A list or map is text only under the keys mermaid draws; `shape`
|
|
266
|
+
# refuses it (`validate_shape`) and any other key ignores it.
|
|
267
|
+
# `priority` is one of those: mermaid compares it by identity, so
|
|
268
|
+
# `[High]` is not the priority `High`. The one `walk` budget covers
|
|
269
|
+
# the whole body.
|
|
270
|
+
def self.stored_fields(resolved)
|
|
271
|
+
walk = { left: JS_LIST_ITEMS }
|
|
272
|
+
resolved.filter_map do |key, value|
|
|
273
|
+
next if dropped_by_mermaid?(value)
|
|
274
|
+
|
|
275
|
+
validate_shape(value) if key == "shape"
|
|
276
|
+
next if collection?(value) && !TEXT_KEYS.include?(key)
|
|
277
|
+
|
|
278
|
+
[key, js_text(value, walk)]
|
|
279
|
+
end.to_h
|
|
280
|
+
end
|
|
281
|
+
private_class_method :stored_fields
|
|
282
|
+
|
|
283
|
+
def self.collection?(value)
|
|
284
|
+
value.is_a?(Array) || value.is_a?(Hash)
|
|
285
|
+
end
|
|
286
|
+
private_class_method :collection?
|
|
287
|
+
|
|
288
|
+
def self.validate_shape(value)
|
|
289
|
+
unless value.is_a?(String)
|
|
290
|
+
raise Parser::ParseError,
|
|
291
|
+
"Unusable shape in metadata."
|
|
292
|
+
end
|
|
293
|
+
return if value == value.downcase && !value.include?("_")
|
|
294
|
+
|
|
295
|
+
raise Parser::ParseError,
|
|
296
|
+
"No such shape: #{value}. Shape names should be lowercase."
|
|
297
|
+
end
|
|
298
|
+
private_class_method :validate_shape
|
|
299
|
+
|
|
300
|
+
# A subclass grammar whose empty body captures as [] reads as "[]",
|
|
301
|
+
# which the YAML engine takes as a mapping key with no value:
|
|
302
|
+
# the same empty metadata as an empty string.
|
|
303
|
+
def self.metadata_body_text(metadata_data)
|
|
304
|
+
metadata_data[:body].to_s
|
|
305
|
+
end
|
|
306
|
+
private_class_method :metadata_body_text
|
|
307
|
+
|
|
308
|
+
# Mermaid strips comments before lexing, so metadata never sees
|
|
309
|
+
# them - `%%{` is a directive rather than a comment and stays.
|
|
310
|
+
# Uses JavaScript's whitespace set, matching Mermaid's comment pass.
|
|
311
|
+
def self.strip_metadata_comments(body)
|
|
312
|
+
body.gsub(/(?<=\n)#{JS_SPACE}*%%(?!\{)[^\n]+\n?/o, "")
|
|
313
|
+
end
|
|
314
|
+
private_class_method :strip_metadata_comments
|
|
315
|
+
|
|
316
|
+
# The kanban lexer rewrites line breaks inside double-quoted
|
|
317
|
+
# metadata before handing the body to js-yaml.
|
|
318
|
+
def self.break_quoted_newlines(body)
|
|
319
|
+
body.gsub(/"[^"]*"/) { |run| run.gsub(QUOTED_BREAK, "<br/>") }
|
|
320
|
+
end
|
|
321
|
+
private_class_method :break_quoted_newlines
|
|
322
|
+
|
|
323
|
+
JS_SPACE = '[\t\n\v\f\r \u00a0\u1680\u2000-\u200a' \
|
|
324
|
+
'\u2028\u2029\u202f\u205f\u3000\ufeff]'
|
|
325
|
+
QUOTED_BREAK = /\n#{JS_SPACE}*/
|
|
326
|
+
FOREIGN_BREAKS = ["\u0085", "\u2028", "\u2029"].freeze
|
|
327
|
+
YAML_CODE_ESCAPE = /\\(?:x(\h{2})|u(\h{4})|U(\h{8}))/
|
|
328
|
+
JS_LIST_ITEMS = 10_000
|
|
329
|
+
JS_LIST_DEPTH = 256
|
|
330
|
+
TEXT_KEYS = %w[assigned ticket icon label].freeze
|
|
331
|
+
|
|
332
|
+
private_constant :JS_SPACE, :QUOTED_BREAK, :FOREIGN_BREAKS,
|
|
333
|
+
:YAML_CODE_ESCAPE, :JS_LIST_ITEMS, :JS_LIST_DEPTH,
|
|
334
|
+
:TEXT_KEYS
|
|
335
|
+
|
|
336
|
+
# `null`, `false`, `0` and `""` are all falsy to mermaid, which
|
|
337
|
+
# skips the key rather than failing on it - the same table
|
|
338
|
+
# Builders::Flowchart#truthy? applies to its own resolved values.
|
|
339
|
+
def self.dropped_by_mermaid?(value)
|
|
340
|
+
case value
|
|
341
|
+
when nil, false, 0, "" then true
|
|
342
|
+
else value.is_a?(Float) && value.nan?
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
private_class_method :dropped_by_mermaid?
|
|
346
|
+
|
|
347
|
+
# Mermaid draws `.toString()` of what js-yaml resolved: numbers and
|
|
348
|
+
# booleans as JavaScript prints them (`0x10` is "16"), a list as its
|
|
349
|
+
# items joined by commas (`[one, two]` is "one,two"), a map as
|
|
350
|
+
# "[object Object]". An alias repeats a list by reference, so
|
|
351
|
+
# `JS_LIST_ITEMS` caps the items joined and `JS_LIST_DEPTH` the
|
|
352
|
+
# nesting, as `Source::Frontmatter` bounds its own walk; mmdc draws
|
|
353
|
+
# some bodies this refuses.
|
|
354
|
+
#
|
|
355
|
+
# @param walk [Hash] items still allowed to be joined, body-wide
|
|
356
|
+
# @param ancestors [Array<Array>] lists being joined; one holding
|
|
357
|
+
# itself joins as "" the way JavaScript's `join` does
|
|
358
|
+
def self.js_text(value, walk, ancestors = [])
|
|
359
|
+
case value
|
|
360
|
+
when Numeric then Etassia::JsNumber.stringify(value)
|
|
361
|
+
when Hash then "[object Object]"
|
|
362
|
+
when Array then js_join(value, walk, ancestors)
|
|
363
|
+
else value.to_s
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
private_class_method :js_text
|
|
367
|
+
|
|
368
|
+
def self.js_join(list, walk, ancestors)
|
|
369
|
+
return "" if ancestors.any? { |outer| outer.equal?(list) }
|
|
370
|
+
|
|
371
|
+
refuse_list("nested too deeply") if ancestors.size >= JS_LIST_DEPTH
|
|
372
|
+
list.map do |item|
|
|
373
|
+
walk[:left] -= 1
|
|
374
|
+
refuse_list("too large") if walk[:left].negative?
|
|
375
|
+
|
|
376
|
+
js_text(item, walk, ancestors + [list])
|
|
377
|
+
end.join(",")
|
|
378
|
+
end
|
|
379
|
+
private_class_method :js_join
|
|
380
|
+
|
|
381
|
+
def self.refuse_list(reason)
|
|
382
|
+
raise Parser::ParseError, "Metadata list #{reason}."
|
|
383
|
+
end
|
|
384
|
+
private_class_method :refuse_list
|
|
385
|
+
|
|
386
|
+
# Transform the lines array into columns and cards
|
|
387
|
+
rule(lines: subtree(:lines)) do
|
|
388
|
+
builder = BoardBuilder.new
|
|
389
|
+
lines_array = Array(lines)
|
|
390
|
+
|
|
391
|
+
lines_array.each do |line_data|
|
|
392
|
+
# An empty line never captures anything, so it contributes no
|
|
393
|
+
# entry at all to this array rather than a Hash lacking keys -
|
|
394
|
+
# this Hash check is the only filter empty lines need. It is
|
|
395
|
+
# NOT a stand-in for requiring `:id`: an unlabelled shaped item
|
|
396
|
+
# (`(text)`) is a Hash with no `:id` key and is a real item.
|
|
397
|
+
next unless line_data.is_a?(Hash)
|
|
398
|
+
|
|
399
|
+
builder.add_line(line_data)
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
builder.finalize
|
|
403
|
+
|
|
404
|
+
{
|
|
405
|
+
columns: builder.columns,
|
|
406
|
+
}
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
end
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "parslet"
|
|
4
|
+
|
|
5
|
+
module Etassia
|
|
6
|
+
module Parser
|
|
7
|
+
module Builders
|
|
8
|
+
# Transform for Mindmap diagrams
|
|
9
|
+
class Mindmap < Parslet::Transform
|
|
10
|
+
# Helper class to build mindmap tree from indented nodes
|
|
11
|
+
class TreeBuilder
|
|
12
|
+
# The first matching rule wins, in this order.
|
|
13
|
+
SHAPE_NAMES = %w[circle bang cloud hexagon square round].freeze
|
|
14
|
+
NODE_KEYS = %i[
|
|
15
|
+
content icon classes shape_circle shape_bang shape_cloud
|
|
16
|
+
shape_hexagon shape_square shape_round
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
19
|
+
attr_reader :root, :all_nodes
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
@all_nodes = []
|
|
23
|
+
@root = nil
|
|
24
|
+
@level_stack = []
|
|
25
|
+
@pending_icon = nil
|
|
26
|
+
@pending_classes = []
|
|
27
|
+
@min_indent = nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add_node(node_data)
|
|
31
|
+
if node_data[:icon]
|
|
32
|
+
return apply_to_previous(:icon, node_data[:icon].to_s)
|
|
33
|
+
end
|
|
34
|
+
return apply_classes(node_data[:classes]) if node_data[:classes]
|
|
35
|
+
|
|
36
|
+
add_tree_node(node_data)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def node_data?(node_data)
|
|
40
|
+
NODE_KEYS.any? { |key| node_data[key] }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def apply_to_previous(attribute, value)
|
|
44
|
+
@all_nodes.last[attribute] = value if @all_nodes.last
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def apply_classes(classes)
|
|
48
|
+
apply_to_previous(:classes, classes.to_s.split(/\s+/))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def add_tree_node(node_data)
|
|
52
|
+
indent_size = get_indent_size(node_data[:indent])
|
|
53
|
+
if @min_indent.nil? || indent_size < @min_indent
|
|
54
|
+
@min_indent = indent_size
|
|
55
|
+
end
|
|
56
|
+
level = calculate_level(node_data[:indent])
|
|
57
|
+
node = node_entry(node_data, level, indent_size)
|
|
58
|
+
@all_nodes << node
|
|
59
|
+
link_node(node, level)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def node_entry(node_data, level, indent_size)
|
|
63
|
+
{
|
|
64
|
+
id: "node-#{@all_nodes.size}",
|
|
65
|
+
content: extract_content(node_data), level: level,
|
|
66
|
+
shape: extract_shape(node_data), icon: nil, classes: [],
|
|
67
|
+
children: [], _indent_size: indent_size
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def link_node(node, level)
|
|
72
|
+
if level.zero?
|
|
73
|
+
@root = node
|
|
74
|
+
@level_stack = [node]
|
|
75
|
+
else
|
|
76
|
+
attach_to_parent(node, find_parent(level))
|
|
77
|
+
@level_stack = @level_stack[0..(level - 1)] + [node]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def attach_to_parent(node, parent)
|
|
82
|
+
return unless parent
|
|
83
|
+
|
|
84
|
+
parent[:children] << node
|
|
85
|
+
node[:parent] = parent
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def finalize
|
|
89
|
+
# Nothing to link if no real node was ever added.
|
|
90
|
+
return if @min_indent.nil?
|
|
91
|
+
|
|
92
|
+
# Rebuild hierarchy directly from each node's raw indentation,
|
|
93
|
+
# not a level number banded to a fixed 2- or 4-space step. The
|
|
94
|
+
# banded approach broke whenever one level's indent jumped by an
|
|
95
|
+
# amount the fixed band didn't expect (e.g. a first indent step
|
|
96
|
+
# of 4 columns from a zero-indent root): it produced a level
|
|
97
|
+
# with no entry below it in the stack, so the child was computed
|
|
98
|
+
# but never linked as anyone's child.
|
|
99
|
+
rebuild_hierarchy
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# A `%%` comment line embedded inside a multi-line round-shape
|
|
103
|
+
# node's own content (e.g. "root(\n one\n %% hidden\n two\n)").
|
|
104
|
+
# Mermaid strips every such line, terminator included, before its
|
|
105
|
+
# node lexer ever runs, but only when the `%%` starts a REAL
|
|
106
|
+
# source line -- one already preceded by a newline -- and only
|
|
107
|
+
# when at least one character follows it; `root(%% keep this)`
|
|
108
|
+
# is one physical line (the `%%` sits after "root(", not at a
|
|
109
|
+
# line start) so mermaid renders it literally, and a bare "%%"
|
|
110
|
+
# with nothing after it is not a comment either. `^` matched at
|
|
111
|
+
# the start of the extracted content string too, which isn't a
|
|
112
|
+
# real line start unless something upstream already consumed a
|
|
113
|
+
# newline to get there; `(?<=\n)` requires an actual newline
|
|
114
|
+
# immediately before, without consuming it, so the string's own
|
|
115
|
+
# start is never mistaken for one. Mirrors
|
|
116
|
+
# Builders::Flowchart#strip_metadata_comments (flowchart.rb),
|
|
117
|
+
# the same rule for a node's `@{...}` metadata block -- including
|
|
118
|
+
# its indentation class: mermaid's own comment-strip regex uses
|
|
119
|
+
# JavaScript's `\s`, wider than ASCII space/tab (e.g. it matches a
|
|
120
|
+
# no-break space), so `[ \t]` alone left a comment unrecognised
|
|
121
|
+
# when it was indented with one.
|
|
122
|
+
ROUND_COMMENT_INDENT = '[\t\n\v\f\r \u00a0\u1680\u2000-\u200a' \
|
|
123
|
+
'\u2028\u2029\u202f\u205f\u3000\ufeff]'
|
|
124
|
+
ROUND_COMMENT_LINE = Regexp.new(
|
|
125
|
+
"(?<=\\n)#{ROUND_COMMENT_INDENT}*%%(?!\\{)" \
|
|
126
|
+
"[^\\r\\n]+\\r?\\n?",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
def rebuild_hierarchy
|
|
132
|
+
@root = nil
|
|
133
|
+
# Stack of [indent_size, node] for the current ancestor chain:
|
|
134
|
+
# each new node pops every entry whose indent is >= its own
|
|
135
|
+
# (those are siblings or deeper nodes it isn't nested under),
|
|
136
|
+
# then attaches under whatever remains on top.
|
|
137
|
+
indent_stack = []
|
|
138
|
+
@all_nodes.each { |node| rebuild_node(node, indent_stack) }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def rebuild_node(node, indent_stack)
|
|
142
|
+
indent_size = prepare_for_rebuild(node)
|
|
143
|
+
discard_siblings(indent_stack, indent_size)
|
|
144
|
+
attach_rebuilt_node(node, indent_stack)
|
|
145
|
+
indent_stack << [indent_size, node]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def prepare_for_rebuild(node)
|
|
149
|
+
indent_size = node.delete(:_indent_size) || 0
|
|
150
|
+
node[:children] = []
|
|
151
|
+
node.delete(:parent)
|
|
152
|
+
indent_size
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def discard_siblings(indent_stack, indent_size)
|
|
156
|
+
while indent_stack.any? && indent_stack.last[0] >= indent_size
|
|
157
|
+
indent_stack.pop
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def attach_rebuilt_node(node, indent_stack)
|
|
162
|
+
return install_root(node) if indent_stack.empty?
|
|
163
|
+
|
|
164
|
+
parent = indent_stack.last[1]
|
|
165
|
+
node[:level] = parent[:level] + 1
|
|
166
|
+
attach_to_parent(node, parent)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# A second node with no ancestor is a second root. Mermaid rejects
|
|
170
|
+
# it rather than silently discarding the first root and its subtree.
|
|
171
|
+
def install_root(node)
|
|
172
|
+
if @root
|
|
173
|
+
raise Etassia::Parser::ParseError, "Multiple roots are illegal"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
node[:level] = 0
|
|
177
|
+
@root = node
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def get_indent_size(indent_data)
|
|
181
|
+
return 0 if indent_data.nil?
|
|
182
|
+
return 0 if indent_data.is_a?(Array) && indent_data.empty?
|
|
183
|
+
|
|
184
|
+
indent_string(indent_data).length
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def calculate_level(indent_data)
|
|
188
|
+
return 0 if indent_data.nil?
|
|
189
|
+
return 0 if indent_data.is_a?(Array) && indent_data.empty?
|
|
190
|
+
|
|
191
|
+
indent_str = indent_string(indent_data)
|
|
192
|
+
return 0 if indent_str.empty?
|
|
193
|
+
|
|
194
|
+
spaces = indent_str.length
|
|
195
|
+
spaces.even? ? spaces / 2 : spaces / 4
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def indent_string(indent_data)
|
|
199
|
+
separator = ""
|
|
200
|
+
return indent_data.join(separator) if indent_data.is_a?(Array)
|
|
201
|
+
|
|
202
|
+
indent_data.to_s
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def extract_content(node_data)
|
|
206
|
+
return "" unless node_data[:content]
|
|
207
|
+
|
|
208
|
+
content = node_data[:content].to_s
|
|
209
|
+
return content unless node_data[:shape_round]
|
|
210
|
+
|
|
211
|
+
round_content(content, node_data[:round_quoted])
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Quoting protects the leading newline but not a real comment line:
|
|
215
|
+
# Mermaid strips those comments before quote lexing.
|
|
216
|
+
def round_content(content, quoted)
|
|
217
|
+
quoted ? strip_round_comments(content) : strip_round_extras(content)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def strip_round_comments(content)
|
|
221
|
+
content.gsub(ROUND_COMMENT_LINE, "")
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def strip_round_extras(content)
|
|
225
|
+
# Drop embedded comment-only lines first (matching mermaid's own
|
|
226
|
+
# pre-lexer pass), then the one leading newline that a
|
|
227
|
+
# multi-line node's own opening "(\n" legitimately introduces --
|
|
228
|
+
# mermaid's rendered output drops exactly that line break and
|
|
229
|
+
# nothing else. `\r?` handles a caller that passes CRLF source
|
|
230
|
+
# straight to this parser without going through
|
|
231
|
+
# `Source.normalize` (Engine's real render path always does).
|
|
232
|
+
strip_round_comments(content).sub(/\A\r?\n/, "")
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def extract_shape(node_data)
|
|
236
|
+
SHAPE_NAMES.find { |name| node_data[:"shape_#{name}"] } ||
|
|
237
|
+
"default"
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def find_parent(level)
|
|
241
|
+
# Parent is the last node at level - 1; level 0 is the root and
|
|
242
|
+
# never asks.
|
|
243
|
+
@level_stack[level - 1]
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Transform the nodes array into a tree structure
|
|
248
|
+
rule(nodes: subtree(:nodes)) do
|
|
249
|
+
builder = TreeBuilder.new
|
|
250
|
+
nodes_array = Array(nodes)
|
|
251
|
+
|
|
252
|
+
nodes_array.each do |node_data|
|
|
253
|
+
next unless node_data.is_a?(Hash)
|
|
254
|
+
|
|
255
|
+
next unless builder.node_data?(node_data)
|
|
256
|
+
|
|
257
|
+
builder.add_node(node_data)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Finalize to adjust levels and rebuild hierarchy
|
|
261
|
+
builder.finalize
|
|
262
|
+
|
|
263
|
+
{
|
|
264
|
+
root: builder.root,
|
|
265
|
+
nodes: builder.all_nodes,
|
|
266
|
+
}
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|