diff --git a/packages/mermaid/src/diagrams/git/gitGraphDiagram.ts b/packages/mermaid/src/diagrams/git/gitGraphDiagram.ts index 2a9efdb59c..e5534140b1 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphDiagram.ts +++ b/packages/mermaid/src/diagrams/git/gitGraphDiagram.ts @@ -1,12 +1,12 @@ // @ts-ignore: JISON doesn't support types -import gitGraphParser from './parser/gitGraph.jison'; +import { parser } from './gitGraphParser.js'; import gitGraphDb from './gitGraphAst.js'; import gitGraphRenderer from './gitGraphRenderer.js'; import gitGraphStyles from './styles.js'; import type { DiagramDefinition } from '../../diagram-api/types.js'; export const diagram: DiagramDefinition = { - parser: gitGraphParser, + parser: parser, db: gitGraphDb, renderer: gitGraphRenderer, styles: gitGraphStyles, diff --git a/packages/mermaid/src/diagrams/git/gitGraphParser.ts b/packages/mermaid/src/diagrams/git/gitGraphParser.ts index d193109b47..c9488173dd 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphParser.ts +++ b/packages/mermaid/src/diagrams/git/gitGraphParser.ts @@ -4,14 +4,78 @@ import type { ParserDefinition } from '../../diagram-api/types.js'; import { log } from '../../logger.js'; import { populateCommonDb } from '../common/populateCommonDb.js'; import db from './gitGraphAst.js'; +import { commitType } from './gitGraphAst.js'; +import type { + CheckoutAst, + CherryPickingAst, + MergeAst, + CommitAst, + BranchAst, +} from './gitGraphTypes.js'; -const populate = (ast: any) => { +const populate = (ast: GitGraph) => { populateCommonDb(ast, db); for (const statement of ast.statements) { - log.debug(statement); + parseStatement(statement); } }; +const parseStatement = (statement: any) => { + switch (statement.$type) { + case 'Commit': + parseCommit(statement); + break; + case 'Branch': + parseBranch(statement); + break; + case 'Merge': + parseMerge(statement); + break; + case 'Checkout': + parseCheckout(statement); + break; + case 'CherryPicking': + parseCherryPicking(statement); + break; + default: + log.warn(`Unknown statement type`); + } +}; + +const parseCommit = (commit: CommitAst) => { + const id = commit.id; + const message = commit.message ?? ''; + const tags = commit.tags ?? []; + const type = commit.type !== undefined ? commitType[commit.type] : 0; + db.commit(message, id, type, tags); +}; + +const parseBranch = (branch: BranchAst) => { + const name = branch.name; + const order = branch.order ?? 0; + db.branch(name, order); +}; + +const parseMerge = (merge: MergeAst) => { + const branch = merge.branch; + const id = merge.id ?? ''; + const tags = merge.tags ?? []; + const type = merge.type !== undefined ? commitType[merge.type] : 0; + db.merge(branch, id, type, tags); +}; + +const parseCheckout = (checkout: CheckoutAst) => { + const branch = checkout.branch; + db.checkout(branch); +}; + +const parseCherryPicking = (cherryPicking: CherryPickingAst) => { + const id = cherryPicking.id; + const tags = cherryPicking.tags ?? []; + const parent = cherryPicking.parent; + db.cherryPick(id, '', tags, parent); +}; + export const parser: ParserDefinition = { parse: async (input: string): Promise => { const ast: GitGraph = await parse('gitGraph', input); diff --git a/packages/mermaid/src/diagrams/git/gitGraphTypes.ts b/packages/mermaid/src/diagrams/git/gitGraphTypes.ts index 92bc3617a5..36d88fd974 100644 --- a/packages/mermaid/src/diagrams/git/gitGraphTypes.ts +++ b/packages/mermaid/src/diagrams/git/gitGraphTypes.ts @@ -16,7 +16,7 @@ export interface GitGraph { statements: Statement[]; } -export type Statement = CommitAst | Branch | Merge | Checkout | CherryPicking; +export type Statement = CommitAst | BranchAst | MergeAst | CheckoutAst | CherryPickingAst; export interface CommitAst { $type: 'Commit'; @@ -26,13 +26,13 @@ export interface CommitAst { type?: 'NORMAL' | 'REVERSE' | 'HIGHLIGHT'; } -export interface Branch { +export interface BranchAst { $type: 'Branch'; name: string; order?: number; } -export interface Merge { +export interface MergeAst { $type: 'Merge'; branch: string; id?: string; @@ -40,12 +40,12 @@ export interface Merge { type?: 'NORMAL' | 'REVERSE' | 'HIGHLIGHT'; } -export interface Checkout { +export interface CheckoutAst { $type: 'Checkout'; branch: string; } -export interface CherryPicking { +export interface CherryPickingAst { $type: 'CherryPicking'; id: string; tags?: string[]; diff --git a/packages/parser/src/language/gitGraph/gitGraph.langium b/packages/parser/src/language/gitGraph/gitGraph.langium index 88adaf3f71..8751dccc15 100644 --- a/packages/parser/src/language/gitGraph/gitGraph.langium +++ b/packages/parser/src/language/gitGraph/gitGraph.langium @@ -1,6 +1,28 @@ grammar GitGraph -import "../common/common"; +interface Common { + accDescr?: string; + accTitle?: string; + title?: string; +} + +fragment TitleAndAccessibilities: + ((accDescr=ACC_DESCR | accTitle=ACC_TITLE | title=TITLE) EOL)+ +; + +fragment EOL returns string: + NEWLINE+ | EOF +; + +terminal NEWLINE: /\r?\n/; +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]*|)/; + +hidden terminal WHITESPACE: /[\t ]+/; +hidden terminal YAML: /---[\t ]*\r?\n(?:[\S\s]*?\r?\n)?---(?:\r?\n|(?!\S))/; +hidden terminal DIRECTIVE: /[\t ]*%%{[\S\s]*?}%%(?:\r?\n|(?!\S))/; +hidden terminal SINGLE_LINE_COMMENT: /[\t ]*%%[^\n\r]*/; entry GitGraph: NEWLINE* @@ -62,6 +84,8 @@ CherryPicking: |'parent:' id=STRING )* EOL; + + terminal INT returns number: /[0-9]+(?=\s)/; terminal ID returns string: /\w([-\./\w]*[-\w])?/; terminal STRING: /"[^"]*"|'[^']*'/; diff --git a/packages/parser/tests/gitGraph.test.ts b/packages/parser/tests/gitGraph.test.ts index aff69977aa..850b34bf9a 100644 --- a/packages/parser/tests/gitGraph.test.ts +++ b/packages/parser/tests/gitGraph.test.ts @@ -8,11 +8,23 @@ describe('gitGraph', () => { const result = parse(`gitGraph`); expect(result.value.$type).toBe(GitGraph); expect(result.value.statements).toHaveLength(0); + expect(result.lexerErrors).toHaveLength(0); + expect(result.parserErrors).toHaveLength(0); }); it('should handle gitGraph with one statement', () => { - const result = parse(`gitGraph\n A`); + const result = parse(`gitGraph\n commit\n`); expect(result.value.$type).toBe(GitGraph); + expect(result.lexerErrors).toHaveLength(0); + expect(result.parserErrors).toHaveLength(0); + expect(result.value.statements).toHaveLength(1); + }); + + it('should handle gitGraph with multiple statements and use accTitle', () => { + const result = parse(`gitGraph\n commit\n commit\n accTitle: title\n commit\n`); + expect(result.value.$type).toBe(GitGraph); + expect(result.lexerErrors).toHaveLength(0); + expect(result.parserErrors).toHaveLength(0); }); }); });