@diagrammo/dgmo 0.47.0 → 0.48.1
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.
- package/dist/advanced.js +11 -11
- package/dist/auto.js +16 -16
- package/dist/auto.mjs +41 -12
- package/dist/block.js +4 -4
- package/dist/{chunk-FNB3PNGF.js → chunk-7DTWEFUE.js} +3 -3
- package/dist/{chunk-2DXMZRWJ.js → chunk-B65G5DNL.js} +8 -1
- package/dist/{chunk-RZCF2VVM.js → chunk-B7JUCQUA.js} +3 -1
- package/dist/{chunk-DU7W62N5.js → chunk-FANX6UJR.js} +3 -3
- package/dist/{chunk-6FZYCKXB.js → chunk-HSH73DP6.js} +2 -2
- package/dist/{chunk-P4RH4OSP.js → chunk-ILKORZ3H.js} +2 -2
- package/dist/{chunk-Y5QGVTVC.js → chunk-JTSMEYIZ.js} +7 -3
- package/dist/{chunk-RCERWOJR.js → chunk-ODZVHU3R.js} +1 -2
- package/dist/{chunk-DZAQBW35.js → chunk-SGZYXELT.js} +3 -7
- package/dist/{chunk-CUVHQHZ7.js → chunk-THXCFRTU.js} +3 -3
- package/dist/{chunk-IH6ZMVQ5.js → chunk-UC7TFHUM.js} +29 -11
- package/dist/{chunk-EMS2SK26.js → chunk-ZBWMSD5N.js} +3 -1
- package/dist/cli.cjs +15 -15
- package/dist/{dgmo-router-DZCIDUO5.js → dgmo-router-X35VLZLM.js} +3 -3
- package/dist/element.js +16 -16
- package/dist/element.mjs +41 -12
- package/dist/{flowchart-renderer-ZAEOL4DX.js → flowchart-renderer-SNDSII4D.js} +2 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -4
- package/dist/{parser-FUSKFGZN.js → parser-2EWECDSA.js} +2 -2
- package/dist/{renderer-UWA5XS74.js → renderer-4JHRUBE6.js} +3 -3
- package/dist/{renderer-WZRREQ2P.js → renderer-5MYBMBYO.js} +2 -2
- package/dist/{renderer-TOCOVLHS.js → renderer-B2OMQIQG.js} +3 -3
- package/dist/{renderer-2XQQQ6QR.js → renderer-N43WR5GV.js} +3 -3
- package/dist/{renderer-T7YKC5VP.js → renderer-ZRNG54CW.js} +3 -3
- package/dist/{state-renderer-RFYHSP4K.js → state-renderer-B4DQBM6M.js} +2 -2
- package/package.json +23 -71
- package/dist/advanced.cjs +0 -71648
- package/dist/advanced.d.cts +0 -5719
- package/dist/auto.cjs +0 -70648
- package/dist/auto.d.cts +0 -54
- package/dist/block.cjs +0 -70208
- package/dist/block.d.cts +0 -115
- package/dist/editor.cjs +0 -526
- package/dist/editor.d.cts +0 -26
- package/dist/element.cjs +0 -70530
- package/dist/element.d.cts +0 -62
- package/dist/highlight.cjs +0 -852
- package/dist/highlight.d.cts +0 -49
- package/dist/index.cjs +0 -70491
- package/dist/index.d.cts +0 -160
- package/dist/pert.cjs +0 -325
- package/dist/pert.d.cts +0 -559
- package/dist/themes-1-CuKpeH.d.cts +0 -908
- package/src/advanced.ts +0 -873
- package/src/arc/parser.ts +0 -15
- package/src/arc/renderer.ts +0 -633
- package/src/auto/index.ts +0 -621
- package/src/auto/shared.ts +0 -370
- package/src/auto/styles.ts +0 -73
- package/src/block/layout.ts +0 -198
- package/src/block/parser.ts +0 -427
- package/src/block/renderer.ts +0 -484
- package/src/block/types.ts +0 -67
- package/src/boxes-and-lines/collapse.ts +0 -96
- package/src/boxes-and-lines/focus.ts +0 -257
- package/src/boxes-and-lines/label-placement.ts +0 -339
- package/src/boxes-and-lines/layout-grouped.ts +0 -912
- package/src/boxes-and-lines/layout-layered.ts +0 -722
- package/src/boxes-and-lines/layout-search.ts +0 -1678
- package/src/boxes-and-lines/layout.ts +0 -398
- package/src/boxes-and-lines/parser.ts +0 -1181
- package/src/boxes-and-lines/renderer.ts +0 -1465
- package/src/boxes-and-lines/types.ts +0 -66
- package/src/c4/layout.ts +0 -2441
- package/src/c4/parser.ts +0 -1158
- package/src/c4/renderer.ts +0 -2008
- package/src/c4/types.ts +0 -89
- package/src/chart-type-registry.ts +0 -529
- package/src/chart-types.ts +0 -225
- package/src/chart.ts +0 -685
- package/src/charts-d3/bar.ts +0 -338
- package/src/charts-d3/chord.ts +0 -140
- package/src/charts-d3/function.ts +0 -147
- package/src/charts-d3/funnel.ts +0 -104
- package/src/charts-d3/heatmap.ts +0 -145
- package/src/charts-d3/index.ts +0 -337
- package/src/charts-d3/interactions.ts +0 -477
- package/src/charts-d3/line.ts +0 -344
- package/src/charts-d3/mount.ts +0 -151
- package/src/charts-d3/pie.ts +0 -117
- package/src/charts-d3/polar.ts +0 -89
- package/src/charts-d3/radar.ts +0 -0
- package/src/charts-d3/sankey.ts +0 -0
- package/src/charts-d3/scatter.ts +0 -185
- package/src/charts-d3/shared.ts +0 -208
- package/src/class/layout.ts +0 -326
- package/src/class/parser.ts +0 -635
- package/src/class/renderer.ts +0 -793
- package/src/class/types.ts +0 -58
- package/src/cli-banner.ts +0 -106
- package/src/cli.ts +0 -1080
- package/src/colors-diagnostics.ts +0 -35
- package/src/colors.ts +0 -379
- package/src/completion-types.ts +0 -27
- package/src/completion.ts +0 -2668
- package/src/cycle/layout.ts +0 -1026
- package/src/cycle/parser.ts +0 -387
- package/src/cycle/renderer.ts +0 -653
- package/src/cycle/types.ts +0 -101
- package/src/d3.ts +0 -1434
- package/src/data-chart-parser.ts +0 -1007
- package/src/dgmo-router.ts +0 -296
- package/src/diagnostics-registry.ts +0 -108
- package/src/diagnostics.ts +0 -0
- package/src/dimensions.ts +0 -20
- package/src/directives-registry.ts +0 -396
- package/src/editor/dgmo.grammar +0 -76
- package/src/editor/dgmo.grammar.d.ts +0 -2
- package/src/editor/dgmo.grammar.js +0 -18
- package/src/editor/dgmo.grammar.terms.d.ts +0 -5
- package/src/editor/dgmo.grammar.terms.js +0 -35
- package/src/editor/highlight-api.ts +0 -596
- package/src/editor/highlight.ts +0 -36
- package/src/editor/index.ts +0 -27
- package/src/editor/keywords.ts +0 -128
- package/src/editor/tokens.ts +0 -30
- package/src/element/index.ts +0 -432
- package/src/embed/css.ts +0 -312
- package/src/embed/escape.ts +0 -15
- package/src/embed/index.ts +0 -280
- package/src/er/classify.ts +0 -228
- package/src/er/layout.ts +0 -530
- package/src/er/parser.ts +0 -750
- package/src/er/renderer.ts +0 -725
- package/src/er/types.ts +0 -52
- package/src/error-card.ts +0 -269
- package/src/event-line/diagnostics.ts +0 -117
- package/src/event-line/parser.ts +0 -643
- package/src/event-line/renderer.ts +0 -1742
- package/src/event-line/types.ts +0 -74
- package/src/fonts.ts +0 -3
- package/src/gantt/calculator.ts +0 -979
- package/src/gantt/parser.ts +0 -1641
- package/src/gantt/renderer.ts +0 -3787
- package/src/gantt/resolver.ts +0 -172
- package/src/gantt/types.ts +0 -206
- package/src/graph/diagnostics.ts +0 -64
- package/src/graph/edge-spline.ts +0 -29
- package/src/graph/flowchart-parser.ts +0 -759
- package/src/graph/flowchart-renderer.ts +0 -817
- package/src/graph/layout.ts +0 -472
- package/src/graph/notes.ts +0 -21
- package/src/graph/state-collapse.ts +0 -78
- package/src/graph/state-parser.ts +0 -574
- package/src/graph/state-renderer.ts +0 -653
- package/src/graph/types.ts +0 -65
- package/src/index.ts +0 -261
- package/src/infra/compute.ts +0 -1385
- package/src/infra/layout.ts +0 -888
- package/src/infra/parser.ts +0 -1234
- package/src/infra/renderer.ts +0 -2534
- package/src/infra/roles.ts +0 -75
- package/src/infra/types.ts +0 -216
- package/src/infra/validation.ts +0 -201
- package/src/journey-map/layout.ts +0 -490
- package/src/journey-map/parser.ts +0 -557
- package/src/journey-map/renderer.ts +0 -1580
- package/src/journey-map/types.ts +0 -47
- package/src/kanban/mutations.ts +0 -175
- package/src/kanban/parser.ts +0 -509
- package/src/kanban/renderer.ts +0 -1449
- package/src/kanban/types.ts +0 -39
- package/src/label-layout.ts +0 -368
- package/src/map/colorize.ts +0 -54
- package/src/map/completion.ts +0 -316
- package/src/map/context-labels.ts +0 -609
- package/src/map/data/PROVENANCE.json +0 -1
- package/src/map/data/README.md +0 -40
- package/src/map/data/airport-collisions.json +0 -1
- package/src/map/data/airports.json +0 -1
- package/src/map/data/gazetteer.json +0 -1
- package/src/map/data/lakes.json +0 -1
- package/src/map/data/mountain-ranges.json +0 -1
- package/src/map/data/na-lakes.json +0 -1
- package/src/map/data/na-land.json +0 -1
- package/src/map/data/region-names.json +0 -1
- package/src/map/data/rivers.json +0 -1
- package/src/map/data/types.ts +0 -121
- package/src/map/data/us-states.json +0 -1
- package/src/map/data/water-bodies.json +0 -1
- package/src/map/data/world-coarse.json +0 -1
- package/src/map/data/world-detail.json +0 -1
- package/src/map/diagnostics.ts +0 -134
- package/src/map/dimensions.ts +0 -150
- package/src/map/geo-query.ts +0 -295
- package/src/map/geo.ts +0 -438
- package/src/map/invert.ts +0 -111
- package/src/map/layout.ts +0 -5069
- package/src/map/legend-band.ts +0 -127
- package/src/map/load-data.ts +0 -186
- package/src/map/parser.ts +0 -783
- package/src/map/renderer.ts +0 -1445
- package/src/map/resolved-types.ts +0 -160
- package/src/map/resolver.ts +0 -1146
- package/src/map/types.ts +0 -194
- package/src/mindmap/collapse.ts +0 -67
- package/src/mindmap/layout.ts +0 -649
- package/src/mindmap/parser.ts +0 -430
- package/src/mindmap/renderer.ts +0 -637
- package/src/mindmap/text-wrap.ts +0 -218
- package/src/mindmap/types.ts +0 -56
- package/src/org/collapse.ts +0 -151
- package/src/org/layout.ts +0 -1262
- package/src/org/parser.ts +0 -519
- package/src/org/renderer.ts +0 -866
- package/src/org/resolver.ts +0 -636
- package/src/palettes/atlas.ts +0 -77
- package/src/palettes/blueprint.ts +0 -73
- package/src/palettes/catppuccin.ts +0 -84
- package/src/palettes/color-utils.ts +0 -443
- package/src/palettes/index.ts +0 -67
- package/src/palettes/nord.ts +0 -67
- package/src/palettes/registry.ts +0 -114
- package/src/palettes/slate.ts +0 -73
- package/src/palettes/tidewater.ts +0 -73
- package/src/palettes/tokyo-night.ts +0 -86
- package/src/palettes/types.ts +0 -88
- package/src/pert/analyzer.ts +0 -1183
- package/src/pert/diagnostics.ts +0 -110
- package/src/pert/index.ts +0 -43
- package/src/pert/internal.ts +0 -252
- package/src/pert/layout.ts +0 -971
- package/src/pert/monte-carlo.ts +0 -446
- package/src/pert/parser.ts +0 -1606
- package/src/pert/renderer.ts +0 -4009
- package/src/pert/share-normalize.ts +0 -62
- package/src/pert/types.ts +0 -604
- package/src/pyramid/parser.ts +0 -185
- package/src/pyramid/renderer.ts +0 -768
- package/src/pyramid/types.ts +0 -28
- package/src/quadrant/parser.ts +0 -18
- package/src/quadrant/renderer.ts +0 -638
- package/src/raci/diagnostics.ts +0 -213
- package/src/raci/index.ts +0 -23
- package/src/raci/mutations.ts +0 -321
- package/src/raci/parser.ts +0 -699
- package/src/raci/renderer.ts +0 -1627
- package/src/raci/types.ts +0 -82
- package/src/raci/variants.ts +0 -320
- package/src/render.ts +0 -268
- package/src/ring/parser.ts +0 -212
- package/src/ring/renderer.ts +0 -490
- package/src/ring/types.ts +0 -27
- package/src/sequence/collapse.ts +0 -174
- package/src/sequence/parser.ts +0 -1596
- package/src/sequence/participant-inference.ts +0 -152
- package/src/sequence/renderer.ts +0 -3168
- package/src/sequence/tag-resolution.ts +0 -164
- package/src/sharing.ts +0 -211
- package/src/sitemap/collapse.ts +0 -100
- package/src/sitemap/layout.ts +0 -925
- package/src/sitemap/parser.ts +0 -755
- package/src/sitemap/renderer.ts +0 -828
- package/src/sitemap/types.ts +0 -42
- package/src/slope/parser.ts +0 -18
- package/src/slope/renderer.ts +0 -378
- package/src/swimlane/diagnostics.ts +0 -94
- package/src/swimlane/layout.ts +0 -597
- package/src/swimlane/parser.ts +0 -613
- package/src/swimlane/renderer.ts +0 -681
- package/src/swimlane/types.ts +0 -151
- package/src/tech-radar/interactive.ts +0 -1129
- package/src/tech-radar/layout.ts +0 -197
- package/src/tech-radar/parser.ts +0 -436
- package/src/tech-radar/renderer.ts +0 -1239
- package/src/tech-radar/shared.ts +0 -151
- package/src/tech-radar/types.ts +0 -86
- package/src/themes.ts +0 -22
- package/src/timeline/parser.ts +0 -472
- package/src/timeline/renderer.ts +0 -3024
- package/src/timeline/types.ts +0 -33
- package/src/timeline/viz-parser.ts +0 -21
- package/src/treemap/diagnostics.ts +0 -62
- package/src/treemap/layout-radial.ts +0 -233
- package/src/treemap/layout.ts +0 -189
- package/src/treemap/parser.ts +0 -463
- package/src/treemap/renderer-radial.ts +0 -438
- package/src/treemap/renderer.ts +0 -572
- package/src/treemap/treemap-shared.ts +0 -309
- package/src/treemap/types.ts +0 -60
- package/src/utils/arrow-markers.ts +0 -51
- package/src/utils/arrows-diagnostics.ts +0 -62
- package/src/utils/arrows.ts +0 -227
- package/src/utils/brand.ts +0 -61
- package/src/utils/card.ts +0 -183
- package/src/utils/collapse-engine/tree-edges.ts +0 -115
- package/src/utils/collapse-engine/tree.ts +0 -117
- package/src/utils/d3-helpers.ts +0 -219
- package/src/utils/d3-types.ts +0 -9
- package/src/utils/description-helpers.ts +0 -38
- package/src/utils/duration.ts +0 -312
- package/src/utils/export-container.ts +0 -41
- package/src/utils/expr-eval.ts +0 -27
- package/src/utils/fit-canvas.ts +0 -64
- package/src/utils/hover-styles.ts +0 -673
- package/src/utils/inline-markdown.ts +0 -96
- package/src/utils/legend-constants.ts +0 -48
- package/src/utils/legend-d3.ts +0 -618
- package/src/utils/legend-integration.ts +0 -107
- package/src/utils/legend-layout.ts +0 -776
- package/src/utils/legend-svg.ts +0 -214
- package/src/utils/legend-types.ts +0 -272
- package/src/utils/name-normalize.ts +0 -163
- package/src/utils/note-box/constants.ts +0 -25
- package/src/utils/note-box/index.ts +0 -11
- package/src/utils/note-box/metrics.ts +0 -90
- package/src/utils/note-box/svg.ts +0 -331
- package/src/utils/notes/bounds.ts +0 -30
- package/src/utils/notes/build.ts +0 -131
- package/src/utils/notes/index.ts +0 -18
- package/src/utils/notes/model.ts +0 -19
- package/src/utils/notes/parse.ts +0 -131
- package/src/utils/notes/place.ts +0 -177
- package/src/utils/notes/resolve.ts +0 -88
- package/src/utils/number-format.ts +0 -36
- package/src/utils/parsing.ts +0 -953
- package/src/utils/reserved-key-registry.ts +0 -239
- package/src/utils/safe-href.ts +0 -38
- package/src/utils/scaling.ts +0 -97
- package/src/utils/svg-embed.ts +0 -251
- package/src/utils/tag-groups.ts +0 -607
- package/src/utils/text-measure.ts +0 -122
- package/src/utils/time-ticks.ts +0 -253
- package/src/utils/title-constants.ts +0 -24
- package/src/utils/visual-conventions.ts +0 -61
- package/src/utils/wrapped-desc.ts +0 -87
- package/src/venn/parser.ts +0 -18
- package/src/venn/renderer.ts +0 -1047
- package/src/version-control/diagnostics.ts +0 -67
- package/src/version-control/parser.ts +0 -544
- package/src/version-control/renderer.ts +0 -724
- package/src/version-control/types.ts +0 -100
- package/src/visualizations/parse.ts +0 -1560
- package/src/visualizations/types.ts +0 -262
- package/src/wireframe/layout.ts +0 -479
- package/src/wireframe/parser.ts +0 -1114
- package/src/wireframe/renderer.ts +0 -1318
- package/src/wireframe/types.ts +0 -110
- package/src/wordcloud/parser.ts +0 -18
- package/src/wordcloud/renderer.ts +0 -357
package/src/class/parser.ts
DELETED
|
@@ -1,635 +0,0 @@
|
|
|
1
|
-
import { resolveColorWithDiagnostic } from '../colors';
|
|
2
|
-
import type { PaletteColors } from '../palettes';
|
|
3
|
-
import {
|
|
4
|
-
makeDgmoError,
|
|
5
|
-
formatDgmoError,
|
|
6
|
-
NAME_DIAGNOSTIC_CODES,
|
|
7
|
-
nameMergedMessage,
|
|
8
|
-
} from '../diagnostics';
|
|
9
|
-
import { validateLabelCharacters } from '../utils/arrows';
|
|
10
|
-
import {
|
|
11
|
-
measureIndent,
|
|
12
|
-
parseFirstLine,
|
|
13
|
-
OPTION_NOCOLON_RE,
|
|
14
|
-
tryParseSharedOption,
|
|
15
|
-
} from '../utils/parsing';
|
|
16
|
-
import { normalizeName, displayName } from '../utils/name-normalize';
|
|
17
|
-
import { tryCollectNote, resolveNotes, type DiagramNote } from '../utils/notes';
|
|
18
|
-
import type { Writable } from '../utils/brand';
|
|
19
|
-
import type {
|
|
20
|
-
ParsedClassDiagram,
|
|
21
|
-
ClassNode,
|
|
22
|
-
ClassMember,
|
|
23
|
-
ClassModifier,
|
|
24
|
-
MemberVisibility,
|
|
25
|
-
RelationshipType,
|
|
26
|
-
} from './types';
|
|
27
|
-
|
|
28
|
-
// ============================================================
|
|
29
|
-
// Helpers
|
|
30
|
-
// ============================================================
|
|
31
|
-
|
|
32
|
-
function classId(name: string): string {
|
|
33
|
-
return normalizeName(name);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ============================================================
|
|
37
|
-
// Regex patterns
|
|
38
|
-
// ============================================================
|
|
39
|
-
|
|
40
|
-
// Class declaration: [modifier] ClassName [extends Parent] [implements Interface] [color] [as alias]
|
|
41
|
-
// Color is the universal §1.5 trailing-token form (a bare lowercase palette
|
|
42
|
-
// color word after structural slots). Multi-word names allowed
|
|
43
|
-
// (`Customer Service`); quote with `"name"` if name contains reserved chars.
|
|
44
|
-
// ClassName must start with uppercase. Captures (positional):
|
|
45
|
-
// 1: modifier (abstract|interface|enum) | undefined
|
|
46
|
-
// 2: quotedClassName | undefined
|
|
47
|
-
// 3: bareClassName | undefined
|
|
48
|
-
// 4: quotedExtends | undefined
|
|
49
|
-
// 5: bareExtends | undefined
|
|
50
|
-
// 6: quotedImplements | undefined
|
|
51
|
-
// 7: bareImplements | undefined
|
|
52
|
-
// 8: legacy bracket modifier | undefined
|
|
53
|
-
// 9: color (trailing token; recognized palette word) | undefined
|
|
54
|
-
// 10: alias literal (TD-18) | undefined
|
|
55
|
-
const CLASS_DECL_RE =
|
|
56
|
-
/^(?:(abstract|interface|enum)\s+)?(?:"([^"]+)"|([A-Z][^":]*?))(?:\s+extends\s+(?:"([^"]+)"|([A-Z][^":]*?)))?(?:\s+implements\s+(?:"([^"]+)"|([A-Z][^":]*?)))?(?:\s+\[(abstract|interface|enum)\])?(?:\s+(red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white))?(?:\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11}))?\s*$/;
|
|
57
|
-
|
|
58
|
-
// Relationship — arrow syntax (indented under source class).
|
|
59
|
-
// Arrows: --|> ..|> *-- o-- ..> ->
|
|
60
|
-
// Captures: [1]=arrow [2]=quotedTarget [3]=bareTarget [4]=label
|
|
61
|
-
// Bare target accepts both `[A-Z]…` (canonical class name) and
|
|
62
|
-
// `[a-z]…` (TD-18 alias literal) so alias references resolve here.
|
|
63
|
-
const INDENT_REL_ARROW_RE =
|
|
64
|
-
/^(--\|>|\.\.\|>|\*--|o--|\.\.>|->)\s*(?:"([^"]+)"|([A-Za-z][^":]*?))(?:\s+:?\s*(.+))?$/;
|
|
65
|
-
|
|
66
|
-
// Legacy top-level relationship regex (used only for detection/rejection)
|
|
67
|
-
// Captures: [1]=qSrc [2]=bareSrc [3]=arrow [4]=qTarget [5]=bareTarget [6]=label
|
|
68
|
-
const REL_ARROW_RE =
|
|
69
|
-
/^(?:"([^"]+)"|([A-Z][^":]*?))\s*(--\|>|\.\.\|>|\*--|o--|\.\.>|->)\s*(?:"([^"]+)"|([A-Z][^":]*?))(?:\s+:?\s*(.+))?$/;
|
|
70
|
-
|
|
71
|
-
// Member line patterns
|
|
72
|
-
const VISIBILITY_RE = /^([+\-#])\s*/;
|
|
73
|
-
const STATIC_SUFFIX_RE = /\{static\}\s*$/;
|
|
74
|
-
const METHOD_RE = /^(.+?)\(([^)]*)\)(?:\s*:\s*(.+))?$/;
|
|
75
|
-
const FIELD_RE = /^(.+?)\s*:\s*(.+)$/;
|
|
76
|
-
|
|
77
|
-
const ARROW_TO_TYPE: Record<string, RelationshipType> = {
|
|
78
|
-
'--|>': 'extends',
|
|
79
|
-
'..|>': 'implements',
|
|
80
|
-
'*--': 'composes',
|
|
81
|
-
'o--': 'aggregates',
|
|
82
|
-
'..>': 'depends',
|
|
83
|
-
'->': 'associates',
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
function parseVisibility(prefix: string): MemberVisibility {
|
|
87
|
-
switch (prefix) {
|
|
88
|
-
case '-':
|
|
89
|
-
return 'private';
|
|
90
|
-
case '#':
|
|
91
|
-
return 'protected';
|
|
92
|
-
default:
|
|
93
|
-
return 'public';
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// ============================================================
|
|
98
|
-
// Member parser
|
|
99
|
-
// ============================================================
|
|
100
|
-
|
|
101
|
-
function parseMember(
|
|
102
|
-
line: string,
|
|
103
|
-
lineNumber: number,
|
|
104
|
-
isEnum: boolean
|
|
105
|
-
): ClassMember | null {
|
|
106
|
-
let text = line.trim();
|
|
107
|
-
if (!text) return null;
|
|
108
|
-
|
|
109
|
-
// Enum values: plain text, no colon, no parens needed
|
|
110
|
-
if (isEnum) {
|
|
111
|
-
return {
|
|
112
|
-
name: text,
|
|
113
|
-
visibility: 'public',
|
|
114
|
-
isStatic: false,
|
|
115
|
-
isMethod: false,
|
|
116
|
-
lineNumber,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Extract visibility prefix
|
|
121
|
-
let visibility: MemberVisibility = 'public';
|
|
122
|
-
const visMatch = text.match(VISIBILITY_RE);
|
|
123
|
-
if (visMatch) {
|
|
124
|
-
// VISIBILITY_RE captures the prefix; group 1 always exists when matched.
|
|
125
|
-
visibility = parseVisibility(visMatch[1]!);
|
|
126
|
-
text = text.substring(visMatch[0].length);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Check for {static} suffix
|
|
130
|
-
let isStatic = false;
|
|
131
|
-
if (STATIC_SUFFIX_RE.test(text)) {
|
|
132
|
-
isStatic = true;
|
|
133
|
-
text = text.replace(STATIC_SUFFIX_RE, '').trim();
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Method: name(params) : returnType
|
|
137
|
-
const methodMatch = text.match(METHOD_RE);
|
|
138
|
-
if (methodMatch) {
|
|
139
|
-
// METHOD_RE: [1]=name, [2]=params, [3]=optional returnType.
|
|
140
|
-
const methodType = methodMatch[3]?.trim();
|
|
141
|
-
return {
|
|
142
|
-
name: methodMatch[1]!.trim(),
|
|
143
|
-
params: methodMatch[2]!.trim(),
|
|
144
|
-
...(methodType !== undefined && { type: methodType }),
|
|
145
|
-
visibility,
|
|
146
|
-
isStatic,
|
|
147
|
-
isMethod: true,
|
|
148
|
-
lineNumber,
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Field: name : type
|
|
153
|
-
const fieldMatch = text.match(FIELD_RE);
|
|
154
|
-
if (fieldMatch) {
|
|
155
|
-
// FIELD_RE: [1]=name, [2]=type.
|
|
156
|
-
return {
|
|
157
|
-
name: fieldMatch[1]!.trim(),
|
|
158
|
-
type: fieldMatch[2]!.trim(),
|
|
159
|
-
visibility,
|
|
160
|
-
isStatic,
|
|
161
|
-
isMethod: false,
|
|
162
|
-
lineNumber,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Plain name (field with no type)
|
|
167
|
-
return {
|
|
168
|
-
name: text,
|
|
169
|
-
visibility,
|
|
170
|
-
isStatic,
|
|
171
|
-
isMethod: false,
|
|
172
|
-
lineNumber,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// ============================================================
|
|
177
|
-
// Main parser
|
|
178
|
-
// ============================================================
|
|
179
|
-
|
|
180
|
-
export function parseClassDiagram(
|
|
181
|
-
content: string,
|
|
182
|
-
palette?: PaletteColors
|
|
183
|
-
): ParsedClassDiagram {
|
|
184
|
-
const lines = content.split('\n');
|
|
185
|
-
const options: Record<string, string> = {};
|
|
186
|
-
const result: Writable<ParsedClassDiagram> = {
|
|
187
|
-
type: 'class',
|
|
188
|
-
classes: [],
|
|
189
|
-
relationships: [],
|
|
190
|
-
options,
|
|
191
|
-
diagnostics: [],
|
|
192
|
-
error: null,
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const classMap = new Map<string, Writable<ClassNode>>();
|
|
196
|
-
const notes: DiagramNote[] = [];
|
|
197
|
-
|
|
198
|
-
// Per-parse alias literal → canonical class id (TD-18). Per C8.
|
|
199
|
-
const nameAliasMap = new Map<string, string>();
|
|
200
|
-
function resolveAliasName(token: string | undefined): string | undefined {
|
|
201
|
-
if (!token) return token;
|
|
202
|
-
const trimmed = token.trim();
|
|
203
|
-
const hit = nameAliasMap.get(trimmed);
|
|
204
|
-
if (hit !== undefined) {
|
|
205
|
-
// The aliasMap stores canonical-id; need to invert to canonical-name
|
|
206
|
-
// for callers like getOrCreateClass that hash the name. Walk classMap.
|
|
207
|
-
for (const [, node] of classMap) {
|
|
208
|
-
if (classId(node.name) === hit) return node.name;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return token;
|
|
212
|
-
}
|
|
213
|
-
let currentClass: Writable<ClassNode> | null = null;
|
|
214
|
-
let contentStarted = false;
|
|
215
|
-
|
|
216
|
-
function getOrCreateClass(
|
|
217
|
-
name: string,
|
|
218
|
-
lineNumber: number
|
|
219
|
-
): Writable<ClassNode> {
|
|
220
|
-
const key = classId(name);
|
|
221
|
-
const existing = classMap.get(key);
|
|
222
|
-
if (existing) {
|
|
223
|
-
const incomingDisplay = displayName(name);
|
|
224
|
-
const existingDisplay = displayName(existing.name);
|
|
225
|
-
if (incomingDisplay !== existingDisplay) {
|
|
226
|
-
result.diagnostics.push(
|
|
227
|
-
makeDgmoError(
|
|
228
|
-
lineNumber,
|
|
229
|
-
nameMergedMessage({
|
|
230
|
-
incomingDisplay,
|
|
231
|
-
incomingLine: lineNumber,
|
|
232
|
-
existingDisplay,
|
|
233
|
-
existingLine: existing.lineNumber,
|
|
234
|
-
}),
|
|
235
|
-
'warning',
|
|
236
|
-
NAME_DIAGNOSTIC_CODES.NAME_MERGED
|
|
237
|
-
)
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
return existing;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const node: Writable<ClassNode> = {
|
|
244
|
-
id: key,
|
|
245
|
-
name,
|
|
246
|
-
members: [],
|
|
247
|
-
lineNumber,
|
|
248
|
-
};
|
|
249
|
-
classMap.set(key, node);
|
|
250
|
-
result.classes.push(node);
|
|
251
|
-
return node;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
for (let i = 0; i < lines.length; i++) {
|
|
255
|
-
// In-bounds by loop guard.
|
|
256
|
-
const raw = lines[i]!;
|
|
257
|
-
const trimmed = raw.trim();
|
|
258
|
-
const lineNumber = i + 1;
|
|
259
|
-
const indent = measureIndent(raw);
|
|
260
|
-
|
|
261
|
-
// Skip empty lines
|
|
262
|
-
if (!trimmed) {
|
|
263
|
-
// Empty line ends current class context
|
|
264
|
-
if (indent === 0) currentClass = null;
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Skip comments
|
|
269
|
-
if (trimmed.startsWith('//')) continue;
|
|
270
|
-
|
|
271
|
-
// Note annotation (top-level): `note <ClassName> [inline body]` + an
|
|
272
|
-
// optional indented body. Checked before options so a note is never
|
|
273
|
-
// swallowed as an option; gated to indent 0 so an indented member
|
|
274
|
-
// starting with "note" stays a member. `note -> X` is excluded.
|
|
275
|
-
if (indent === 0) {
|
|
276
|
-
const noteResult = tryCollectNote(
|
|
277
|
-
lines,
|
|
278
|
-
i,
|
|
279
|
-
indent,
|
|
280
|
-
palette,
|
|
281
|
-
result.diagnostics
|
|
282
|
-
);
|
|
283
|
-
if (noteResult) {
|
|
284
|
-
currentClass = null;
|
|
285
|
-
contentStarted = true;
|
|
286
|
-
if (noteResult.note) notes.push(noteResult.note);
|
|
287
|
-
i = noteResult.lastIndex;
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// First line: bare chart type + optional title (new syntax)
|
|
293
|
-
if (!contentStarted && indent === 0 && i === 0) {
|
|
294
|
-
const firstLine = parseFirstLine(trimmed);
|
|
295
|
-
if (firstLine?.chartType === 'class') {
|
|
296
|
-
if (firstLine.title) {
|
|
297
|
-
result.title = firstLine.title;
|
|
298
|
-
result.titleLineNumber = lineNumber;
|
|
299
|
-
}
|
|
300
|
-
continue;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Space-separated options before content (new syntax): `no-auto-color`
|
|
305
|
-
// Only match lines starting with a lowercase token (options), not uppercase (class names)
|
|
306
|
-
if (!contentStarted && indent === 0 && /^[a-z]/.test(trimmed)) {
|
|
307
|
-
if (trimmed.toLowerCase() === 'no-auto-color') {
|
|
308
|
-
result.diagnostics.push(
|
|
309
|
-
makeDgmoError(
|
|
310
|
-
lineNumber,
|
|
311
|
-
'"no-auto-color" was removed — colors are always auto-assigned from the palette; delete this line.',
|
|
312
|
-
'warning'
|
|
313
|
-
)
|
|
314
|
-
);
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
if (tryParseSharedOption(trimmed, options)) {
|
|
318
|
-
continue;
|
|
319
|
-
}
|
|
320
|
-
const optMatch = trimmed.match(OPTION_NOCOLON_RE);
|
|
321
|
-
if (optMatch) {
|
|
322
|
-
// OPTION_NOCOLON_RE: [1]=key, [2]=value.
|
|
323
|
-
const key = optMatch[1]!.toLowerCase();
|
|
324
|
-
const value = optMatch[2]!.trim();
|
|
325
|
-
// Don't swallow lines that look like class modifier keywords
|
|
326
|
-
if (key !== 'abstract' && key !== 'interface' && key !== 'enum') {
|
|
327
|
-
options[key] = value;
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Indented lines = relationships or members of current class
|
|
334
|
-
if (indent > 0 && currentClass) {
|
|
335
|
-
// Try indented relationship arrow: --|> TargetClass [label]
|
|
336
|
-
// Captures: [1]=arrow [2]=quotedTarget [3]=bareTarget [4]=label
|
|
337
|
-
const indentRel = trimmed.match(INDENT_REL_ARROW_RE);
|
|
338
|
-
if (indentRel) {
|
|
339
|
-
// INDENT_REL_ARROW_RE: [1]=arrow (always present on match).
|
|
340
|
-
const arrow = indentRel[1]!;
|
|
341
|
-
const rawTarget = (indentRel[2] ?? indentRel[3] ?? '').trim();
|
|
342
|
-
// TD-18: resolve alias literal → canonical class name.
|
|
343
|
-
const targetName = resolveAliasName(rawTarget) ?? rawTarget;
|
|
344
|
-
const label = indentRel[4]?.trim();
|
|
345
|
-
|
|
346
|
-
getOrCreateClass(targetName, lineNumber);
|
|
347
|
-
|
|
348
|
-
if (label) {
|
|
349
|
-
result.diagnostics.push(
|
|
350
|
-
...validateLabelCharacters(label, lineNumber)
|
|
351
|
-
);
|
|
352
|
-
}
|
|
353
|
-
result.relationships.push({
|
|
354
|
-
source: currentClass.id,
|
|
355
|
-
target: classId(targetName),
|
|
356
|
-
// INDENT_REL_ARROW_RE only matches arrows present in ARROW_TO_TYPE.
|
|
357
|
-
type: ARROW_TO_TYPE[arrow]!,
|
|
358
|
-
...(label && { label }),
|
|
359
|
-
lineNumber,
|
|
360
|
-
});
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const member = parseMember(
|
|
365
|
-
trimmed,
|
|
366
|
-
lineNumber,
|
|
367
|
-
currentClass.modifier === 'enum'
|
|
368
|
-
);
|
|
369
|
-
if (member) {
|
|
370
|
-
currentClass.members.push(member);
|
|
371
|
-
}
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// At indent 0 — this ends any previous class context
|
|
376
|
-
currentClass = null;
|
|
377
|
-
contentStarted = true;
|
|
378
|
-
|
|
379
|
-
// Reject top-level relationship arrows — must be indented under source class
|
|
380
|
-
// Captures: [1]=qSrc [2]=bareSrc [3]=arrow [4]=qTarget [5]=bareTarget [6]=label
|
|
381
|
-
const relArrow = trimmed.match(REL_ARROW_RE);
|
|
382
|
-
if (relArrow) {
|
|
383
|
-
const sourceName = (relArrow[1] ?? relArrow[2] ?? '').trim();
|
|
384
|
-
const arrow = relArrow[3];
|
|
385
|
-
const targetName = (relArrow[4] ?? relArrow[5] ?? '').trim();
|
|
386
|
-
result.diagnostics.push(
|
|
387
|
-
makeDgmoError(
|
|
388
|
-
lineNumber,
|
|
389
|
-
`Relationship "${sourceName} ${arrow} ${targetName}" must be indented under the source class "${sourceName}"`,
|
|
390
|
-
'warning'
|
|
391
|
-
)
|
|
392
|
-
);
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// Try class declaration
|
|
397
|
-
// Captures: [1]=modifier [2]=qName [3]=bareName [4]=qExt [5]=bareExt
|
|
398
|
-
// [6]=qImpl [7]=bareImpl [8]=bracketMod [9]=color
|
|
399
|
-
const classDecl = trimmed.match(CLASS_DECL_RE);
|
|
400
|
-
if (classDecl) {
|
|
401
|
-
const prefixModifier = classDecl[1] as ClassModifier | undefined;
|
|
402
|
-
const name = (classDecl[2] ?? classDecl[3] ?? '').trim();
|
|
403
|
-
const extendsRaw = classDecl[4] ?? classDecl[5];
|
|
404
|
-
const extendsParent = extendsRaw ? extendsRaw.trim() : undefined;
|
|
405
|
-
const implementsRaw = classDecl[6] ?? classDecl[7];
|
|
406
|
-
const implementsInterface = implementsRaw
|
|
407
|
-
? implementsRaw.trim()
|
|
408
|
-
: undefined;
|
|
409
|
-
const bracketModifier = classDecl[8] as ClassModifier | undefined;
|
|
410
|
-
const modifier = prefixModifier ?? bracketModifier;
|
|
411
|
-
const colorName = classDecl[9]?.trim();
|
|
412
|
-
const color = colorName
|
|
413
|
-
? resolveColorWithDiagnostic(
|
|
414
|
-
colorName,
|
|
415
|
-
lineNumber,
|
|
416
|
-
result.diagnostics,
|
|
417
|
-
palette
|
|
418
|
-
)
|
|
419
|
-
: undefined;
|
|
420
|
-
|
|
421
|
-
const aliasLiteral = classDecl[10];
|
|
422
|
-
|
|
423
|
-
const node = getOrCreateClass(name, lineNumber);
|
|
424
|
-
if (modifier) node.modifier = modifier;
|
|
425
|
-
if (color) node.color = color;
|
|
426
|
-
if (aliasLiteral) nameAliasMap.set(aliasLiteral, classId(name));
|
|
427
|
-
// Update line number to the declaration line (may have been created by relationship)
|
|
428
|
-
node.lineNumber = lineNumber;
|
|
429
|
-
|
|
430
|
-
// Inline extends creates an extends relationship — resolve alias.
|
|
431
|
-
if (extendsParent) {
|
|
432
|
-
const ext = resolveAliasName(extendsParent) ?? extendsParent;
|
|
433
|
-
getOrCreateClass(ext, lineNumber);
|
|
434
|
-
result.relationships.push({
|
|
435
|
-
source: classId(name),
|
|
436
|
-
target: classId(ext),
|
|
437
|
-
type: 'extends',
|
|
438
|
-
lineNumber,
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Inline implements creates an implements relationship — resolve alias.
|
|
443
|
-
if (implementsInterface) {
|
|
444
|
-
const impl =
|
|
445
|
-
resolveAliasName(implementsInterface) ?? implementsInterface;
|
|
446
|
-
getOrCreateClass(impl, lineNumber);
|
|
447
|
-
result.relationships.push({
|
|
448
|
-
source: classId(name),
|
|
449
|
-
target: classId(impl),
|
|
450
|
-
type: 'implements',
|
|
451
|
-
lineNumber,
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
currentClass = node;
|
|
456
|
-
continue;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Catch-all: nothing matched this line
|
|
460
|
-
result.diagnostics.push(
|
|
461
|
-
makeDgmoError(
|
|
462
|
-
lineNumber,
|
|
463
|
-
`Unexpected line: '${trimmed}'. Classes start uppercase at indent 0 ('Ship'); members are indented and colon-typed ('+ name: string', '+ sail(): void'); relationships are indented arrows ('--|> Vessel'). (§10)`,
|
|
464
|
-
'warning'
|
|
465
|
-
)
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
// Resolve note refs against class names (forward refs OK — runs after all
|
|
470
|
-
// classes parsed). The id→note binding is recomputed in layout; this pass
|
|
471
|
-
// only surfaces unknown/ambiguous/duplicate diagnostics.
|
|
472
|
-
if (notes.length > 0) {
|
|
473
|
-
result.notes = notes;
|
|
474
|
-
resolveNotes(
|
|
475
|
-
notes,
|
|
476
|
-
result.classes.map((c) => ({ id: c.id, label: c.name })),
|
|
477
|
-
result.diagnostics
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// Validation
|
|
482
|
-
if (result.classes.length === 0 && !result.error) {
|
|
483
|
-
const diag = makeDgmoError(
|
|
484
|
-
1,
|
|
485
|
-
'No classes found. Add class declarations like "ClassName" or "ClassName [interface]".'
|
|
486
|
-
);
|
|
487
|
-
result.diagnostics.push(diag);
|
|
488
|
-
result.error = formatDgmoError(diag);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// Warn about isolated classes (not in any relationship)
|
|
492
|
-
if (
|
|
493
|
-
result.classes.length >= 2 &&
|
|
494
|
-
result.relationships.length >= 1 &&
|
|
495
|
-
!result.error
|
|
496
|
-
) {
|
|
497
|
-
const connectedIds = new Set<string>();
|
|
498
|
-
for (const rel of result.relationships) {
|
|
499
|
-
// rel.source/target are class ids set via classId() at relationship parse
|
|
500
|
-
// eslint-disable-next-line name-normalize/required-at-insertion
|
|
501
|
-
connectedIds.add(rel.source);
|
|
502
|
-
// eslint-disable-next-line name-normalize/required-at-insertion
|
|
503
|
-
connectedIds.add(rel.target);
|
|
504
|
-
}
|
|
505
|
-
for (const cls of result.classes) {
|
|
506
|
-
if (!connectedIds.has(cls.id)) {
|
|
507
|
-
result.diagnostics.push(
|
|
508
|
-
makeDgmoError(
|
|
509
|
-
cls.lineNumber,
|
|
510
|
-
`Class "${cls.name}" is not connected to any other class`,
|
|
511
|
-
'warning'
|
|
512
|
-
)
|
|
513
|
-
);
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
return result;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// ============================================================
|
|
522
|
-
// Detection helper
|
|
523
|
-
// ============================================================
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* Detect if content looks like a class diagram without explicit `chart: class`.
|
|
527
|
-
* Requires class-like patterns (capitalized names with modifiers or UML relationships).
|
|
528
|
-
* Must not false-positive on flowcharts.
|
|
529
|
-
*/
|
|
530
|
-
export function looksLikeClassDiagram(content: string): boolean {
|
|
531
|
-
const lines = content.split('\n');
|
|
532
|
-
|
|
533
|
-
let hasModifier = false;
|
|
534
|
-
let hasRelationship = false;
|
|
535
|
-
let hasIndentedMember = false;
|
|
536
|
-
let hasClassDecl = false;
|
|
537
|
-
|
|
538
|
-
for (const line of lines) {
|
|
539
|
-
const trimmed = line.trim();
|
|
540
|
-
if (!trimmed || trimmed.startsWith('//')) continue;
|
|
541
|
-
|
|
542
|
-
// Skip metadata
|
|
543
|
-
if (/^(chart|title)\s*:/i.test(trimmed)) continue;
|
|
544
|
-
// Skip new-style first line (bare chart type)
|
|
545
|
-
if (/^class(\s|$)/i.test(trimmed)) continue;
|
|
546
|
-
|
|
547
|
-
const indent = measureIndent(line);
|
|
548
|
-
|
|
549
|
-
if (indent === 0) {
|
|
550
|
-
// Check for bare modifier keyword: `abstract ClassName`, `interface ClassName`, `enum ClassName`
|
|
551
|
-
if (/^(abstract|interface|enum)\s+[A-Z][A-Za-z0-9_]*/i.test(trimmed)) {
|
|
552
|
-
hasModifier = true;
|
|
553
|
-
hasClassDecl = true;
|
|
554
|
-
}
|
|
555
|
-
// Check for old modifier pattern: ClassName [abstract|interface|enum]
|
|
556
|
-
if (
|
|
557
|
-
/^[A-Z][A-Za-z0-9_]*\s+\[(abstract|interface|enum)\]/i.test(trimmed)
|
|
558
|
-
) {
|
|
559
|
-
hasModifier = true;
|
|
560
|
-
hasClassDecl = true;
|
|
561
|
-
}
|
|
562
|
-
// Check for inline extends/implements in class declaration
|
|
563
|
-
if (/^[A-Z][A-Za-z0-9_]*\s+(extends|implements)\s+[A-Z]/.test(trimmed)) {
|
|
564
|
-
hasRelationship = true;
|
|
565
|
-
hasClassDecl = true;
|
|
566
|
-
}
|
|
567
|
-
// Check for relationship arrows
|
|
568
|
-
if (REL_ARROW_RE.test(trimmed)) {
|
|
569
|
-
hasRelationship = true;
|
|
570
|
-
}
|
|
571
|
-
// Check for plain class declaration (capitalized name only)
|
|
572
|
-
if (CLASS_DECL_RE.test(trimmed)) {
|
|
573
|
-
hasClassDecl = true;
|
|
574
|
-
}
|
|
575
|
-
} else {
|
|
576
|
-
// Indented lines that look like members
|
|
577
|
-
if (/^[+\-#]?\s*\w+.*[:(]/.test(trimmed)) {
|
|
578
|
-
hasIndentedMember = true;
|
|
579
|
-
}
|
|
580
|
-
// Indented relationship arrows
|
|
581
|
-
if (INDENT_REL_ARROW_RE.test(trimmed)) {
|
|
582
|
-
hasRelationship = true;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// Require modifier OR (relationship + class-like declarations)
|
|
588
|
-
// Modifier alone is strong enough signal
|
|
589
|
-
if (hasModifier) return true;
|
|
590
|
-
|
|
591
|
-
// Relationship keywords/arrows + at least one class declaration + member
|
|
592
|
-
if (hasRelationship && hasClassDecl && hasIndentedMember) return true;
|
|
593
|
-
|
|
594
|
-
return false;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// ============================================================
|
|
598
|
-
// Symbol extraction (for completion API)
|
|
599
|
-
// ============================================================
|
|
600
|
-
|
|
601
|
-
import type { DiagramSymbols } from '../completion-types';
|
|
602
|
-
|
|
603
|
-
/**
|
|
604
|
-
* Extract class names (entities) from class diagram document text.
|
|
605
|
-
* Used by the dgmo completion API for ghost hints and popup completions.
|
|
606
|
-
*/
|
|
607
|
-
export function extractSymbols(docText: string): DiagramSymbols {
|
|
608
|
-
const entities: string[] = [];
|
|
609
|
-
let inMetadata = true;
|
|
610
|
-
for (const rawLine of docText.split('\n')) {
|
|
611
|
-
const line = rawLine.trim();
|
|
612
|
-
// Skip old-style colon metadata and new-style first line / space-separated options
|
|
613
|
-
if (
|
|
614
|
-
inMetadata &&
|
|
615
|
-
(/^[a-z-]+\s*:/i.test(line) || /^class(\s|$)/i.test(line))
|
|
616
|
-
)
|
|
617
|
-
continue;
|
|
618
|
-
if (inMetadata && line.toLowerCase() === 'no-auto-color') continue;
|
|
619
|
-
if (inMetadata && /^[a-z]/.test(line) && OPTION_NOCOLON_RE.test(line)) {
|
|
620
|
-
const key = line.match(OPTION_NOCOLON_RE)![1]!.toLowerCase();
|
|
621
|
-
if (key !== 'abstract' && key !== 'interface' && key !== 'enum') continue;
|
|
622
|
-
}
|
|
623
|
-
inMetadata = false;
|
|
624
|
-
if (line.length === 0 || /^\s/.test(rawLine)) continue;
|
|
625
|
-
const m = CLASS_DECL_RE.exec(line);
|
|
626
|
-
if (m) {
|
|
627
|
-
const name = (m[2] ?? m[3] ?? '').trim();
|
|
628
|
-
if (name && !entities.includes(name)) entities.push(name);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
return {
|
|
632
|
-
kind: 'class',
|
|
633
|
-
entities,
|
|
634
|
-
};
|
|
635
|
-
}
|