@@ -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