Skip to content

Commit f3029bf

Browse files
authored
Copy JSDoc comments to generated AST (#1835)
1 parent 58ce261 commit f3029bf

File tree

13 files changed

+160
-82
lines changed

13 files changed

+160
-82
lines changed

examples/statemachine/src/language-server/generated/ast.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export function isCommand(item: unknown): item is Command {
4141
return reflection.isInstance(item, Command);
4242
}
4343

44+
/** An event is the trigger for a transition */
4445
export interface Event extends langium.AstNode {
4546
readonly $container: Statemachine;
4647
readonly $type: 'Event';
@@ -53,11 +54,13 @@ export function isEvent(item: unknown): item is Event {
5354
return reflection.isInstance(item, Event);
5455
}
5556

57+
/** A description of the status of a system */
5658
export interface State extends langium.AstNode {
5759
readonly $container: Statemachine;
5860
readonly $type: 'State';
5961
actions: Array<langium.Reference<Command>>;
6062
name: string;
63+
/** The transitions to other states that can take place from the current one */
6164
transitions: Array<Transition>;
6265
}
6366

@@ -67,12 +70,17 @@ export function isState(item: unknown): item is State {
6770
return reflection.isInstance(item, State);
6871
}
6972

73+
/** A textual represntation of a state machine */
7074
export interface Statemachine extends langium.AstNode {
7175
readonly $type: 'Statemachine';
7276
commands: Array<Command>;
77+
/** The list of recognized event names */
7378
events: Array<Event>;
79+
/** The starting state for the machine */
7480
init: langium.Reference<State>;
81+
/** The name of the machine */
7582
name: string;
83+
/** Definitions of available states */
7684
states: Array<State>;
7785
}
7886

@@ -82,10 +90,13 @@ export function isStatemachine(item: unknown): item is Statemachine {
8290
return reflection.isInstance(item, Statemachine);
8391
}
8492

93+
/** A change from one state to another */
8594
export interface Transition extends langium.AstNode {
8695
readonly $container: State;
8796
readonly $type: 'Transition';
97+
/** The event triggering the transition */
8898
event: langium.Reference<Event>;
99+
/** The target state */
89100
state: langium.Reference<State>;
90101
}
91102

examples/statemachine/src/language-server/generated/grammar.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
3333
"$ref": "#/rules@6"
3434
},
3535
"arguments": []
36-
}
36+
},
37+
"$comment": "/** The name of the machine */"
3738
},
3839
{
3940
"$type": "Group",
@@ -53,7 +54,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
5354
},
5455
"arguments": []
5556
},
56-
"cardinality": "+"
57+
"cardinality": "+",
58+
"$comment": "/** The list of recognized event names */"
5759
}
5860
],
5961
"cardinality": "?"
@@ -95,7 +97,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
9597
"$ref": "#/rules@3"
9698
},
9799
"deprecatedSyntax": false
98-
}
100+
},
101+
"$comment": "/** The starting state for the machine */"
99102
},
100103
{
101104
"$type": "Assignment",
@@ -108,15 +111,17 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
108111
},
109112
"arguments": []
110113
},
111-
"cardinality": "*"
114+
"cardinality": "*",
115+
"$comment": "/** Definitions of available states */"
112116
}
113117
]
114118
},
115119
"definesHiddenTokens": false,
116120
"fragment": false,
117121
"hiddenTokens": [],
118122
"parameters": [],
119-
"wildcard": false
123+
"wildcard": false,
124+
"$comment": "/** A textual represntation of a state machine */"
120125
},
121126
{
122127
"$type": "ParserRule",
@@ -138,7 +143,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
138143
"fragment": false,
139144
"hiddenTokens": [],
140145
"parameters": [],
141-
"wildcard": false
146+
"wildcard": false,
147+
"$comment": "/** An event is the trigger for a transition */"
142148
},
143149
{
144150
"$type": "ParserRule",
@@ -226,7 +232,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
226232
},
227233
"arguments": []
228234
},
229-
"cardinality": "*"
235+
"cardinality": "*",
236+
"$comment": "/** The transitions to other states that can take place from the current one */"
230237
},
231238
{
232239
"$type": "Keyword",
@@ -239,7 +246,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
239246
"fragment": false,
240247
"hiddenTokens": [],
241248
"parameters": [],
242-
"wildcard": false
249+
"wildcard": false,
250+
"$comment": "/** A description of the status of a system */"
243251
},
244252
{
245253
"$type": "ParserRule",
@@ -257,7 +265,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
257265
"$ref": "#/rules@1"
258266
},
259267
"deprecatedSyntax": false
260-
}
268+
},
269+
"$comment": "/** The event triggering the transition */"
261270
},
262271
{
263272
"$type": "Keyword",
@@ -273,16 +282,19 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
273282
"$ref": "#/rules@3"
274283
},
275284
"deprecatedSyntax": false
276-
}
285+
},
286+
"$comment": "/** The target state */"
277287
}
278-
]
288+
],
289+
"$comment": "/** The event triggering the transition */"
279290
},
280291
"definesHiddenTokens": false,
281292
"entry": false,
282293
"fragment": false,
283294
"hiddenTokens": [],
284295
"parameters": [],
285-
"wildcard": false
296+
"wildcard": false,
297+
"$comment": "/** A change from one state to another */"
286298
},
287299
{
288300
"$type": "TerminalRule",

examples/statemachine/src/language-server/statemachine.langium

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,41 @@
11
grammar Statemachine
22

3+
/** A textual represntation of a state machine */
34
entry Statemachine:
4-
'statemachine' name=ID
5-
('events' events+=Event+)?
5+
'statemachine'
6+
/** The name of the machine */
7+
name=ID
8+
('events'
9+
/** The list of recognized event names */
10+
events+=Event+)?
611
('commands' commands+=Command+)?
7-
'initialState' init=[State]
12+
'initialState'
13+
/** The starting state for the machine */
14+
init=[State]
15+
/** Definitions of available states */
816
states+=State*;
917

18+
/** An event is the trigger for a transition */
1019
Event:
1120
name=ID;
1221

1322
Command:
1423
name=ID;
1524

25+
/** A description of the status of a system */
1626
State:
1727
'state' name=ID
1828
('actions' '{' actions+=[Command]+ '}')?
29+
/** The transitions to other states that can take place from the current one */
1930
transitions+=Transition*
2031
'end';
2132

33+
/** A change from one state to another */
2234
Transition:
23-
event=[Event] '=>' state=[State];
35+
/** The event triggering the transition */
36+
event=[Event] '=>'
37+
/** The target state */
38+
state=[State];
2439

2540
hidden terminal WS: /\s+/;
2641
terminal ID: /[_a-zA-Z][\w_]*/;

packages/langium-cli/src/generator/ast-generator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { generatedHeader } from './node-util.js';
1313
import { collectKeywords, collectTerminalRegexps } from './langium-util.js';
1414

1515
export function generateAst(services: LangiumCoreServices, grammars: Grammar[], config: LangiumConfig): string {
16-
const astTypes = collectAst(grammars, services.shared.workspace.LangiumDocuments);
16+
const astTypes = collectAst(grammars, services);
1717
const importFrom = config.langiumInternal ? `../../syntax-tree${config.importExtension}` : 'langium';
1818
/* eslint-disable @typescript-eslint/indent */
1919
const fileNode = expandToNode`

packages/langium-cli/src/generator/types-generator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { collectAst, LangiumGrammarGrammar } from 'langium/grammar';
99
import { collectKeywords } from './langium-util.js';
1010

1111
export function generateTypesFile(services: LangiumCoreServices, grammars: Grammar[]): string {
12-
const { unions, interfaces } = collectAst(grammars, services.shared.workspace.LangiumDocuments);
12+
const { unions, interfaces } = collectAst(grammars, services);
1313
const reservedWords = new Set(collectKeywords(LangiumGrammarGrammar()));
1414

1515
const fileNode = joinToNode([

packages/langium/src/grammar/ast-reflection-interpreter.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
******************************************************************************/
66

77
import type { AstReflection, ReferenceInfo, TypeProperty, TypeMetaData } from '../syntax-tree.js';
8-
import type { LangiumDocuments } from '../workspace/documents.js';
8+
import type { LangiumCoreServices } from '../index.js';
99
import type { Grammar } from '../languages/generated/ast.js';
1010
import type { AstTypes, Property } from './type-system/type-collector/types.js';
1111
import { AbstractAstReflection } from '../syntax-tree.js';
@@ -15,11 +15,11 @@ import { collectAst } from './type-system/ast-collector.js';
1515
import { collectTypeHierarchy, findReferenceTypes, isAstType, mergeTypesAndInterfaces } from './type-system/types-util.js';
1616

1717
export function interpretAstReflection(astTypes: AstTypes): AstReflection;
18-
export function interpretAstReflection(grammar: Grammar, documents?: LangiumDocuments): AstReflection;
19-
export function interpretAstReflection(grammarOrTypes: Grammar | AstTypes, documents?: LangiumDocuments): AstReflection {
18+
export function interpretAstReflection(grammar: Grammar, services?: LangiumCoreServices): AstReflection;
19+
export function interpretAstReflection(grammarOrTypes: Grammar | AstTypes, services?: LangiumCoreServices): AstReflection {
2020
let collectedTypes: AstTypes;
2121
if (isGrammar(grammarOrTypes)) {
22-
collectedTypes = collectAst(grammarOrTypes, documents);
22+
collectedTypes = collectAst(grammarOrTypes, services);
2323
} else {
2424
collectedTypes = grammarOrTypes;
2525
}

packages/langium/src/grammar/type-system/ast-collector.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
******************************************************************************/
66

77
import type { Grammar } from '../../languages/generated/ast.js';
8-
import type { LangiumDocuments } from '../../workspace/documents.js';
8+
import type { LangiumCoreServices } from '../../index.js';
99
import type { AstTypes, InterfaceType, PropertyType, TypeOption, UnionType } from './type-collector/types.js';
1010
import type { ValidationAstTypes } from './type-collector/all-types.js';
1111
import type { PlainAstTypes, PlainInterface, PlainUnion } from './type-collector/plain-types.js';
@@ -18,10 +18,10 @@ import { plainToTypes } from './type-collector/plain-types.js';
1818
* Collects all types for the generated AST. The types collector entry point.
1919
*
2020
* @param grammars All grammars involved in the type generation process
21-
* @param documents Additional documents so that imports can be resolved as necessary
21+
* @param services Langium core services to resolve imports as needed, and to pass along JSDoc comments to the generated AST
2222
*/
23-
export function collectAst(grammars: Grammar | Grammar[], documents?: LangiumDocuments): AstTypes {
24-
const { inferred, declared } = collectTypeResources(grammars, documents);
23+
export function collectAst(grammars: Grammar | Grammar[], services?: LangiumCoreServices): AstTypes {
24+
const { inferred, declared } = collectTypeResources(grammars, services);
2525
return createAstTypes(inferred, declared);
2626
}
2727

@@ -30,10 +30,10 @@ export function collectAst(grammars: Grammar | Grammar[], documents?: LangiumDoc
3030
* The validation process requires us to compare our inferred types with our declared types.
3131
*
3232
* @param grammars All grammars involved in the validation process
33-
* @param documents Additional documents so that imports can be resolved as necessary
33+
* @param services Langium core services to resolve imports as needed, and to pass along JSDoc comments to the generated AST
3434
*/
35-
export function collectValidationAst(grammars: Grammar | Grammar[], documents?: LangiumDocuments): ValidationAstTypes {
36-
const { inferred, declared, astResources } = collectTypeResources(grammars, documents);
35+
export function collectValidationAst(grammars: Grammar | Grammar[], services?: LangiumCoreServices): ValidationAstTypes {
36+
const { inferred, declared, astResources } = collectTypeResources(grammars, services);
3737

3838
return {
3939
astResources,

packages/langium/src/grammar/type-system/type-collector/all-types.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import type { ParserRule, Interface, Type, Grammar } from '../../../languages/generated/ast.js';
88
import type { URI } from '../../../utils/uri-utils.js';
9-
import type { LangiumDocuments } from '../../../workspace/documents.js';
9+
import type { LangiumCoreServices } from '../../../index.js';
1010
import type { PlainAstTypes } from './plain-types.js';
1111
import type { AstTypes } from './types.js';
1212
import { collectInferredTypes } from './inferred-types.js';
@@ -35,10 +35,10 @@ export interface ValidationAstTypes {
3535
astResources: AstResources
3636
}
3737

38-
export function collectTypeResources(grammars: Grammar | Grammar[], documents?: LangiumDocuments): TypeResources {
39-
const astResources = collectAllAstResources(grammars, documents);
40-
const declared = collectDeclaredTypes(astResources.interfaces, astResources.types);
41-
const inferred = collectInferredTypes(astResources.parserRules, astResources.datatypeRules, declared);
38+
export function collectTypeResources(grammars: Grammar | Grammar[], services?: LangiumCoreServices): TypeResources {
39+
const astResources = collectAllAstResources(grammars, undefined, undefined, services);
40+
const declared = collectDeclaredTypes(astResources.interfaces, astResources.types, services);
41+
const inferred = collectInferredTypes(astResources.parserRules, astResources.datatypeRules, declared, services);
4242

4343
return {
4444
astResources,
@@ -49,8 +49,8 @@ export function collectTypeResources(grammars: Grammar | Grammar[], documents?:
4949

5050
///////////////////////////////////////////////////////////////////////////////
5151

52-
export function collectAllAstResources(grammars: Grammar | Grammar[], documents?: LangiumDocuments, visited: Set<URI> = new Set(),
53-
astResources: AstResources = { parserRules: [], datatypeRules: [], interfaces: [], types: [] }): AstResources {
52+
export function collectAllAstResources(grammars: Grammar | Grammar[], visited: Set<URI> = new Set(),
53+
astResources: AstResources = { parserRules: [], datatypeRules: [], interfaces: [], types: [] }, services?: LangiumCoreServices): AstResources {
5454

5555
if (!Array.isArray(grammars)) grammars = [grammars];
5656
for (const grammar of grammars) {
@@ -71,9 +71,10 @@ export function collectAllAstResources(grammars: Grammar | Grammar[], documents?
7171
grammar.interfaces.forEach(e => astResources.interfaces.push(e));
7272
grammar.types.forEach(e => astResources.types.push(e));
7373

74+
const documents = services?.shared.workspace.LangiumDocuments;
7475
if (documents) {
7576
const importedGrammars = grammar.imports.map(e => resolveImport(documents, e)).filter((e): e is Grammar => e !== undefined);
76-
collectAllAstResources(importedGrammars, documents, visited, astResources);
77+
collectAllAstResources(importedGrammars, visited, astResources, services);
7778
}
7879
}
7980
return astResources;

packages/langium/src/grammar/type-system/type-collector/declared-types.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
******************************************************************************/
66

77
import type { Interface, Type, TypeDefinition, ValueLiteral } from '../../../languages/generated/ast.js';
8+
import type { LangiumCoreServices } from '../../../index.js';
89
import { isArrayLiteral, isBooleanLiteral } from '../../../languages/generated/ast.js';
910
import type { PlainAstTypes, PlainInterface, PlainProperty, PlainPropertyDefaultValue, PlainPropertyType, PlainUnion } from './plain-types.js';
1011
import { isArrayType, isReferenceType, isUnionType, isSimpleType } from '../../../languages/generated/ast.js';
1112
import { getTypeNameWithoutError, isPrimitiveGrammarType } from '../../internal-grammar-util.js';
1213
import { getTypeName } from '../../../utils/grammar-utils.js';
1314

14-
export function collectDeclaredTypes(interfaces: Interface[], unions: Type[]): PlainAstTypes {
15+
export function collectDeclaredTypes(interfaces: Interface[], unions: Type[], services?: LangiumCoreServices): PlainAstTypes {
16+
const commentProvider = services?.documentation.CommentProvider;
1517
const declaredTypes: PlainAstTypes = { unions: [], interfaces: [] };
1618

1719
// add interfaces
@@ -22,7 +24,8 @@ export function collectDeclaredTypes(interfaces: Interface[], unions: Type[]): P
2224
name: attribute.name,
2325
optional: attribute.isOptional,
2426
astNodes: new Set([attribute]),
25-
type: typeDefinitionToPropertyType(attribute.type)
27+
type: typeDefinitionToPropertyType(attribute.type),
28+
comment: commentProvider?.getComment(attribute)
2629
};
2730
if (attribute.defaultValue) {
2831
property.defaultValue = toPropertyDefaultValue(attribute.defaultValue);
@@ -41,7 +44,8 @@ export function collectDeclaredTypes(interfaces: Interface[], unions: Type[]): P
4144
abstract: false,
4245
properties: properties,
4346
superTypes: superTypes,
44-
subTypes: new Set()
47+
subTypes: new Set(),
48+
comment: commentProvider?.getComment(type),
4549
};
4650
declaredTypes.interfaces.push(interfaceType);
4751
}
@@ -53,7 +57,8 @@ export function collectDeclaredTypes(interfaces: Interface[], unions: Type[]): P
5357
declared: true,
5458
type: typeDefinitionToPropertyType(union.type),
5559
superTypes: new Set(),
56-
subTypes: new Set()
60+
subTypes: new Set(),
61+
comment: commentProvider?.getComment(union),
5762
};
5863
declaredTypes.unions.push(unionType);
5964
}

0 commit comments

Comments
 (0)