mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-30 05:36:43 +02:00
Added the langium module for gitGraph
This commit is contained in:
@@ -138,6 +138,7 @@ mergeStatement
|
|||||||
| MERGE ref commitTags COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $5, $7, $3)}
|
| MERGE ref commitTags COMMIT_ID STR COMMIT_TYPE commitType {yy.merge($2, $5, $7, $3)}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
commitStatement
|
commitStatement
|
||||||
: COMMIT commit_arg {yy.commit($2)}
|
: COMMIT commit_arg {yy.commit($2)}
|
||||||
| COMMIT commitTags {yy.commit('','',yy.commitType.NORMAL,$2)}
|
| COMMIT commitTags {yy.commit('','',yy.commitType.NORMAL,$2)}
|
||||||
|
@@ -15,6 +15,11 @@
|
|||||||
"id": "pie",
|
"id": "pie",
|
||||||
"grammar": "src/language/pie/pie.langium",
|
"grammar": "src/language/pie/pie.langium",
|
||||||
"fileExtensions": [".mmd", ".mermaid"]
|
"fileExtensions": [".mmd", ".mermaid"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "gitGraph",
|
||||||
|
"grammar": "src/language/gitGraph/gitGraph.langium",
|
||||||
|
"fileExtensions": [".mmd", ".mermaid"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"mode": "production",
|
"mode": "production",
|
||||||
|
84
packages/parser/src/language/gitGraph/gitGraph.langium
Normal file
84
packages/parser/src/language/gitGraph/gitGraph.langium
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
grammar GitGraph
|
||||||
|
|
||||||
|
import "../common/common";
|
||||||
|
entry GitGraph:
|
||||||
|
NEWLINE*
|
||||||
|
'gitGraph' Direction? ':'?
|
||||||
|
NEWLINE*
|
||||||
|
(
|
||||||
|
Options?
|
||||||
|
NEWLINE*
|
||||||
|
(TitleAndAccessibilities |
|
||||||
|
statements+=Statement |
|
||||||
|
NEWLINE)*
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
Statement
|
||||||
|
: Commit
|
||||||
|
| Branch
|
||||||
|
| Merge
|
||||||
|
| Checkout
|
||||||
|
| CherryPicking
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
Options:
|
||||||
|
'options' '{' rawOptions+=STRING* '}' EOL;
|
||||||
|
|
||||||
|
Direction:
|
||||||
|
dir=('LR' | 'TB' | 'BT') EOL;
|
||||||
|
|
||||||
|
Commit:
|
||||||
|
'commit' properties+=CommitProperty* EOL;
|
||||||
|
|
||||||
|
CommitProperty
|
||||||
|
: CommitId
|
||||||
|
| CommitMessage
|
||||||
|
| Tags
|
||||||
|
| CommitType
|
||||||
|
;
|
||||||
|
|
||||||
|
CommitId:
|
||||||
|
'id:' id=STRING;
|
||||||
|
|
||||||
|
CommitMessage:
|
||||||
|
'msg:'? message=STRING;
|
||||||
|
|
||||||
|
Tags:
|
||||||
|
'tag:' tags=STRING;
|
||||||
|
|
||||||
|
CommitType:
|
||||||
|
'type:' name=('NORMAL' | 'REVERSE' | 'HIGHLIGHT');
|
||||||
|
|
||||||
|
Branch:
|
||||||
|
'branch' name=(ID|STRING) ('order:' order=INT)? EOL;
|
||||||
|
|
||||||
|
Merge:
|
||||||
|
'merge' name=(ID|STRING) properties+=MergeProperties* EOL;
|
||||||
|
|
||||||
|
MergeProperties
|
||||||
|
: CommitId
|
||||||
|
| Tags
|
||||||
|
| CommitType
|
||||||
|
;
|
||||||
|
|
||||||
|
Checkout:
|
||||||
|
('checkout'|'switch') id=(ID|STRING) EOL;
|
||||||
|
|
||||||
|
CherryPicking:
|
||||||
|
'cherry-pick' properties+=CherryPickProperties* EOL;
|
||||||
|
|
||||||
|
CherryPickProperties
|
||||||
|
: CommitId
|
||||||
|
| Tags
|
||||||
|
| ParentCommit
|
||||||
|
;
|
||||||
|
|
||||||
|
ParentCommit:
|
||||||
|
'parent:' id=STRING;
|
||||||
|
|
||||||
|
terminal INT returns number: /[0-9]+(?=\s)/;
|
||||||
|
terminal ID returns string: /\w([-\./\w]*[-\w])?/;
|
||||||
|
terminal STRING: /"[^"]*"|'[^']*'/;
|
||||||
|
|
1
packages/parser/src/language/gitGraph/index.ts
Normal file
1
packages/parser/src/language/gitGraph/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './module.js';
|
53
packages/parser/src/language/gitGraph/module.ts
Normal file
53
packages/parser/src/language/gitGraph/module.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import type {
|
||||||
|
DefaultSharedCoreModuleContext,
|
||||||
|
LangiumCoreServices,
|
||||||
|
LangiumSharedCoreServices,
|
||||||
|
Module,
|
||||||
|
PartialLangiumCoreServices,
|
||||||
|
} from 'langium';
|
||||||
|
import {
|
||||||
|
inject,
|
||||||
|
createDefaultCoreModule,
|
||||||
|
createDefaultSharedCoreModule,
|
||||||
|
EmptyFileSystem,
|
||||||
|
} from 'langium';
|
||||||
|
|
||||||
|
import { CommonValueConverter } from '../common/valueConverter.js';
|
||||||
|
import { MermaidGeneratedSharedModule, GitGraphGeneratedModule } from '../generated/module.js';
|
||||||
|
import { GitGraphTokenBuilder } from './tokenBuilder.js';
|
||||||
|
|
||||||
|
interface GitGraphAddedServices {
|
||||||
|
parser: {
|
||||||
|
TokenBuilder: GitGraphTokenBuilder;
|
||||||
|
ValueConverter: CommonValueConverter;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GitGraphServices = LangiumCoreServices & GitGraphAddedServices;
|
||||||
|
|
||||||
|
export const GitGraphModule: Module<
|
||||||
|
GitGraphServices,
|
||||||
|
PartialLangiumCoreServices & GitGraphAddedServices
|
||||||
|
> = {
|
||||||
|
parser: {
|
||||||
|
TokenBuilder: () => new GitGraphTokenBuilder(),
|
||||||
|
ValueConverter: () => new CommonValueConverter(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createGitGraphServices(context: DefaultSharedCoreModuleContext = EmptyFileSystem): {
|
||||||
|
shared: LangiumSharedCoreServices;
|
||||||
|
GitGraph: GitGraphServices;
|
||||||
|
} {
|
||||||
|
const shared: LangiumSharedCoreServices = inject(
|
||||||
|
createDefaultSharedCoreModule(context),
|
||||||
|
MermaidGeneratedSharedModule
|
||||||
|
);
|
||||||
|
const GitGraph: GitGraphServices = inject(
|
||||||
|
createDefaultCoreModule({ shared }),
|
||||||
|
GitGraphGeneratedModule,
|
||||||
|
GitGraphModule
|
||||||
|
);
|
||||||
|
shared.ServiceRegistry.register(GitGraph);
|
||||||
|
return { shared, GitGraph };
|
||||||
|
}
|
7
packages/parser/src/language/gitGraph/tokenBuilder.ts
Normal file
7
packages/parser/src/language/gitGraph/tokenBuilder.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { AbstractMermaidTokenBuilder } from '../common/index.js';
|
||||||
|
|
||||||
|
export class GitGraphTokenBuilder extends AbstractMermaidTokenBuilder {
|
||||||
|
public constructor() {
|
||||||
|
super(['gitGraph']);
|
||||||
|
}
|
||||||
|
}
|
@@ -5,20 +5,30 @@ export {
|
|||||||
PacketBlock,
|
PacketBlock,
|
||||||
Pie,
|
Pie,
|
||||||
PieSection,
|
PieSection,
|
||||||
|
GitGraph,
|
||||||
|
Branch,
|
||||||
|
Commit,
|
||||||
|
Merge,
|
||||||
isCommon,
|
isCommon,
|
||||||
isInfo,
|
isInfo,
|
||||||
isPacket,
|
isPacket,
|
||||||
isPacketBlock,
|
isPacketBlock,
|
||||||
isPie,
|
isPie,
|
||||||
isPieSection,
|
isPieSection,
|
||||||
|
isGitGraph,
|
||||||
|
isBranch,
|
||||||
|
isCommit,
|
||||||
|
isMerge,
|
||||||
} from './generated/ast.js';
|
} from './generated/ast.js';
|
||||||
export {
|
export {
|
||||||
InfoGeneratedModule,
|
InfoGeneratedModule,
|
||||||
MermaidGeneratedSharedModule,
|
MermaidGeneratedSharedModule,
|
||||||
PacketGeneratedModule,
|
PacketGeneratedModule,
|
||||||
PieGeneratedModule,
|
PieGeneratedModule,
|
||||||
|
GitGraphGeneratedModule,
|
||||||
} from './generated/module.js';
|
} from './generated/module.js';
|
||||||
|
|
||||||
|
export * from './gitGraph/index.js';
|
||||||
export * from './common/index.js';
|
export * from './common/index.js';
|
||||||
export * from './info/index.js';
|
export * from './info/index.js';
|
||||||
export * from './packet/index.js';
|
export * from './packet/index.js';
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import type { LangiumParser, ParseResult } from 'langium';
|
import type { LangiumParser, ParseResult } from 'langium';
|
||||||
|
|
||||||
import type { Info, Packet, Pie } from './index.js';
|
import type { Info, Packet, Pie, GitGraph } from './index.js';
|
||||||
|
|
||||||
export type DiagramAST = Info | Packet | Pie;
|
export type DiagramAST = Info | Packet | Pie | GitGraph;
|
||||||
|
|
||||||
const parsers: Record<string, LangiumParser> = {};
|
const parsers: Record<string, LangiumParser> = {};
|
||||||
const initializers = {
|
const initializers = {
|
||||||
@@ -21,11 +21,18 @@ const initializers = {
|
|||||||
const parser = createPieServices().Pie.parser.LangiumParser;
|
const parser = createPieServices().Pie.parser.LangiumParser;
|
||||||
parsers.pie = parser;
|
parsers.pie = parser;
|
||||||
},
|
},
|
||||||
|
gitGraph: async () => {
|
||||||
|
const { createGitGraphServices } = await import('./language/gitGraph/index.js');
|
||||||
|
const parser = createGitGraphServices().GitGraph.parser.LangiumParser;
|
||||||
|
parsers.gitGraph = parser;
|
||||||
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export async function parse(diagramType: 'info', text: string): Promise<Info>;
|
export async function parse(diagramType: 'info', text: string): Promise<Info>;
|
||||||
export async function parse(diagramType: 'packet', text: string): Promise<Packet>;
|
export async function parse(diagramType: 'packet', text: string): Promise<Packet>;
|
||||||
export async function parse(diagramType: 'pie', text: string): Promise<Pie>;
|
export async function parse(diagramType: 'pie', text: string): Promise<Pie>;
|
||||||
|
export async function parse(diagramType: 'gitGraph', text: string): Promise<GitGraph>;
|
||||||
|
|
||||||
export async function parse<T extends DiagramAST>(
|
export async function parse<T extends DiagramAST>(
|
||||||
diagramType: keyof typeof initializers,
|
diagramType: keyof typeof initializers,
|
||||||
text: string
|
text: string
|
||||||
|
99
packages/parser/tests/gitGraph.test.ts
Normal file
99
packages/parser/tests/gitGraph.test.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { GitGraph } from '../src/language/index.js';
|
||||||
|
import { gitGraphParse as parse } from './test-util.js';
|
||||||
|
|
||||||
|
describe('gitGraph', () => {
|
||||||
|
describe('Basic Parsing', () => {
|
||||||
|
it('should handle empty gitGraph', () => {
|
||||||
|
const result = parse(`gitGraph`);
|
||||||
|
expect(result.value.$type).toBe(GitGraph);
|
||||||
|
expect(result.value.statements).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple commits', () => {
|
||||||
|
const result = parse(`
|
||||||
|
gitGraph
|
||||||
|
commit
|
||||||
|
commit
|
||||||
|
`);
|
||||||
|
expect(result.value.$type).toBe(GitGraph);
|
||||||
|
expect(result.value.statements).toHaveLength(2);
|
||||||
|
expect(
|
||||||
|
result.value.statements.every((s: { $type: string }) => s.$type === 'Commit')
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle branches and checkouts', () => {
|
||||||
|
const result = parse(`
|
||||||
|
gitGraph
|
||||||
|
branch feature
|
||||||
|
branch release
|
||||||
|
checkout feature
|
||||||
|
`);
|
||||||
|
expect(result.value.statements).toHaveLength(3);
|
||||||
|
expect(result.value.statements[0].$type).toBe('Branch');
|
||||||
|
expect(result.value.statements[0].name).toBe('feature');
|
||||||
|
expect(result.value.statements[1].$type).toBe('Branch');
|
||||||
|
expect(result.value.statements[1].name).toBe('release');
|
||||||
|
expect(result.value.statements[2].$type).toBe('Checkout');
|
||||||
|
expect(result.value.statements[2].id).toBe('feature');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle merges', () => {
|
||||||
|
const result = parse(`
|
||||||
|
gitGraph
|
||||||
|
branch feature
|
||||||
|
commit id: "A"
|
||||||
|
merge feature id: "M"
|
||||||
|
`);
|
||||||
|
expect(result.value.statements).toHaveLength(3);
|
||||||
|
expect(result.value.statements[2].$type).toBe('Merge');
|
||||||
|
expect(result.value.statements[2].name).toBe('feature');
|
||||||
|
expect(result.value.statements[2].properties[0].id).toBe('M');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle cherry-picking with tags and parent', () => {
|
||||||
|
const result = parse(`
|
||||||
|
gitGraph
|
||||||
|
branch feature
|
||||||
|
commit id: "M"
|
||||||
|
checkout main
|
||||||
|
cherry-pick id: "M" tag: "v2.1:ZERO" parent:"ZERO"
|
||||||
|
`);
|
||||||
|
expect(result.value.statements).toHaveLength(4);
|
||||||
|
expect(result.value.statements[3].$type).toBe('CherryPicking');
|
||||||
|
expect(result.value.statements[3].properties.length).toBe(3);
|
||||||
|
expect(result.value.statements[3].properties[0].id).toBe('M');
|
||||||
|
expect(result.value.statements[3].properties[1].tags).toBe('v2.1:ZERO');
|
||||||
|
expect(result.value.statements[3].properties[2].id).toBe('ZERO');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should parse complex gitGraph interactions', () => {
|
||||||
|
const result = parse(`
|
||||||
|
gitGraph
|
||||||
|
commit id: "ZERO"
|
||||||
|
branch feature
|
||||||
|
branch release
|
||||||
|
checkout feature
|
||||||
|
commit id: "A"
|
||||||
|
commit id: "B"
|
||||||
|
checkout main
|
||||||
|
merge feature id: "M"
|
||||||
|
checkout release
|
||||||
|
commit id: "C"
|
||||||
|
cherry-pick id: "M" tag: "v2.1:ZERO" parent:"ZERO"
|
||||||
|
commit id: "D"
|
||||||
|
`);
|
||||||
|
expect(result.value.statements).toHaveLength(12);
|
||||||
|
expect(result.value.statements[0].$type).toBe('Commit');
|
||||||
|
expect(result.value.statements[0].properties[0].id).toBe('ZERO');
|
||||||
|
expect(result.value.statements[1].$type).toBe('Branch');
|
||||||
|
expect(result.value.statements[6].$type).toBe('Merge');
|
||||||
|
expect(result.value.statements[10].$type).toBe('CherryPicking');
|
||||||
|
expect(result.value.statements[10].properties[0].id).toBe('M');
|
||||||
|
expect(result.value.statements[10].properties[2].id).toBe('ZERO');
|
||||||
|
expect(result.value.statements[11].$type).toBe('Commit');
|
||||||
|
expect(result.value.statements[11].properties[0].id).toBe('D');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@@ -1,7 +1,18 @@
|
|||||||
import type { LangiumParser, ParseResult } from 'langium';
|
import type { LangiumParser, ParseResult } from 'langium';
|
||||||
import { expect, vi } from 'vitest';
|
import { expect, vi } from 'vitest';
|
||||||
import type { Info, InfoServices, Pie, PieServices } from '../src/language/index.js';
|
import type {
|
||||||
import { createInfoServices, createPieServices } from '../src/language/index.js';
|
Info,
|
||||||
|
InfoServices,
|
||||||
|
Pie,
|
||||||
|
PieServices,
|
||||||
|
GitGraph,
|
||||||
|
GitGraphServices,
|
||||||
|
} from '../src/language/index.js';
|
||||||
|
import {
|
||||||
|
createInfoServices,
|
||||||
|
createPieServices,
|
||||||
|
createGitGraphServices,
|
||||||
|
} from '../src/language/index.js';
|
||||||
|
|
||||||
const consoleMock = vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
const consoleMock = vi.spyOn(console, 'log').mockImplementation(() => undefined);
|
||||||
|
|
||||||
@@ -40,3 +51,14 @@ export function createPieTestServices() {
|
|||||||
return { services: pieServices, parse };
|
return { services: pieServices, parse };
|
||||||
}
|
}
|
||||||
export const pieParse = createPieTestServices().parse;
|
export const pieParse = createPieTestServices().parse;
|
||||||
|
|
||||||
|
const gitGraphServices: GitGraphServices = createGitGraphServices().GitGraph;
|
||||||
|
const gitGraphParser: LangiumParser = gitGraphServices.parser.LangiumParser;
|
||||||
|
export function createGitGraphTestServices() {
|
||||||
|
const parse = (input: string) => {
|
||||||
|
return gitGraphParser.parse<GitGraph>(input);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { services: gitGraphServices, parse };
|
||||||
|
}
|
||||||
|
export const gitGraphParse = createGitGraphTestServices().parse;
|
||||||
|
Reference in New Issue
Block a user