@@ -37,6 +37,7 @@ import 'package:analysis_server/src/server/error_notifier.dart';
3737import 'package:analysis_server/src/services/completion/completion_performance.dart'
3838 show CompletionPerformance;
3939import 'package:analysis_server/src/services/refactoring/refactoring.dart' ;
40+ import 'package:analyzer/dart/analysis/context_locator.dart' ;
4041import 'package:analyzer/error/error.dart' ;
4142import 'package:analyzer/exception/exception.dart' ;
4243import 'package:analyzer/file_system/file_system.dart' ;
@@ -50,6 +51,7 @@ import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
5051import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
5152import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' as plugin;
5253import 'package:http/http.dart' as http;
54+ import 'package:meta/meta.dart' ;
5355import 'package:watcher/watcher.dart' ;
5456
5557/// Instances of the class [LspAnalysisServer] implement an LSP-based server
@@ -104,29 +106,15 @@ class LspAnalysisServer extends AbstractAnalysisServer {
104106
105107 StreamSubscription _pluginChangeSubscription;
106108
107- /// Temporary analysis roots for open files.
108- ///
109- /// When a file is opened and there is no driver available (for example no
110- /// folder was opened in the editor, so the set of analysis roots is empty)
111- /// we add temporary roots for the project (or containing) folder. When the
112- /// file is closed, it is removed from this map and if no other open file
113- /// uses that root, it will be removed from the set of analysis roots.
114- ///
115- /// key: file path of the open file
116- /// value: folder to be used as a root.
117- final _temporaryAnalysisRoots = < String , String > {};
118-
119- /// The set of analysis roots explicitly added to the workspace.
120- final _explicitAnalysisRoots = < String > {};
109+ /// The current workspace folders provided by the client. Used as analysis roots.
110+ final _workspaceFolders = < String > {};
121111
122112 /// A progress reporter for analysis status.
123113 ProgressReporter analyzingProgressReporter;
124114
125- /// The last paths that were set as included analysis roots.
126- Set <String > _lastIncludedRootPaths;
127-
128- /// The last paths that were set as excluded analysis roots.
129- Set <String > _lastExcludedRootPaths;
115+ /// The number of times contexts have been created/recreated.
116+ @visibleForTesting
117+ int contextBuilds = 0 ;
130118
131119 /// Initialize a newly created server to send and receive messages to the
132120 /// given [channel] .
@@ -204,15 +192,10 @@ class LspAnalysisServer extends AbstractAnalysisServer {
204192 assert (didAdd);
205193 if (didAdd) {
206194 _updateDriversAndPluginsPriorityFiles ();
195+ _refreshAnalysisRoots ();
207196 }
208197 }
209198
210- /// Adds a temporary analysis root for an open file.
211- void addTemporaryAnalysisRoot (String filePath, String folderPath) {
212- _temporaryAnalysisRoots[filePath] = folderPath;
213- _refreshAnalysisRoots ();
214- }
215-
216199 /// The socket from which messages are being read has been closed.
217200 void done () {}
218201
@@ -479,15 +462,10 @@ class LspAnalysisServer extends AbstractAnalysisServer {
479462 assert (didRemove);
480463 if (didRemove) {
481464 _updateDriversAndPluginsPriorityFiles ();
465+ _refreshAnalysisRoots ();
482466 }
483467 }
484468
485- /// Removes any temporary analysis root for a file that was closed.
486- void removeTemporaryAnalysisRoot (String filePath) {
487- _temporaryAnalysisRoots.remove (filePath);
488- _refreshAnalysisRoots ();
489- }
490-
491469 void sendErrorResponse (Message message, ResponseError error) {
492470 if (message is RequestMessage ) {
493471 channel.sendResponse (ResponseMessage (
@@ -651,10 +629,11 @@ class LspAnalysisServer extends AbstractAnalysisServer {
651629 sendServerErrorNotification ('Socket error' , error, stack);
652630 }
653631
654- void updateAnalysisRoots (List <String > addedPaths, List <String > removedPaths) {
632+ void updateWorkspaceFolders (
633+ List <String > addedPaths, List <String > removedPaths) {
655634 // TODO(dantup): This is currently case-sensitive!
656635
657- _explicitAnalysisRoots
636+ _workspaceFolders
658637 ..addAll (addedPaths ?? const [])
659638 ..removeAll (removedPaths ?? const []);
660639
@@ -671,14 +650,40 @@ class LspAnalysisServer extends AbstractAnalysisServer {
671650 notifyFlutterWidgetDescriptions (path);
672651 }
673652
653+ /// Computes analysis roots for a set of open files.
654+ ///
655+ /// This is used when there are no workspace folders open directly.
656+ List <String > _getRootsForOpenFiles () {
657+ final openFiles = priorityFiles.toList ();
658+ final contextLocator = ContextLocator (resourceProvider: resourceProvider);
659+ final roots = contextLocator.locateRoots (includedPaths: openFiles);
660+
661+ // For files in folders that don't have pubspecs, a root would be
662+ // produced for the root of the drive which we do not want, so filter those out.
663+ roots.removeWhere ((root) => root.root.isRoot);
664+
665+ // Find any files that are no longer covered by roots because of the above
666+ // removal.
667+ final additionalFiles =
668+ openFiles.where ((file) => ! roots.any ((root) => root.isAnalyzed (file)));
669+
670+ return [
671+ ...roots.map ((root) => root.root.path),
672+ ...additionalFiles,
673+ ];
674+ }
675+
674676 void _onPluginsChanged () {
675677 capabilitiesComputer.performDynamicRegistration ();
676678 }
677679
678680 void _refreshAnalysisRoots () {
679- // Always include any temporary analysis roots for open files.
680- final includedPaths = _explicitAnalysisRoots.toSet ()
681- ..addAll (_temporaryAnalysisRoots.values);
681+ // When there are open folders, they are always the roots. If there are no
682+ // open workspace folders, then we use the open (priority) files to compute
683+ // roots.
684+ final includedPaths = _workspaceFolders.isNotEmpty
685+ ? _workspaceFolders.toSet ()
686+ : _getRootsForOpenFiles ();
682687
683688 final excludedPaths = clientConfiguration.analysisExcludedFolders
684689 .expand ((excludePath) => resourceProvider.pathContext
@@ -688,25 +693,10 @@ class LspAnalysisServer extends AbstractAnalysisServer {
688693 // TODO(dantup): Consider supporting per-workspace config by
689694 // calling workspace/configuration whenever workspace folders change
690695 // and caching the config for each one.
691- : _explicitAnalysisRoots .map (
696+ : _workspaceFolders .map (
692697 (root) => resourceProvider.pathContext.join (root, excludePath)))
693698 .toSet ();
694699
695- // If the roots didn't actually change from the last time they were set
696- // (this can happen a lot as temporary roots are collected for open files)
697- // we can avoid doing expensive work like discarding/re-scanning the
698- // declarations.
699- final rootsChanged =
700- includedPaths.length != _lastIncludedRootPaths? .length ||
701- ! includedPaths.every (_lastIncludedRootPaths.contains) ||
702- excludedPaths.length != _lastExcludedRootPaths? .length ||
703- ! excludedPaths.every (_lastExcludedRootPaths.contains);
704-
705- if (! rootsChanged) return ;
706-
707- _lastIncludedRootPaths = includedPaths;
708- _lastExcludedRootPaths = excludedPaths;
709-
710700 notificationManager.setAnalysisRoots (
711701 includedPaths.toList (), excludedPaths.toList ());
712702 contextManager.setRoots (includedPaths.toList (), excludedPaths.toList ());
@@ -780,6 +770,7 @@ class LspServerContextManagerCallbacks extends ContextManagerCallbacks {
780770
781771 @override
782772 void afterContextsCreated () {
773+ analysisServer.contextBuilds++ ;
783774 analysisServer.addContextsToDeclarationsTracker ();
784775 }
785776
0 commit comments