Index: pkg/analysis_server/lib/src/context_manager.dart |
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart |
index 2032c899dbd93d13331ecb10f65e4bdbbff378fe..061cc23c2ace658ad2bdc58b004dd1f0b7c985ef 100644 |
--- a/pkg/analysis_server/lib/src/context_manager.dart |
+++ b/pkg/analysis_server/lib/src/context_manager.dart |
@@ -181,6 +181,31 @@ class ContextInfo { |
void setDependencies(Iterable<String> newDependencies) { |
_dependencies = newDependencies.toSet(); |
} |
+ |
+ /** |
+ * Return `true` if the given [path] is managed by this context or by |
+ * any of its children. |
+ */ |
+ bool _managesOrHasChildThatManages(String path) { |
+ if (parent == null) { |
+ for (ContextInfo child in children) { |
+ if (child._managesOrHasChildThatManages(path)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } else { |
+ if (!folder.isOrContains(path)) { |
+ return false; |
+ } |
+ for (ContextInfo child in children) { |
+ if (child._managesOrHasChildThatManages(path)) { |
+ return true; |
+ } |
+ } |
+ return !pathFilter.ignored(path); |
+ } |
+ } |
} |
/** |
@@ -425,7 +450,7 @@ class ContextManagerImpl implements ContextManager { |
* Virtual [ContextInfo] which acts as the ancestor of all other |
* [ContextInfo]s. |
*/ |
- final ContextInfo _rootInfo = new ContextInfo._root(); |
+ final ContextInfo rootInfo = new ContextInfo._root(); |
/** |
* Stream subscription we are using to watch each analysis root directory for |
@@ -481,7 +506,7 @@ class ContextManagerImpl implements ContextManager { |
@override |
bool isIgnored(String path) { |
- ContextInfo info = _rootInfo; |
+ ContextInfo info = rootInfo; |
do { |
info = info.findChildInfoFor(path); |
if (info == null) { |
@@ -500,7 +525,7 @@ class ContextManagerImpl implements ContextManager { |
return false; |
} |
// check if in one of the roots |
- for (ContextInfo info in _rootInfo.children) { |
+ for (ContextInfo info in rootInfo.children) { |
if (info.folder.contains(path)) { |
return true; |
} |
@@ -575,7 +600,7 @@ class ContextManagerImpl implements ContextManager { |
@override |
void refresh(List<Resource> roots) { |
// Destroy old contexts |
- List<ContextInfo> contextInfos = _rootInfo.descendants.toList(); |
+ List<ContextInfo> contextInfos = rootInfo.descendants.toList(); |
if (roots == null) { |
contextInfos.forEach(_destroyContext); |
} else { |
@@ -616,22 +641,31 @@ class ContextManagerImpl implements ContextManager { |
} |
}); |
- List<ContextInfo> contextInfos = _rootInfo.descendants.toList(); |
+ List<ContextInfo> contextInfos = rootInfo.descendants.toList(); |
// included |
- Set<Folder> includedFolders = new HashSet<Folder>(); |
- for (int i = 0; i < includedPaths.length; i++) { |
- String path = includedPaths[i]; |
- Resource resource = resourceProvider.getResource(path); |
- if (resource is Folder) { |
- includedFolders.add(resource); |
- } else if (!resource.exists) { |
- // Non-existent resources are ignored. TODO(paulberry): we should set |
- // up a watcher to ensure that if the resource appears later, we will |
- // begin analyzing it. |
- } else { |
- // TODO(scheglov) implemented separate files analysis |
- throw new UnimplementedError('$path is not a folder. ' |
- 'Only support for folder analysis is implemented currently.'); |
+ List<Folder> includedFolders = <Folder>[]; |
+ { |
+ // Sort paths to ensure that outer roots are handled before inner roots, |
+ // so we can correctly ignore inner roots, which are already managed |
+ // by outer roots. |
+ LinkedHashSet<String> uniqueIncludedPaths = |
+ new LinkedHashSet<String>.from(includedPaths); |
+ List<String> sortedIncludedPaths = uniqueIncludedPaths.toList(); |
+ sortedIncludedPaths.sort((a, b) => a.length - b.length); |
+ // Convert paths to folders. |
+ for (String path in sortedIncludedPaths) { |
+ Resource resource = resourceProvider.getResource(path); |
+ if (resource is Folder) { |
+ includedFolders.add(resource); |
+ } else if (!resource.exists) { |
+ // Non-existent resources are ignored. TODO(paulberry): we should set |
+ // up a watcher to ensure that if the resource appears later, we will |
+ // begin analyzing it. |
+ } else { |
+ // TODO(scheglov) implemented separate files analysis |
+ throw new UnimplementedError('$path is not a folder. ' |
+ 'Only support for folder analysis is implemented currently.'); |
+ } |
} |
} |
this.includedPaths = includedPaths; |
@@ -648,7 +682,7 @@ class ContextManagerImpl implements ContextManager { |
} |
} |
// Update package roots for existing contexts |
- for (ContextInfo info in _rootInfo.descendants) { |
+ for (ContextInfo info in rootInfo.descendants) { |
String newPackageRoot = normalizedPackageRoots[info.folder.path]; |
if (info.packageRoot != newPackageRoot) { |
info.packageRoot = newPackageRoot; |
@@ -657,17 +691,17 @@ class ContextManagerImpl implements ContextManager { |
} |
// create new contexts |
for (Folder includedFolder in includedFolders) { |
- bool wasIncluded = contextInfos.any((info) { |
- return info.folder.isOrContains(includedFolder.path); |
- }); |
- if (!wasIncluded) { |
+ String includedPath = includedFolder.path; |
+ bool isManaged = rootInfo._managesOrHasChildThatManages(includedPath); |
+ if (!isManaged) { |
+ ContextInfo parent = _getParentForNewContext(includedPath); |
changeSubscriptions[includedFolder] = |
includedFolder.changes.listen(_handleWatchEvent); |
- _createContexts(_rootInfo, includedFolder, false); |
+ _createContexts(parent, includedFolder, false); |
} |
} |
// remove newly excluded sources |
- for (ContextInfo info in _rootInfo.descendants) { |
+ for (ContextInfo info in rootInfo.descendants) { |
// prepare excluded sources |
Map<String, Source> excludedSources = new HashMap<String, Source>(); |
info.sources.forEach((String path, Source source) { |
@@ -685,7 +719,7 @@ class ContextManagerImpl implements ContextManager { |
callbacks.applyChangesToContext(info.folder, changeSet); |
} |
// add previously excluded sources |
- for (ContextInfo info in _rootInfo.descendants) { |
+ for (ContextInfo info in rootInfo.descendants) { |
ChangeSet changeSet = new ChangeSet(); |
_addPreviouslyExcludedSources( |
info, changeSet, info.folder, oldExcludedPaths); |
@@ -822,7 +856,7 @@ class ContextManagerImpl implements ContextManager { |
for (Source source in context.sources) { |
flushedFiles.add(source.fullName); |
} |
- for (ContextInfo contextInfo in _rootInfo.descendants) { |
+ for (ContextInfo contextInfo in rootInfo.descendants) { |
AnalysisContext contextN = contextInfo.context; |
if (context != contextN) { |
for (Source source in contextN.sources) { |
@@ -1082,7 +1116,7 @@ class ContextManagerImpl implements ContextManager { |
* If no context contains the given path, `null` is returned. |
*/ |
ContextInfo _getInnermostContextInfoFor(String path) { |
- ContextInfo info = _rootInfo.findChildInfoFor(path); |
+ ContextInfo info = rootInfo.findChildInfoFor(path); |
if (info == null) { |
return null; |
} |
@@ -1095,6 +1129,17 @@ class ContextManagerImpl implements ContextManager { |
} |
} |
+ /** |
+ * Return the parent for a new [ContextInfo] with the given [path] folder. |
+ */ |
+ ContextInfo _getParentForNewContext(String path) { |
+ ContextInfo parent = _getInnermostContextInfoFor(path); |
+ if (parent != null) { |
+ return parent; |
+ } |
+ return rootInfo; |
+ } |
+ |
void _handleWatchEvent(WatchEvent event) { |
// Figure out which context this event applies to. |
// TODO(brianwilkerson) If a file is explicitly included in one context |