Skip to content

Commit 3670747

Browse files
langium: removed short cut guards in document builder's 'resetToState' method (#2022)
in language implementations with transitively resolved cross-refs documents may contain resolved cross-refs although their state is still 'ComputedScopes' or even 'IndexContent' -> reset those docs properly, too; follow-up on PR #1977
1 parent d2c2d3d commit 3670747

File tree

2 files changed

+64
-15
lines changed

2 files changed

+64
-15
lines changed

packages/langium/src/workspace/document-builder.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -353,35 +353,20 @@ export class DefaultDocumentBuilder implements DocumentBuilder {
353353
// Else fall through
354354
}
355355
case DocumentState.Parsed:
356-
if (document.state <= DocumentState.Parsed) {
357-
break;
358-
}
359356
this.indexManager.removeContent(document.uri);
360357
// Fall through
361358
case DocumentState.IndexedContent:
362-
if (document.state <= DocumentState.IndexedContent) {
363-
break;
364-
}
365359
document.localSymbols = undefined;
366360
// Fall through
367361
case DocumentState.ComputedScopes: {
368-
if (document.state <= DocumentState.ComputedScopes) {
369-
break;
370-
}
371362
const linker = this.serviceRegistry.getServices(document.uri).references.Linker;
372363
linker.unlink(document);
373364
// Fall through
374365
}
375366
case DocumentState.Linked:
376-
if (document.state <= DocumentState.Linked) {
377-
break;
378-
}
379367
this.indexManager.removeReferences(document.uri);
380368
// Fall through
381369
case DocumentState.IndexedReferences:
382-
if (document.state <= DocumentState.IndexedReferences) {
383-
break;
384-
}
385370
document.diagnostics = undefined;
386371
}
387372
if (document.state > state) {

packages/langium/test/workspace/document-builder.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,70 @@ describe('DefaultDocumentBuilder', () => {
668668
}
669669
});
670670

671+
test("References are unlinked on custom reset of document state even if the document didn't reach linked phase yet", async () => {
672+
const services = await createServices();
673+
const workspace = services.shared.workspace;
674+
const documentFactory = workspace.LangiumDocumentFactory;
675+
const documents = workspace.LangiumDocuments;
676+
const document = documentFactory.fromString<Model>(`
677+
foo 1 A
678+
foo 11 B
679+
bar A
680+
bar B
681+
`, URI.parse('file:///test1.txt'));
682+
documents.addDocument(document);
683+
684+
const tokenSource = startCancelableOperation();
685+
const builder = workspace.DocumentBuilder;
686+
builder.onBuildPhase(DocumentState.ComputedScopes, () => {
687+
tokenSource.cancel();
688+
});
689+
try {
690+
await builder.build([document], undefined, tokenSource.token);
691+
fail('The update is supposed to be cancelled');
692+
} catch (err) {
693+
expect(isOperationCancelled(err)).toBe(true);
694+
}
695+
expect(document.state).toBe(DocumentState.ComputedScopes);
696+
expect(document.localSymbols).toBeDefined();
697+
expect(document.references).toHaveLength(0);
698+
699+
// Resolve the reference "on-the-fly"
700+
// We would expect that doing so will add the reference to the document references
701+
let first = document.parseResult.value.foos[0].bar.ref;
702+
expect(first).toBeDefined();
703+
expect(first!.$type).toBe('Bar');
704+
expect(document.references).toHaveLength(1);
705+
706+
// Primary testing goal: Reset the document state and clean references in order to get rid of any stale ones;
707+
// here resetting to IndexedContent in order to also clear any pre-computed scopes/local symbol tables
708+
builder.resetToState(document, DocumentState.IndexedContent);
709+
710+
expect(document.state).toBe(DocumentState.IndexedContent);
711+
expect(document.localSymbols).toBeUndefined();
712+
expect(document.references).toHaveLength(0);
713+
714+
// Again, resolve the reference "on-the-fly", this is supposed to work as 'A' is accessible via the index
715+
first = document.parseResult.value.foos[0].bar.ref;
716+
expect(first).toBeDefined();
717+
expect(first!.$type).toBe('Bar');
718+
expect(document.references).toHaveLength(1);
719+
720+
// In addition: Alternatively, attempt to reset the document state to ComputedScopes
721+
builder.resetToState(document, DocumentState.ComputedScopes);
722+
723+
expect(document.state).toBe(DocumentState.IndexedContent);
724+
expect(document.references).toHaveLength(0);
725+
726+
const astNodeReferences = AstUtils.streamAst(document.parseResult.value).flatMap(AstUtils.streamReferences).toArray();
727+
expect(astNodeReferences).toHaveLength(2);
728+
for (const ref of astNodeReferences) {
729+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
730+
const defaultRef = ref.reference as any;
731+
expect(defaultRef._ref).toBeUndefined();
732+
}
733+
});
734+
671735
describe('DefaultDocumentBuilder document sorting', () => {
672736
let services: LangiumServices;
673737
let documentFactory: LangiumDocumentFactory;

0 commit comments

Comments
 (0)