@@ -380,7 +380,7 @@ namespace ts.server {
380
380
// correct results to all other projects.
381
381
382
382
const defaultProjectResults = perProjectResults . get ( defaultProject ) ;
383
- if ( defaultProjectResults ?. [ 0 ] . references [ 0 ] ?. isDefinition === undefined ) {
383
+ if ( defaultProjectResults ?. [ 0 ] ? .references [ 0 ] ?. isDefinition === undefined ) {
384
384
// Clear all isDefinition properties
385
385
perProjectResults . forEach ( projectResults => {
386
386
for ( const referencedSymbol of projectResults ) {
@@ -531,26 +531,35 @@ namespace ts.server {
531
531
defaultDefinition :
532
532
defaultProject . getLanguageService ( ) . getSourceMapper ( ) . tryGetSourcePosition ( defaultDefinition ! ) ) ;
533
533
534
- // Track which projects we have already searched so that we don't repeat searches.
535
- // We store the project key, rather than the project, because that's what `loadAncestorProjectTree` wants.
536
- // (For that same reason, we don't use `resultsMap` for this check.)
537
- const searchedProjects = new Set < string > ( ) ;
534
+ // The keys of resultsMap allow us to check which projects have already been searched, but we also
535
+ // maintain a set of strings because that's what `loadAncestorProjectTree` wants.
536
+ const searchedProjectKeys = new Set < string > ( ) ;
538
537
539
538
onCancellation:
540
539
while ( queue . length ) {
541
540
while ( queue . length ) {
542
541
if ( cancellationToken . isCancellationRequested ( ) ) break onCancellation;
543
542
543
+ let skipCount = 0 ;
544
+ for ( ; skipCount < queue . length && resultsMap . has ( queue [ skipCount ] . project ) ; skipCount ++ ) ;
545
+
546
+ if ( skipCount === queue . length ) {
547
+ queue . length = 0 ;
548
+ break ;
549
+ }
550
+
551
+ if ( skipCount > 0 ) {
552
+ queue . splice ( 0 , skipCount ) ;
553
+ }
554
+
555
+ // NB: we may still skip if it's a project reference redirect
544
556
const { project, location } = queue . shift ( ) ! ;
545
557
546
558
if ( isLocationProjectReferenceRedirect ( project , location ) ) continue ;
547
559
548
- if ( ! tryAddToSet ( searchedProjects , getProjectKey ( project ) ) ) continue ;
549
-
550
560
const projectResults = searchPosition ( project , location ) ;
551
- if ( projectResults ) {
552
- resultsMap . set ( project , projectResults ) ;
553
- }
561
+ resultsMap . set ( project , projectResults ?? emptyArray ) ;
562
+ searchedProjectKeys . add ( getProjectKey ( project ) ) ;
554
563
}
555
564
556
565
// At this point, we know about all projects passed in as arguments and any projects in which
@@ -559,10 +568,10 @@ namespace ts.server {
559
568
// containing `initialLocation`.
560
569
if ( defaultDefinition ) {
561
570
// This seems to mean "load all projects downstream from any member of `seenProjects`".
562
- projectService . loadAncestorProjectTree ( searchedProjects ) ;
571
+ projectService . loadAncestorProjectTree ( searchedProjectKeys ) ;
563
572
projectService . forEachEnabledProject ( project => {
564
573
if ( cancellationToken . isCancellationRequested ( ) ) return ; // There's no mechanism for skipping the remaining projects
565
- if ( searchedProjects . has ( getProjectKey ( project ) ) ) return ; // Can loop forever without this (enqueue here, dequeue above, repeat)
574
+ if ( resultsMap . has ( project ) ) return ; // Can loop forever without this (enqueue here, dequeue above, repeat)
566
575
const location = mapDefinitionInProject ( defaultDefinition , project , getGeneratedDefinition , getSourceDefinition ) ;
567
576
if ( location ) {
568
577
queue . push ( { project, location } ) ;
@@ -573,9 +582,10 @@ namespace ts.server {
573
582
574
583
// In the common case where there's only one project, return a simpler result to make
575
584
// it easier for the caller to skip post-processing.
576
- if ( searchedProjects . size === 1 ) {
585
+ if ( resultsMap . size === 1 ) {
577
586
const it = resultsMap . values ( ) . next ( ) ;
578
- return it . done ? emptyArray : it . value ; // There may not be any results at all
587
+ Debug . assert ( ! it . done ) ;
588
+ return it . value ;
579
589
}
580
590
581
591
return resultsMap ;
@@ -593,7 +603,7 @@ namespace ts.server {
593
603
const originalScriptInfo = projectService . getScriptInfo ( originalLocation . fileName ) ! ;
594
604
595
605
for ( const project of originalScriptInfo . containingProjects ) {
596
- if ( ! project . isOrphan ( ) ) {
606
+ if ( ! project . isOrphan ( ) && ! resultsMap . has ( project ) ) { // Optimization: don't enqueue if will be discarded
597
607
queue . push ( { project, location : originalLocation } ) ;
598
608
}
599
609
}
@@ -602,7 +612,7 @@ namespace ts.server {
602
612
if ( symlinkedProjectsMap ) {
603
613
symlinkedProjectsMap . forEach ( ( symlinkedProjects , symlinkedPath ) => {
604
614
for ( const symlinkedProject of symlinkedProjects ) {
605
- if ( ! symlinkedProject . isOrphan ( ) ) {
615
+ if ( ! symlinkedProject . isOrphan ( ) && ! resultsMap . has ( symlinkedProject ) ) { // Optimization: don't enqueue if will be discarded
606
616
queue . push ( { project : symlinkedProject , location : { fileName : symlinkedPath as string , pos : originalLocation . pos } } ) ;
607
617
}
608
618
}
0 commit comments