Skip to content

Commit 5912b62

Browse files
committed
Support multi-target references
1 parent 6971c83 commit 5912b62

40 files changed

+710
-281
lines changed

examples/domainmodel/src/language-server/domain-model-rename-refactoring.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,24 @@ export class DomainModelRenameProvider extends DefaultRenameProvider {
3131
const offset = document.textDocument.offsetAt(params.position);
3232
const leafNode = CstUtils.findDeclarationNodeAtOffset(rootNode, offset, this.grammarConfig.nameRegexp);
3333
if (!leafNode) return undefined;
34-
const targetNode = this.references.findDeclaration(leafNode);
35-
if (!targetNode) return undefined;
36-
if (isNamed(targetNode)) targetNode.name = params.newName;
37-
const location = this.getNodeLocation(targetNode);
38-
if (location) {
39-
const change = TextEdit.replace(location.range, params.newName);
40-
const uri = location.uri;
41-
if (uri) {
42-
if (changes[uri]) {
43-
changes[uri].push(change);
44-
} else {
45-
changes[uri] = [change];
34+
const targetNodes = this.references.findDeclarations(leafNode);
35+
if (!targetNodes.length) return undefined;
36+
for (const node of targetNodes) {
37+
if (isNamed(node)) node.name = params.newName;
38+
const location = this.getNodeLocation(node);
39+
if (location) {
40+
const change = TextEdit.replace(location.range, params.newName);
41+
const uri = location.uri;
42+
if (uri) {
43+
if (changes[uri]) {
44+
changes[uri].push(change);
45+
} else {
46+
changes[uri] = [change];
47+
}
4648
}
4749
}
4850
}
49-
51+
const targetNode = targetNodes[0];
5052
for (const node of AstUtils.streamAst(targetNode)) {
5153
const qn = this.buildQualifiedName(node);
5254
if (qn) {

examples/domainmodel/src/language-server/domain-model.langium

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ DataType:
1818
'datatype' name=ID;
1919

2020
Entity:
21-
'entity' name=ID ('extends' superType=[Entity:QualifiedName])? '{'
21+
'entity' name=ID ('extends' superType=[*Entity:QualifiedName])? '{'
2222
(features+=Feature)*
2323
'}';
2424

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

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

66
/* eslint-disable */
7-
import type { AstNode, Reference, ReferenceInfo, TypeMetaData } from 'langium';
7+
import type { AstNode, Reference, MultiReference, ReferenceInfo, TypeMetaData } from 'langium';
88
import { AbstractAstReflection } from 'langium';
99

1010
export const DomainModelTerminals = {
@@ -64,7 +64,7 @@ export interface Entity extends AstNode {
6464
readonly $type: 'Entity';
6565
features: Array<Feature>;
6666
name: string;
67-
superType?: Reference<Entity>;
67+
superType?: MultiReference<Entity>;
6868
}
6969

7070
export const Entity = 'Entity';

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ export const DomainModelGrammar = (): Grammar => loadedDomainModelGrammar ?? (lo
211211
"operator": "=",
212212
"terminal": {
213213
"$type": "CrossReference",
214+
"isMulti": true,
214215
"type": {
215216
"$ref": "#/rules@5"
216217
},
@@ -305,7 +306,8 @@ export const DomainModelGrammar = (): Grammar => loadedDomainModelGrammar ?? (lo
305306
},
306307
"arguments": []
307308
},
308-
"deprecatedSyntax": false
309+
"deprecatedSyntax": false,
310+
"isMulti": false
309311
}
310312
}
311313
]

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ export const RequirementsGrammar = (): Grammar => loadedRequirementsGrammar ?? (
168168
"type": {
169169
"$ref": "#/rules@1"
170170
},
171-
"deprecatedSyntax": false
171+
"deprecatedSyntax": false,
172+
"isMulti": false
172173
}
173174
},
174175
{
@@ -187,7 +188,8 @@ export const RequirementsGrammar = (): Grammar => loadedRequirementsGrammar ?? (
187188
"type": {
188189
"$ref": "#/rules@1"
189190
},
190-
"deprecatedSyntax": false
191+
"deprecatedSyntax": false,
192+
"isMulti": false
191193
}
192194
}
193195
],
@@ -428,7 +430,8 @@ export const TestsGrammar = (): Grammar => loadedTestsGrammar ?? (loadedTestsGra
428430
},
429431
"arguments": []
430432
},
431-
"deprecatedSyntax": false
433+
"deprecatedSyntax": false,
434+
"isMulti": false
432435
}
433436
},
434437
{
@@ -454,7 +457,8 @@ export const TestsGrammar = (): Grammar => loadedTestsGrammar ?? (loadedTestsGra
454457
},
455458
"arguments": []
456459
},
457-
"deprecatedSyntax": false
460+
"deprecatedSyntax": false,
461+
"isMulti": false
458462
}
459463
}
460464
],
@@ -480,7 +484,8 @@ export const TestsGrammar = (): Grammar => loadedTestsGrammar ?? (loadedTestsGra
480484
"type": {
481485
"$ref": "#/rules@3"
482486
},
483-
"deprecatedSyntax": false
487+
"deprecatedSyntax": false,
488+
"isMulti": false
484489
}
485490
},
486491
{
@@ -499,7 +504,8 @@ export const TestsGrammar = (): Grammar => loadedTestsGrammar ?? (loadedTestsGra
499504
"type": {
500505
"$ref": "#/rules@3"
501506
},
502-
"deprecatedSyntax": false
507+
"deprecatedSyntax": false,
508+
"isMulti": false
503509
}
504510
}
505511
],
@@ -672,7 +678,8 @@ export const TestsGrammar = (): Grammar => loadedTestsGrammar ?? (loadedTestsGra
672678
"type": {
673679
"$ref": "#/rules@3"
674680
},
675-
"deprecatedSyntax": false
681+
"deprecatedSyntax": false,
682+
"isMulti": false
676683
}
677684
},
678685
{
@@ -691,7 +698,8 @@ export const TestsGrammar = (): Grammar => loadedTestsGrammar ?? (loadedTestsGra
691698
"type": {
692699
"$ref": "#/rules@3"
693700
},
694-
"deprecatedSyntax": false
701+
"deprecatedSyntax": false,
702+
"isMulti": false
695703
}
696704
}
697705
],

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
9494
"type": {
9595
"$ref": "#/rules@3"
9696
},
97-
"deprecatedSyntax": false
97+
"deprecatedSyntax": false,
98+
"isMulti": false
9899
}
99100
},
100101
{
@@ -204,7 +205,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
204205
"type": {
205206
"$ref": "#/rules@2"
206207
},
207-
"deprecatedSyntax": false
208+
"deprecatedSyntax": false,
209+
"isMulti": false
208210
},
209211
"cardinality": "+"
210212
},
@@ -256,7 +258,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
256258
"type": {
257259
"$ref": "#/rules@1"
258260
},
259-
"deprecatedSyntax": false
261+
"deprecatedSyntax": false,
262+
"isMulti": false
260263
}
261264
},
262265
{
@@ -272,7 +275,8 @@ export const StatemachineGrammar = (): Grammar => loadedStatemachineGrammar ?? (
272275
"type": {
273276
"$ref": "#/rules@3"
274277
},
275-
"deprecatedSyntax": false
278+
"deprecatedSyntax": false,
279+
"isMulti": false
276280
}
277281
}
278282
]

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* This program and the accompanying materials are made available under the
44
* terms of the MIT License, which is available in the project root.
55
******************************************************************************/
6+
67
import type { Grammar, LangiumCoreServices } from 'langium';
78
import { type Generated, expandToNode, joinToNode, toString } from 'langium/generate';
89
import type { AstTypes, Property, PropertyDefaultValue } from 'langium/grammar';
@@ -14,14 +15,14 @@ import { collectTerminalRegexps } from './langium-util.js';
1415

