diff --git a/.build/jsonSchema.ts b/.build/jsonSchema.ts index 6fd8ca3f54..08367011dc 100644 --- a/.build/jsonSchema.ts +++ b/.build/jsonSchema.ts @@ -25,6 +25,7 @@ const MERMAID_CONFIG_DIAGRAM_KEYS = [ 'sankey', 'block', 'packet', + 'treeView', ] as const; /** diff --git a/cypress/integration/rendering/treeView.spec.ts b/cypress/integration/rendering/treeView.spec.ts new file mode 100644 index 0000000000..dcb1b20556 --- /dev/null +++ b/cypress/integration/rendering/treeView.spec.ts @@ -0,0 +1,45 @@ +import { imgSnapshotTest } from '../../helpers/util'; + +describe('TreeView Diagram', () => { + it('should render a simple treeView diagram', () => { + imgSnapshotTest( + `treeView-beta + file1.ts` + ); + }); + + it('should render a complex treeView diagram', () => { + imgSnapshotTest( + `treeView-beta + root + folder1 + file1.js + file2.ts + folder2 + file3.spec.ts + folder3 + file4.ts + file5.ts + folder4 + file6.ts + file7.ts` + ); + }); + + it('should render a complex treeView diagram with multiple roots', () => { + imgSnapshotTest( + `treeView-beta + folder1 + file1.js + file2.ts + folder2 + file3.spec.ts + folder3 + file4.ts + file5.ts + folder4 + file6.ts + file7.ts` + ); + }); +}); diff --git a/demos/index.html b/demos/index.html index 61a86a2aa0..6673864330 100644 --- a/demos/index.html +++ b/demos/index.html @@ -88,6 +88,9 @@

Packet

  • Layered Blocks

  • +
  • +

    TreeView

    +
  • diff --git a/demos/treeView.html b/demos/treeView.html new file mode 100644 index 0000000000..08f877c65c --- /dev/null +++ b/demos/treeView.html @@ -0,0 +1,66 @@ + + + + + + Mermaid Quick Test Page + + + + + +

    TreeView Diagram Demo

    + +
    +
    +        treeView-beta
    +            packages
    +                mermaid
    +                    src
    +                parser
    +    
    + +
    +        ---
    +        config:
    +            treeView:
    +                rowIndent: 40
    +                lineThickness: 2
    +            themeVariables:
    +                treeView:
    +                    fontSize: '30px'
    +        ---
    +        treeView-beta
    +            packages
    +                mermaid
    +                    src
    +                parser
    +
    +    
    +
    + + + + + diff --git a/docs/config/setup/interfaces/mermaid.MermaidConfig.md b/docs/config/setup/interfaces/mermaid.MermaidConfig.md index ef5a4313a9..2407cb2c87 100644 --- a/docs/config/setup/interfaces/mermaid.MermaidConfig.md +++ b/docs/config/setup/interfaces/mermaid.MermaidConfig.md @@ -111,7 +111,7 @@ should not change unless content is changed. #### Defined in -[packages/mermaid/src/config.type.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L163) +[packages/mermaid/src/config.type.ts:164](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L164) --- @@ -155,7 +155,7 @@ See #### Defined in -[packages/mermaid/src/config.type.ts:165](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L165) +[packages/mermaid/src/config.type.ts:166](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L166) --- @@ -246,7 +246,7 @@ This option decides the amount of logging to be used by mermaid. #### Defined in -[packages/mermaid/src/config.type.ts:166](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L166) +[packages/mermaid/src/config.type.ts:167](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L167) --- @@ -401,7 +401,7 @@ This is useful when you want to control how to handle syntax errors in your appl #### Defined in -[packages/mermaid/src/config.type.ts:172](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L172) +[packages/mermaid/src/config.type.ts:173](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L173) --- @@ -448,13 +448,23 @@ You may also use `themeCSS` to override this value. --- +### treeView + +• `Optional` **treeView**: `TreeViewDiagramConfig` + +#### Defined in + +[packages/mermaid/src/config.type.ts:163](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L163) + +--- + ### wrap • `Optional` **wrap**: `boolean` #### Defined in -[packages/mermaid/src/config.type.ts:164](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L164) +[packages/mermaid/src/config.type.ts:165](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts#L165) --- diff --git a/docs/config/setup/modules/defaultConfig.md b/docs/config/setup/modules/defaultConfig.md index 512d240362..0f0ace33ca 100644 --- a/docs/config/setup/modules/defaultConfig.md +++ b/docs/config/setup/modules/defaultConfig.md @@ -14,7 +14,7 @@ #### Defined in -[packages/mermaid/src/defaultConfig.ts:275](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L275) +[packages/mermaid/src/defaultConfig.ts:279](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/defaultConfig.ts#L279) --- diff --git a/docs/syntax/treeView.md b/docs/syntax/treeView.md new file mode 100644 index 0000000000..19dd5b15cc --- /dev/null +++ b/docs/syntax/treeView.md @@ -0,0 +1,90 @@ +> **Warning** +> +> ## THIS IS AN AUTOGENERATED FILE. DO NOT EDIT. +> +> ## Please edit the corresponding file in [/packages/mermaid/src/docs/syntax/treeView.md](../../packages/mermaid/src/docs/syntax/treeView.md). + +# TreeView Diagram (v\+) + +## Introduction + +A TreeView diagram is used to represent hierarchical data in the form of a directory-like structure. + +## Syntax + +The structure of the tree depends only on indentation. + +``` +treeView-beta + + + + + +``` + +## Examples + +```mermaid-example +treeView-beta + packages + mermaid + src + parser +``` + +```mermaid +treeView-beta + packages + mermaid + src + parser +``` + +```mermaid-example +--- +config: + treeView: + rowIndent: 5 + themeVariables: + treeView: + fontSize: '20px' +--- +treeView-beta + packages + mermaid + src + parser +``` + +```mermaid +--- +config: + treeView: + rowIndent: 5 + themeVariables: + treeView: + fontSize: '20px' +--- +treeView-beta + packages + mermaid + src + parser +``` + +## Config Variables + +| Property | Description | Default Value | +| ------------- | ------------------------- | ------------- | +| rowIndent | Indentation for each row | 10 | +| paddingX | Horizontal padding of row | 5 | +| paddingY | Vertical padding of row | 5 | +| lineThickness | Thickness of the line | 1 | + +### Theme Variables + +| Property | Description | Default Value | +| --------- | ---------------------- | ------------- | +| fontSize | Font size of the label | '16px' | +| lineColor | Color of the line | 'black' | diff --git a/packages/mermaid/src/config.type.ts b/packages/mermaid/src/config.type.ts index b7cf27e72b..f4489b1f4f 100644 --- a/packages/mermaid/src/config.type.ts +++ b/packages/mermaid/src/config.type.ts @@ -160,6 +160,7 @@ export interface MermaidConfig { sankey?: SankeyDiagramConfig; packet?: PacketDiagramConfig; block?: BlockDiagramConfig; + treeView?: TreeViewDiagramConfig; dompurifyConfig?: DOMPurifyConfiguration; wrap?: boolean; fontSize?: number; @@ -1460,6 +1461,30 @@ export interface PacketDiagramConfig extends BaseDiagramConfig { export interface BlockDiagramConfig extends BaseDiagramConfig { padding?: number; } +/** + * The object containing configurations specific for treeView diagrams. + * + * This interface was referenced by `MermaidConfig`'s JSON-Schema + * via the `definition` "TreeViewDiagramConfig". + */ +export interface TreeViewDiagramConfig extends BaseDiagramConfig { + /** + * Horizontal distance between rows differing by one level + */ + rowIndent?: number; + /** + * Horizontal padding of label + */ + paddingX?: number; + /** + * Vertical padding of label + */ + paddingY?: number; + /** + * Thickness of the line + */ + lineThickness?: number; +} /** * This interface was referenced by `MermaidConfig`'s JSON-Schema * via the `definition` "FontConfig". diff --git a/packages/mermaid/src/defaultConfig.ts b/packages/mermaid/src/defaultConfig.ts index 727842bba1..6018e3d4ae 100644 --- a/packages/mermaid/src/defaultConfig.ts +++ b/packages/mermaid/src/defaultConfig.ts @@ -260,6 +260,10 @@ const config: RequiredDeep = { packet: { ...defaultConfigJson.packet, }, + treeView: { + ...defaultConfigJson.treeView, + useWidth: undefined, + }, }; const keyify = (obj: any, prefix = ''): string[] => diff --git a/packages/mermaid/src/diagram-api/diagram-orchestration.ts b/packages/mermaid/src/diagram-api/diagram-orchestration.ts index 55d05c9aaa..0eec1b435c 100644 --- a/packages/mermaid/src/diagram-api/diagram-orchestration.ts +++ b/packages/mermaid/src/diagram-api/diagram-orchestration.ts @@ -22,6 +22,7 @@ import mindmap from '../diagrams/mindmap/detector.js'; import sankey from '../diagrams/sankey/sankeyDetector.js'; import { packet } from '../diagrams/packet/detector.js'; import block from '../diagrams/block/blockDetector.js'; +import treeView from '../diagrams/treeView/detector.js'; import { registerLazyLoadedDiagrams } from './detectType.js'; import { registerDiagram } from './diagramAPI.js'; @@ -90,6 +91,7 @@ export const addDiagrams = () => { sankey, packet, xychart, - block + block, + treeView ); }; diff --git a/packages/mermaid/src/diagrams/treeView/db.ts b/packages/mermaid/src/diagrams/treeView/db.ts new file mode 100644 index 0000000000..db18b948c5 --- /dev/null +++ b/packages/mermaid/src/diagrams/treeView/db.ts @@ -0,0 +1,79 @@ +import type { TreeViewDiagramConfig } from '../../config.type.js'; +import type { TreeViewDB, Node } from './types.js'; +import { getConfig as getCommonConfig } from '../../config.js'; +import DEFAULT_CONFIG from '../../defaultConfig.js'; +import { + clear as commonClear, + getAccDescription, + getAccTitle, + getDiagramTitle, + setAccDescription, + setAccTitle, + setDiagramTitle, +} from '../common/commonDb.js'; +import { cleanAndMerge } from '../../utils.js'; +import { ImperativeState } from '../../utils/imperativeState.js'; + +interface TreeViewState { + cnt: number; + stack: Node[]; +} + +const state = new ImperativeState(() => ({ + cnt: 1, + stack: [ + { + id: 0, + level: -1, + name: '/', + children: [], + }, + ], +})); + +const clear = () => { + state.reset(); + commonClear(); +}; + +const getRoot = () => { + return state.records.stack[0]; +}; + +const getCount = () => state.records.cnt; + +const defaultConfig: Required = DEFAULT_CONFIG.treeView; + +const getConfig = (): Required => { + return cleanAndMerge(defaultConfig, getCommonConfig().treeView); +}; + +const addNode = (level: number, name: string) => { + while (level <= state.records.stack[state.records.stack.length - 1].level) { + state.records.stack.pop(); + } + const node = { + id: state.records.cnt++, + level, + name, + children: [], + }; + state.records.stack[state.records.stack.length - 1].children.push(node); + state.records.stack.push(node); +}; + +const db: TreeViewDB = { + clear, + addNode, + getRoot, + getCount, + getConfig, + getAccTitle, + getAccDescription, + getDiagramTitle, + setAccDescription, + setAccTitle, + setDiagramTitle, +}; + +export default db; diff --git a/packages/mermaid/src/diagrams/treeView/detector.ts b/packages/mermaid/src/diagrams/treeView/detector.ts new file mode 100644 index 0000000000..ef4d84e44e --- /dev/null +++ b/packages/mermaid/src/diagrams/treeView/detector.ts @@ -0,0 +1,21 @@ +import type { DiagramDetector, DiagramLoader } from '../../diagram-api/types.js'; +import type { ExternalDiagramDefinition } from '../../diagram-api/types.js'; + +const id = 'treeView'; + +const detector: DiagramDetector = (txt) => { + return /^\s*treeView-beta/.test(txt); +}; + +const loader: DiagramLoader = async () => { + const { diagram } = await import('./diagram.js'); + return { id, diagram }; +}; + +const plugin: ExternalDiagramDefinition = { + id, + detector, + loader, +}; + +export default plugin; diff --git a/packages/mermaid/src/diagrams/treeView/diagram.ts b/packages/mermaid/src/diagrams/treeView/diagram.ts new file mode 100644 index 0000000000..19d432d968 --- /dev/null +++ b/packages/mermaid/src/diagrams/treeView/diagram.ts @@ -0,0 +1,12 @@ +import type { DiagramDefinition } from '../../diagram-api/types.js'; +import { parser } from './parser.js'; +import db from './db.js'; +import renderer from './renderer.js'; +import styles from './styles.js'; + +export const diagram: DiagramDefinition = { + db, + renderer, + parser, + styles, +}; diff --git a/packages/mermaid/src/diagrams/treeView/parser.ts b/packages/mermaid/src/diagrams/treeView/parser.ts new file mode 100644 index 0000000000..7d3668b85a --- /dev/null +++ b/packages/mermaid/src/diagrams/treeView/parser.ts @@ -0,0 +1,18 @@ +import type { ParserDefinition } from '../../diagram-api/types.js'; +import { log } from '../../logger.js'; +import { populateCommonDb } from '../common/populateCommonDb.js'; +import db from './db.js'; +import { parse, type TreeView } from '@mermaid-js/parser'; + +const populate = (ast: TreeView) => { + populateCommonDb(ast, db); + ast.nodes.map((node) => db.addNode(node.indent.length, node.name)); +}; + +export const parser: ParserDefinition = { + parse: async (input: string): Promise => { + const ast = await parse('treeView', input); + log.debug(ast); + populate(ast); + }, +}; diff --git a/packages/mermaid/src/diagrams/treeView/renderer.ts b/packages/mermaid/src/diagrams/treeView/renderer.ts new file mode 100644 index 0000000000..150f765b76 --- /dev/null +++ b/packages/mermaid/src/diagrams/treeView/renderer.ts @@ -0,0 +1,126 @@ +import type { DiagramRenderer, DrawDefinition } from '../../diagram-api/types.js'; +import { log } from '../../logger.js'; +import { selectSvgElement } from '../../rendering-util/selectSvgElement.js'; +import type { D3SVGElement, TreeViewDB } from './types.js'; +import { configureSvgSize } from '../../setupGraphViewbox.js'; +import type { TreeViewDiagramConfig } from '../../config.type.js'; +import type { Node } from './types.js'; + +const positionLabel = ( + x: number, + y: number, + node: Node, + domElem: D3SVGElement, + config: Required +) => { + const label = domElem + .append('text') + .text(node.name) + .attr('dominant-baseline', 'middle') + .attr('class', 'treeView-node-label'); + const { height: labelHeight, width: labelWidth } = label.node()!.getBBox(); + const height = labelHeight + config.paddingY * 2; + const width = labelWidth + config.paddingX * 2; + label.attr('x', x + config.paddingX); + label.attr('y', y + height / 2); + node.BBox = { + x, + y, + width, + height, + }; +}; + +const positionLine = ( + domElem: D3SVGElement, + x1: number, + y1: number, + x2: number, + y2: number, + lineThickness: number +) => { + return domElem + .append('line') + .attr('x1', x1) + .attr('y1', y1) + .attr('x2', x2) + .attr('y2', y2) + .attr('stroke-width', lineThickness) + .attr('class', 'treeView-node-line'); +}; + +const drawTree = ( + elem: D3SVGElement, + root: Node, + config: Required +) => { + let totalHeight = 0; + let totalWidth = 0; + const drawNode = ( + elem: D3SVGElement, + node: Node, + config: Required, + depth: number + ) => { + const indent = depth * (config.rowIndent + config.paddingX); + positionLabel(indent, totalHeight, node, elem, config); + const { height, width } = node.BBox!; + positionLine( + elem, + indent - config.rowIndent, + totalHeight + height / 2, + indent, + totalHeight + height / 2, + config.lineThickness + ); + + totalWidth = Math.max(totalWidth, indent + width); + totalHeight += height; + }; + + const processNode = (node: Node, depth = 0) => { + drawNode(elem, node, config, depth); + node.children.forEach((child) => { + processNode(child, depth + 1); + }); + const { x, y, height } = node.BBox!; + if (node.children.length) { + const { y: endY, height: endHeight } = node.children[node.children.length - 1].BBox!; + positionLine( + elem, + x + config.paddingX, + y + height, + x + config.paddingX, + endY + endHeight / 2 + config.lineThickness / 2, + config.lineThickness + ); + } + }; + + processNode(root); + return { totalHeight, totalWidth }; +}; + +const draw: DrawDefinition = (text, id, _ver, diagObj) => { + log.debug('Rendering treeView diagram\n' + text); + + const db = diagObj.db as TreeViewDB; + const root = db.getRoot(); + const config = db.getConfig(); + + const svg = selectSvgElement(id); + const treeElem = svg.append('g'); + treeElem.attr('class', 'tree-view'); + + const { totalHeight, totalWidth } = drawTree(treeElem, root, config); + /* -${config.lineThickness/2} is required for a line with x coordinate = 0 + as there is overflow to the left due to the line being centered */ + svg.attr('viewBox', `-${config.lineThickness / 2} 0 ${totalWidth} ${totalHeight}`); + configureSvgSize(svg, totalHeight, totalWidth, config.useMaxWidth); +}; + +const renderer: DiagramRenderer = { + draw, +}; + +export default renderer; diff --git a/packages/mermaid/src/diagrams/treeView/styles.ts b/packages/mermaid/src/diagrams/treeView/styles.ts new file mode 100644 index 0000000000..19c04a175d --- /dev/null +++ b/packages/mermaid/src/diagrams/treeView/styles.ts @@ -0,0 +1,26 @@ +import type { DiagramStylesProvider } from '../../diagram-api/types.js'; +import { cleanAndMerge } from '../../utils.js'; +import type { TreeViewDiagramStyles } from './types.js'; + +const defaultTreeViewDiagramStyles: Required = { + fontSize: '16px', + lineColor: 'black', +}; + +const styles: DiagramStylesProvider = ({ + treeView, +}: { + treeView?: TreeViewDiagramStyles; +}): string => { + const { fontSize, lineColor } = cleanAndMerge(defaultTreeViewDiagramStyles, treeView); + return ` + .treeView-node-label { + font-size: ${fontSize}; + } + .treeView-node-line { + stroke: ${lineColor}; + } + `; +}; + +export default styles; diff --git a/packages/mermaid/src/diagrams/treeView/types.ts b/packages/mermaid/src/diagrams/treeView/types.ts new file mode 100644 index 0000000000..2cccec3860 --- /dev/null +++ b/packages/mermaid/src/diagrams/treeView/types.ts @@ -0,0 +1,31 @@ +import type { TreeViewDiagramConfig } from '../../config.type.js'; +import type { DiagramDBBase } from '../../diagram-api/types.js'; +import type { Selection } from 'd3-selection'; + +interface BBox { + x: number; + y: number; + width: number; + height: number; +} + +export interface Node { + id: number; + level: number; + name: string; + BBox?: BBox; + children: Node[]; +} + +export interface TreeViewDB extends DiagramDBBase { + addNode: (level: number, name: string) => void; + getRoot: () => Node; + getCount: () => number; +} + +export interface TreeViewDiagramStyles { + fontSize?: string; + lineColor?: string; +} + +export type D3SVGElement = Selection; diff --git a/packages/mermaid/src/docs/.vitepress/config.ts b/packages/mermaid/src/docs/.vitepress/config.ts index 940fc6940a..7d1ee24c58 100644 --- a/packages/mermaid/src/docs/.vitepress/config.ts +++ b/packages/mermaid/src/docs/.vitepress/config.ts @@ -157,6 +157,7 @@ function sidebarSyntax() { { text: 'XY Chart 🔥', link: '/syntax/xyChart' }, { text: 'Block Diagram 🔥', link: '/syntax/block' }, { text: 'Packet 🔥', link: '/syntax/packet' }, + { text: 'TreeView 🔥', link: '/syntax/treeView' }, { text: 'Other Examples', link: '/syntax/examples' }, ], }, diff --git a/packages/mermaid/src/docs/syntax/treeView.md b/packages/mermaid/src/docs/syntax/treeView.md new file mode 100644 index 0000000000..030d3032cd --- /dev/null +++ b/packages/mermaid/src/docs/syntax/treeView.md @@ -0,0 +1,60 @@ +# TreeView Diagram (v+) + +## Introduction + +A TreeView diagram is used to represent hierarchical data in the form of a directory-like structure. + +## Syntax + +The structure of the tree depends only on indentation. + +``` +treeView-beta + + + + + +``` + +## Examples + +```mermaid-example +treeView-beta + packages + mermaid + src + parser +``` + +```mermaid-example +--- +config: + treeView: + rowIndent: 5 + themeVariables: + treeView: + fontSize: '20px' +--- +treeView-beta + packages + mermaid + src + parser +``` + +## Config Variables + +| Property | Description | Default Value | +| ------------- | ------------------------- | ------------- | +| rowIndent | Indentation for each row | 10 | +| paddingX | Horizontal padding of row | 5 | +| paddingY | Vertical padding of row | 5 | +| lineThickness | Thickness of the line | 1 | + +### Theme Variables + +| Property | Description | Default Value | +| --------- | ---------------------- | ------------- | +| fontSize | Font size of the label | '16px' | +| lineColor | Color of the line | 'black' | diff --git a/packages/mermaid/src/mermaidAPI.spec.ts b/packages/mermaid/src/mermaidAPI.spec.ts index 7958f397e0..adcb2f678d 100644 --- a/packages/mermaid/src/mermaidAPI.spec.ts +++ b/packages/mermaid/src/mermaidAPI.spec.ts @@ -31,6 +31,7 @@ vi.mock('./diagrams/xychart/xychartRenderer.js'); vi.mock('./diagrams/requirement/requirementRenderer.js'); vi.mock('./diagrams/sequence/sequenceRenderer.js'); vi.mock('./diagrams/state/stateRenderer-v2.js'); +vi.mock('./diagrams/treeView/renderer.js'); // ------------------------------------- @@ -733,6 +734,7 @@ describe('mermaidAPI', () => { { textDiagramType: 'requirementDiagram', expectedType: 'requirement' }, { textDiagramType: 'sequenceDiagram', expectedType: 'sequence' }, { textDiagramType: 'stateDiagram-v2', expectedType: 'stateDiagram' }, + { textDiagramType: 'treeView-beta', expectedType: 'treeView' }, ]; describe('accessibility', () => { diff --git a/packages/mermaid/src/schemas/config.schema.yaml b/packages/mermaid/src/schemas/config.schema.yaml index d798ec63b2..bfaf5e04ae 100644 --- a/packages/mermaid/src/schemas/config.schema.yaml +++ b/packages/mermaid/src/schemas/config.schema.yaml @@ -52,6 +52,7 @@ required: - sankey - packet - block + - treeView properties: theme: description: | @@ -237,6 +238,8 @@ properties: $ref: '#/$defs/PacketDiagramConfig' block: $ref: '#/$defs/BlockDiagramConfig' + treeView: + $ref: '#/$defs/TreeViewDiagramConfig' dompurifyConfig: title: DOM Purify Configuration description: Configuration options to pass to the `dompurify` library. @@ -2093,6 +2096,34 @@ $defs: # JSON Schema definition (maybe we should move these to a separate file) minimum: 0 default: 8 + TreeViewDiagramConfig: + title: TreeView Diagram Config + allOf: [{ $ref: '#/$defs/BaseDiagramConfig' }] + description: The object containing configurations specific for treeView diagrams. + type: object + unevaluatedProperties: false + properties: + rowIndent: + description: Horizontal distance between rows differing by one level + type: number + minimum: 0 + default: 10 + paddingX: + description: Horizontal padding of label + type: number + minimum: 0 + default: 5 + paddingY: + description: Vertical padding of label + type: number + minimum: 0 + default: 5 + lineThickness: + description: Thickness of the line + type: number + minimum: 0 + default: 1 + FontCalculator: title: Font Calculator description: | diff --git a/packages/mermaid/src/styles.spec.ts b/packages/mermaid/src/styles.spec.ts index 70e9e7ec54..963ee22213 100644 --- a/packages/mermaid/src/styles.spec.ts +++ b/packages/mermaid/src/styles.spec.ts @@ -29,6 +29,7 @@ import timeline from './diagrams/timeline/styles.js'; import mindmap from './diagrams/mindmap/styles.js'; import packet from './diagrams/packet/styles.js'; import block from './diagrams/block/styles.js'; +import treeView from './diagrams/treeView/styles.js'; import themes from './themes/index.js'; function checkValidStylisCSSStyleSheet(stylisString: string) { @@ -99,6 +100,7 @@ describe('styles', () => { block, timeline, packet, + treeView, })) { test(`should return a valid style for diagram ${diagramId} and theme ${themeId}`, async () => { const { default: getStyles, addStylesForDiagram } = await import('./styles.js'); diff --git a/packages/parser/langium-config.json b/packages/parser/langium-config.json index c750f049d5..b7b8ba117b 100644 --- a/packages/parser/langium-config.json +++ b/packages/parser/langium-config.json @@ -15,6 +15,11 @@ "id": "pie", "grammar": "src/language/pie/pie.langium", "fileExtensions": [".mmd", ".mermaid"] + }, + { + "id": "treeView", + "grammar": "src/language/treeView/treeView.langium", + "fileExtensions": [".mmd", ".mermaid"] } ], "mode": "production", diff --git a/packages/parser/src/language/index.ts b/packages/parser/src/language/index.ts index 9f1d92ba8a..7299c30c92 100644 --- a/packages/parser/src/language/index.ts +++ b/packages/parser/src/language/index.ts @@ -11,6 +11,8 @@ export { isPacketBlock, isPie, isPieSection, + TreeView, + TreeNode, } from './generated/ast.js'; export { InfoGeneratedModule, @@ -23,3 +25,4 @@ export * from './common/index.js'; export * from './info/index.js'; export * from './packet/index.js'; export * from './pie/index.js'; +export * from './treeView/index.js'; diff --git a/packages/parser/src/language/treeView/index.ts b/packages/parser/src/language/treeView/index.ts new file mode 100644 index 0000000000..fd3c604b08 --- /dev/null +++ b/packages/parser/src/language/treeView/index.ts @@ -0,0 +1 @@ +export * from './module.js'; diff --git a/packages/parser/src/language/treeView/module.ts b/packages/parser/src/language/treeView/module.ts new file mode 100644 index 0000000000..b1cb3146cd --- /dev/null +++ b/packages/parser/src/language/treeView/module.ts @@ -0,0 +1,77 @@ +import type { + DefaultSharedCoreModuleContext, + LangiumCoreServices, + LangiumSharedCoreServices, + Module, + PartialLangiumCoreServices, +} from 'langium'; +import { + EmptyFileSystem, + createDefaultCoreModule, + createDefaultSharedCoreModule, + inject, +} from 'langium'; + +import { CommonValueConverter } from '../common/valueConverter.js'; +import { MermaidGeneratedSharedModule, TreeViewGeneratedModule } from '../generated/module.js'; +import { TreeViewTokenBuilder } from './tokenBuilder.js'; + +/** + * Declaration of `TreeView` services. + */ +interface TreeViewAddedServices { + parser: { + TokenBuilder: TreeViewTokenBuilder; + ValueConverter: CommonValueConverter; + }; +} + +/** + * Union of Langium default services and `TreeView` services. + */ +export type TreeViewServices = LangiumCoreServices & TreeViewAddedServices; + +/** + * Dependency injection module that overrides Langium default services and + * contributes the declared `TreeView` services. + */ +export const TreeViewModule: Module< + TreeViewServices, + PartialLangiumCoreServices & TreeViewAddedServices +> = { + parser: { + TokenBuilder: () => new TreeViewTokenBuilder(), + ValueConverter: () => new CommonValueConverter(), + }, +}; + +/** + * Create the full set of services required by Langium. + * + * First inject the shared services by merging two modules: + * - Langium default shared services + * - Services generated by langium-cli + * + * Then inject the language-specific services by merging three modules: + * - Langium default language-specific services + * - Services generated by langium-cli + * - Services specified in this file + * @param context - Optional module context with the LSP connection + * @returns An object wrapping the shared services and the language-specific services + */ +export function createTreeViewServices(context: DefaultSharedCoreModuleContext = EmptyFileSystem): { + shared: LangiumSharedCoreServices; + TreeView: TreeViewServices; +} { + const shared: LangiumSharedCoreServices = inject( + createDefaultSharedCoreModule(context), + MermaidGeneratedSharedModule + ); + const TreeView: TreeViewServices = inject( + createDefaultCoreModule({ shared }), + TreeViewGeneratedModule, + TreeViewModule + ); + shared.ServiceRegistry.register(TreeView); + return { shared, TreeView }; +} diff --git a/packages/parser/src/language/treeView/tokenBuilder.ts b/packages/parser/src/language/treeView/tokenBuilder.ts new file mode 100644 index 0000000000..5aacc5f903 --- /dev/null +++ b/packages/parser/src/language/treeView/tokenBuilder.ts @@ -0,0 +1,7 @@ +import { AbstractMermaidTokenBuilder } from '../common/index.js'; + +export class TreeViewTokenBuilder extends AbstractMermaidTokenBuilder { + public constructor() { + super(['treeView-beta']); + } +} diff --git a/packages/parser/src/language/treeView/treeView.langium b/packages/parser/src/language/treeView/treeView.langium new file mode 100644 index 0000000000..f574c3312b --- /dev/null +++ b/packages/parser/src/language/treeView/treeView.langium @@ -0,0 +1,34 @@ +grammar TreeView + +entry TreeView: + SPACELIST? + "treeView-beta" + TitleAndAccessibilities? + nodes+=TreeNode* + SPACELIST? +; + +TreeNode: + indent=SPACELIST name=TEXT +; + +interface Common { + accDescr?: string; + accTitle?: string; + title?: string; +} + +fragment TitleAndAccessibilities: + ((accDescr=ACC_DESCR | accTitle=ACC_TITLE | title=TITLE))+ +; + +hidden terminal EOL: /\s*(\r?\n)+/; +terminal SPACELIST: /\s+/; +terminal TEXT: /\S+/; +terminal ACC_DESCR: /[\t ]*accDescr(?:[\t ]*:([^\n\r]*?(?=%%)|[^\n\r]*)|\s*{([^}]*)})/; +terminal ACC_TITLE: /[\t ]*accTitle[\t ]*:(?:[^\n\r]*?(?=%%)|[^\n\r]*)/; +terminal TITLE: /[\t ]*title(?:[\t ][^\n\r]*?(?=%%)|[\t ][^\n\r]*|)/; + + + + diff --git a/packages/parser/src/parse.ts b/packages/parser/src/parse.ts index 992b965060..0c6852b6f1 100644 --- a/packages/parser/src/parse.ts +++ b/packages/parser/src/parse.ts @@ -1,8 +1,8 @@ import type { LangiumParser, ParseResult } from 'langium'; -import type { Info, Packet, Pie } from './index.js'; +import type { Info, Packet, Pie, TreeView } from './index.js'; -export type DiagramAST = Info | Packet | Pie; +export type DiagramAST = Info | Packet | Pie | TreeView; const parsers: Record = {}; const initializers = { @@ -21,11 +21,17 @@ const initializers = { const parser = createPieServices().Pie.parser.LangiumParser; parsers.pie = parser; }, + treeView: async () => { + const { createTreeViewServices } = await import('./language/treeView/index.js'); + const parser = createTreeViewServices().TreeView.parser.LangiumParser; + parsers.treeView = parser; + }, } as const; export async function parse(diagramType: 'info', text: string): Promise; export async function parse(diagramType: 'packet', text: string): Promise; export async function parse(diagramType: 'pie', text: string): Promise; +export async function parse(diagramType: 'treeView', text: string): Promise; export async function parse( diagramType: keyof typeof initializers, text: string