| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library services.completion.dart.cache; | 5 library services.completion.dart.cache; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import 'package:analysis_server/src/protocol_server.dart' hide Element, | 10 import 'package:analysis_server/src/protocol_server.dart' hide Element, |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 DartCompletionCache(AnalysisContext context, Source source) | 74 DartCompletionCache(AnalysisContext context, Source source) |
| 75 : super(context, source); | 75 : super(context, source); |
| 76 | 76 |
| 77 /** | 77 /** |
| 78 * Return a hash of the import directives for the cached import info | 78 * Return a hash of the import directives for the cached import info |
| 79 * or `null` if nothing has been cached. | 79 * or `null` if nothing has been cached. |
| 80 */ | 80 */ |
| 81 String get importKey => _importKey; | 81 String get importKey => _importKey; |
| 82 | 82 |
| 83 /** | 83 /** |
| 84 * Compute suggestions based upon the imports in the given compilation unit. | 84 * Given a resolved compilation unit, compute suggestions based upon the |
| 85 * On return, the cache will be populated except for lower priority | 85 * imports and other dart files (e.g. "part" files) in the library containing |
| 86 * suggestions added as a result of a global search. Callers may wait | 86 * the given compilation unit. The returned future completes when the cache |
| 87 * on the returned future if they want to ensure those lower priority | 87 * is populated. |
| 88 * suggestions are part of the cached suggestions. | 88 * |
| 89 * If [shouldWaitForLowPrioritySuggestions] is `true` then the returned |
| 90 * future will complete when the cache is fully populated. If `false`, |
| 91 * the returned future will complete sooner, but the cache will not include |
| 92 * the lower priority suggestions added as a result of a global search. |
| 93 * In this case, those lower priority suggestions will be added later |
| 94 * when the index has been updated and the global search completes. |
| 89 */ | 95 */ |
| 90 Future<bool> computeImportInfo(CompilationUnit unit, | 96 Future<bool> computeImportInfo(CompilationUnit unit, |
| 91 SearchEngine searchEngine) { | 97 SearchEngine searchEngine, bool shouldWaitForLowPrioritySuggestions) { |
| 92 importedTypeSuggestions = <CompletionSuggestion>[]; | 98 importedTypeSuggestions = <CompletionSuggestion>[]; |
| 93 libraryPrefixSuggestions = <CompletionSuggestion>[]; | 99 libraryPrefixSuggestions = <CompletionSuggestion>[]; |
| 94 otherImportedSuggestions = <CompletionSuggestion>[]; | 100 otherImportedSuggestions = <CompletionSuggestion>[]; |
| 95 importedVoidReturnSuggestions = <CompletionSuggestion>[]; | 101 importedVoidReturnSuggestions = <CompletionSuggestion>[]; |
| 96 importedClassMap = new Map<String, ClassElement>(); | 102 importedClassMap = new Map<String, ClassElement>(); |
| 97 _importedCompletions = new HashSet<String>(); | 103 _importedCompletions = new HashSet<String>(); |
| 98 | 104 |
| 105 // Assert that the compilation unit is resolved |
| 106 // and represents the expected source |
| 107 assert(unit.element.source == source); |
| 108 |
| 99 // Exclude elements from local library | 109 // Exclude elements from local library |
| 100 // because they are provided by LocalComputer | 110 // because they are provided by LocalComputer |
| 101 Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); | 111 Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); |
| 102 excludedLibs.add(unit.element.enclosingElement); | 112 excludedLibs.add(unit.element.enclosingElement); |
| 103 | 113 |
| 114 // Determine the compilation unit defining the library containing |
| 115 // this compilation unit |
| 116 List<Source> libraries = context.getLibrariesContaining(source); |
| 117 Source libSource = null; |
| 118 Future<CompilationUnit> futureLibUnit; |
| 119 if (libraries != null && libraries.length > 0) { |
| 120 libSource = libraries[0]; |
| 121 if (libSource == source) { |
| 122 // If the sources are the same then we already have the library unit |
| 123 futureLibUnit = new Future.value(unit); |
| 124 } else { |
| 125 // If the sources are different, then get the library unit |
| 126 // to traverse the library directives and cache the imported elements |
| 127 futureLibUnit = |
| 128 context.computeResolvedCompilationUnitAsync(libSource, libSource); |
| 129 } |
| 130 } else { |
| 131 futureLibUnit = new Future.value(null); |
| 132 } |
| 133 |
| 104 // Include explicitly imported elements | 134 // Include explicitly imported elements |
| 105 unit.directives.forEach((Directive directive) { | 135 Future futureImportsCached = futureLibUnit.then((CompilationUnit libUnit) { |
| 106 if (directive is ImportDirective) { | 136 if (libUnit != null) { |
| 107 ImportElement importElem = directive.element; | 137 libUnit.directives.forEach((Directive directive) { |
| 108 if (importElem != null && importElem.importedLibrary != null) { | 138 if (directive is ImportDirective) { |
| 109 if (directive.prefix == null) { | 139 ImportElement importElem = directive.element; |
| 110 Namespace importNamespace = | 140 if (importElem != null && importElem.importedLibrary != null) { |
| 111 new NamespaceBuilder().createImportNamespaceForDirective(importE
lem); | 141 if (directive.prefix == null) { |
| 112 // Include top level elements | 142 Namespace importNamespace = |
| 113 importNamespace.definedNames.forEach((String name, Element elem) { | 143 new NamespaceBuilder().createImportNamespaceForDirective(imp
ortElem); |
| 114 if (elem is ClassElement) { | 144 // Include top level elements |
| 115 importedClassMap[name] = elem; | 145 importNamespace.definedNames.forEach( |
| 146 (String name, Element elem) { |
| 147 if (elem is ClassElement) { |
| 148 importedClassMap[name] = elem; |
| 149 } |
| 150 addSuggestion(elem, CompletionRelevance.DEFAULT); |
| 151 }); |
| 152 } else { |
| 153 // Exclude elements from prefixed imports |
| 154 // because they are provided by InvocationComputer |
| 155 excludedLibs.add(importElem.importedLibrary); |
| 156 _addLibraryPrefixSuggestion(importElem); |
| 116 } | 157 } |
| 117 addSuggestion(elem, CompletionRelevance.DEFAULT); | 158 } |
| 118 }); | 159 } else if (directive is PartDirective) { |
| 119 } else { | 160 CompilationUnitElement partElem = directive.element; |
| 120 // Exclude elements from prefixed imports | 161 if (partElem != null && partElem.source != source) { |
| 121 // because they are provided by InvocationComputer | 162 partElem.accept(new _NonLocalElementCacheVisitor(this)); |
| 122 excludedLibs.add(importElem.importedLibrary); | 163 } |
| 123 _addLibraryPrefixSuggestion(importElem); | |
| 124 } | 164 } |
| 165 }); |
| 166 if (libSource != source) { |
| 167 libUnit.element.accept(new _NonLocalElementCacheVisitor(this)); |
| 125 } | 168 } |
| 126 } | 169 } |
| 170 // Don't wait for search of lower relevance results to complete. |
| 171 // Set key indicating results are ready, and lower relevance results |
| 172 // will be added to the cache when the search completes. |
| 173 _importKey = _computeImportKey(unit); |
| 174 return true; |
| 127 }); | 175 }); |
| 128 | 176 |
| 129 // Include implicitly imported dart:core elements | 177 // Include implicitly imported dart:core elements |
| 130 Source coreUri = context.sourceFactory.forUri('dart:core'); | 178 Source coreUri = context.sourceFactory.forUri('dart:core'); |
| 131 LibraryElement coreLib = context.getLibraryElement(coreUri); | 179 LibraryElement coreLib = context.getLibraryElement(coreUri); |
| 132 Namespace coreNamespace = | 180 Namespace coreNamespace = |
| 133 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); | 181 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); |
| 134 coreNamespace.definedNames.forEach((String name, Element elem) { | 182 coreNamespace.definedNames.forEach((String name, Element elem) { |
| 135 if (elem is ClassElement) { | 183 if (elem is ClassElement) { |
| 136 importedClassMap[name] = elem; | 184 importedClassMap[name] = elem; |
| 137 } | 185 } |
| 138 addSuggestion(elem, CompletionRelevance.DEFAULT); | 186 addSuggestion(elem, CompletionRelevance.DEFAULT); |
| 139 }); | 187 }); |
| 140 _objectClassElement = importedClassMap['Object']; | 188 _objectClassElement = importedClassMap['Object']; |
| 141 | 189 |
| 142 /* | 190 // Add non-imported elements as low relevance |
| 143 * Don't wait for search of lower relevance results to complete. | 191 // after the imported element suggestions have been added |
| 144 * Set key indicating results are ready, and lower relevance results | 192 Future<bool> futureAllCached = futureImportsCached.then((_) { |
| 145 * will be added to the cache when the search completes. | 193 return searchEngine.searchTopLevelDeclarations( |
| 146 */ | 194 '').then((List<SearchMatch> matches) { |
| 147 _importKey = _computeImportKey(unit); | 195 matches.forEach((SearchMatch match) { |
| 196 if (match.kind == MatchKind.DECLARATION) { |
| 197 Element element = match.element; |
| 198 if (element.isPublic && |
| 199 !excludedLibs.contains(element.library) && |
| 200 !_importedCompletions.contains(element.displayName)) { |
| 201 addSuggestion(element, CompletionRelevance.LOW); |
| 202 } |
| 203 } |
| 204 }); |
| 205 return true; |
| 206 }); |
| 207 }); |
| 148 | 208 |
| 149 // Add non-imported elements as low relevance | 209 return shouldWaitForLowPrioritySuggestions ? |
| 150 Future<List<SearchMatch>> future = | 210 futureAllCached : |
| 151 searchEngine.searchTopLevelDeclarations(''); | 211 futureImportsCached; |
| 152 return future.then((List<SearchMatch> matches) { | |
| 153 matches.forEach((SearchMatch match) { | |
| 154 if (match.kind == MatchKind.DECLARATION) { | |
| 155 Element element = match.element; | |
| 156 if (element.isPublic && | |
| 157 !excludedLibs.contains(element.library) && | |
| 158 !_importedCompletions.contains(element.displayName)) { | |
| 159 addSuggestion(element, CompletionRelevance.LOW); | |
| 160 } | |
| 161 } | |
| 162 }); | |
| 163 return true; | |
| 164 }); | |
| 165 } | 212 } |
| 166 | 213 |
| 167 /** | 214 /** |
| 168 * Return the [ClassElement] for Object. | 215 * Return the [ClassElement] for Object. |
| 169 */ | 216 */ |
| 170 ClassElement get objectClassElement { | 217 ClassElement get objectClassElement { |
| 171 if (_objectClassElement == null) { | 218 if (_objectClassElement == null) { |
| 172 Source coreUri = context.sourceFactory.forUri('dart:core'); | 219 Source coreUri = context.sourceFactory.forUri('dart:core'); |
| 173 LibraryElement coreLib = context.getLibraryElement(coreUri); | 220 LibraryElement coreLib = context.getLibraryElement(coreUri); |
| 174 Namespace coreNamespace = | 221 Namespace coreNamespace = |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 String _computeImportKey(CompilationUnit unit) { | 285 String _computeImportKey(CompilationUnit unit) { |
| 239 StringBuffer sb = new StringBuffer(); | 286 StringBuffer sb = new StringBuffer(); |
| 240 unit.directives.forEach((Directive directive) { | 287 unit.directives.forEach((Directive directive) { |
| 241 if (directive is ImportDirective) { | 288 if (directive is ImportDirective) { |
| 242 sb.write(directive.toSource()); | 289 sb.write(directive.toSource()); |
| 243 } | 290 } |
| 244 }); | 291 }); |
| 245 return sb.toString(); | 292 return sb.toString(); |
| 246 } | 293 } |
| 247 } | 294 } |
| 295 |
| 296 /** |
| 297 * A visitor for building suggestions based upon the elements defined by |
| 298 * a source file contained in the same library but not the same as |
| 299 * the source in which the completions are being requested. |
| 300 */ |
| 301 class _NonLocalElementCacheVisitor extends GeneralizingElementVisitor { |
| 302 final DartCompletionCache cache; |
| 303 |
| 304 _NonLocalElementCacheVisitor(this.cache); |
| 305 |
| 306 @override |
| 307 void visitClassElement(ClassElement element) { |
| 308 cache.addSuggestion(element, CompletionRelevance.DEFAULT); |
| 309 } |
| 310 |
| 311 @override |
| 312 void visitCompilationUnitElement(CompilationUnitElement element) { |
| 313 element.visitChildren(this); |
| 314 } |
| 315 |
| 316 @override |
| 317 void visitElement(Element element) { |
| 318 // ignored |
| 319 } |
| 320 |
| 321 @override |
| 322 void visitFunctionElement(FunctionElement element) { |
| 323 cache.addSuggestion(element, CompletionRelevance.DEFAULT); |
| 324 } |
| 325 |
| 326 @override |
| 327 void visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
| 328 cache.addSuggestion(element, CompletionRelevance.DEFAULT); |
| 329 } |
| 330 |
| 331 @override |
| 332 void visitTopLevelVariableElement(TopLevelVariableElement element) { |
| 333 cache.addSuggestion(element, CompletionRelevance.DEFAULT); |
| 334 } |
| 335 } |
| OLD | NEW |