1516
export function generateAst(services: LangiumCoreServices, grammars: Grammar[], config: LangiumConfig): string {
1617
const astTypes = collectAst(grammars, services.shared.workspace.LangiumDocuments);
17-
const crossRef = grammars.some(grammar => hasCrossReferences(grammar));
18+
const crossRef = getCrossReferenceTypes(grammars);
1819
const importFrom = config.langiumInternal ? `../../syntax-tree${config.importExtension}` : 'langium';
1920
/* eslint-disable @typescript-eslint/indent */
2021
const fileNode = expandToNode`
2122
${generatedHeader}
2223
2324
/* eslint-disable */
24-
import type { AstNode${crossRef ? ', Reference' : ''}, ReferenceInfo, TypeMetaData } from '${importFrom}';
25+
import type { AstNode${crossRef.single ? ', Reference' : ''}${crossRef.multi ? ', MultiReference' : ''}, ReferenceInfo, TypeMetaData } from '${importFrom}';
2526
import { AbstractAstReflection } from '${importFrom}';
2627
2728
${generateTerminalConstants(grammars, config)}
@@ -37,8 +38,13 @@ export function generateAst(services: LangiumCoreServices, grammars: Grammar[],
3738
/* eslint-enable @typescript-eslint/indent */
3839
}
3940

40-
function hasCrossReferences(grammar: Grammar): boolean {
41-
return Boolean(AstUtils.streamAllContents(grammar).find(GrammarAST.isCrossReference));
41+
function getCrossReferenceTypes(grammars: Grammar[]): { single: boolean, multi: boolean } {
42+
const allCrossReferences = grammars.flatMap(grammar => AstUtils.streamAllContents(grammar).filter(GrammarAST.isCrossReference).toArray());
43+
const multiCrossReferences = allCrossReferences.filter(e => e.isMulti);
44+
return {
45+
single: multiCrossReferences.length < allCrossReferences.length,
46+
multi: multiCrossReferences.length > 0
47+
};
4248
}
4349

4450
function generateAstReflection(config: LangiumConfig, astTypes: AstTypes): Generated {

packages/langium-cli/src/generator/grammar-serializer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* terms of the MIT License, which is available in the project root.
55
******************************************************************************/
66

7-
import type { Grammar, LangiumCoreServices, Reference } from 'langium';
7+
import type { Grammar, LangiumCoreServices } from 'langium';
88
import { expandToNode, joinToNode, normalizeEOL, toString } from 'langium/generate';
99
import type { URI } from 'vscode-uri';
1010
import type { LangiumConfig } from '../package-types.js';
@@ -30,9 +30,9 @@ export function serializeGrammar(services: LangiumCoreServices, grammars: Gramma
3030
grammar => {
3131
const production = config.mode === 'production';
3232
const delimiter = production ? "'" : '`';
33-
const uriConverter = (uri: URI, ref: Reference) => {
33+
const uriConverter = (uri: URI) => {
3434
// We expect the grammar to be self-contained after the transformations we've done before
35-
throw new Error(`Unexpected reference to symbol '${ref.$refText}' in document: ${uri.toString()}`);
35+
throw new Error(`Unexpected reference to element in document: ${uri.toString()}`);
3636
};
3737
const serializedGrammar = services.serializer.JsonSerializer.serialize(grammar, {
3838
space: production ? undefined : 2,

0 commit comments

Comments
 (0)