| 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 futureLibUnit = |
| 126 context.computeResolvedCompilationUnitAsync(libSource, libSource); |
| 127 } |
| 128 } else { |
| 129 futureLibUnit = new Future.value(null); |
| 130 } |
| 131 |
| 104 // Include explicitly imported elements | 132 // Include explicitly imported elements |
| 105 unit.directives.forEach((Directive directive) { | 133 Future futureImportsCached = futureLibUnit.then((CompilationUnit libUnit) { |
| 106 if (directive is ImportDirective) { | 134 if (libUnit != null) { |
| 107 ImportElement importElem = directive.element; | 135 libUnit.directives.forEach((Directive directive) { |
| 108 if (importElem != null && importElem.importedLibrary != null) { | 136 if (directive is ImportDirective) { |
| 109 if (directive.prefix == null) { | 137 ImportElement importElem = directive.element; |
| 110 Namespace importNamespace = | 138 if (importElem != null && importElem.importedLibrary != null) { |
| 111 new NamespaceBuilder().createImportNamespaceForDirective(importE
lem); | 139 if (directive.prefix == null) { |
| 112 // Include top level elements | 140 Namespace importNamespace = |
| 113 importNamespace.definedNames.forEach((String name, Element elem) { | 141 new NamespaceBuilder().createImportNamespaceForDirective(imp
ortElem); |
| 114 if (elem is ClassElement) { | 142 // Include top level elements |
| 115 importedClassMap[name] = elem; | 143 importNamespace.definedNames.forEach( |
| 144 (String name, Element elem) { |
| 145 if (elem is ClassElement) { |
| 146 importedClassMap[name] = elem; |
| 147 } |
| 148 addSuggestion(elem, CompletionRelevance.DEFAULT); |
| 149 }); |
| 150 } else { |
| 151 // Exclude elements from prefixed imports |
| 152 // because they are provided by InvocationComputer |
| 153 excludedLibs.add(importElem.importedLibrary); |
| 154 _addLibraryPrefixSuggestion(importElem); |
| 116 } | 155 } |
| 117 addSuggestion(elem, CompletionRelevance.DEFAULT); | 156 } |
| 118 }); | 157 } else if (directive is PartDirective) { |
| 119 } else { | 158 CompilationUnitElement partElem = directive.element; |
| 120 // Exclude elements from prefixed imports | 159 if (partElem != null && partElem.source != source) { |
| 121 // because they are provided by InvocationComputer | 160 partElem.accept(new _NonLocalElementCacheVisitor(this)); |
| 122 excludedLibs.add(importElem.importedLibrary); | 161 } |
| 123 _addLibraryPrefixSuggestion(importElem); | |
| 124 } | 162 } |
| 163 }); |
| 164 if (libSource != source) { |
| 165 libUnit.element.accept(new _NonLocalElementCacheVisitor(this)); |
| 125 } | 166 } |
| 126 } | 167 } |
| 168 // Don't wait for search of lower relevance results to complete. |
| 169 // Set key indicating results are ready, and lower relevance results |
| 170 // will be added to the cache when the search completes. |
| 171 _importKey = _computeImportKey(unit); |
| 172 return true; |
| 127 }); | 173 }); |
| 128 | 174 |
| 129 // Include implicitly imported dart:core elements | 175 // Include implicitly imported dart:core elements |
| 130 Source coreUri = context.sourceFactory.forUri('dart:core'); | 176 Source coreUri = context.sourceFactory.forUri('dart:core'); |
| 131 LibraryElement coreLib = context.getLibraryElement(coreUri); | 177 LibraryElement coreLib = context.getLibraryElement(coreUri); |
| 132 Namespace coreNamespace = | 178 Namespace coreNamespace = |
| 133 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); | 179 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); |
| 134 coreNamespace.definedNames.forEach((String name, Element elem) { | 180 coreNamespace.definedNames.forEach((String name, Element elem) { |
| 135 if (elem is ClassElement) { | 181 if (elem is ClassElement) { |
| 136 importedClassMap[name] = elem; | 182 importedClassMap[name] = elem; |
| 137 } | 183 } |
| 138 addSuggestion(elem, CompletionRelevance.DEFAULT); | 184 addSuggestion(elem, CompletionRelevance.DEFAULT); |
| 139 }); | 185 }); |
| 140 _objectClassElement = importedClassMap['Object']; | 186 _objectClassElement = importedClassMap['Object']; |
| 141 | 187 |
| 142 /* | 188 // Add non-imported elements as low relevance |
| 143 * Don't wait for search of lower relevance results to complete. | 189 // after the imported element suggestions have been added |
| 144 * Set key indicating results are ready, and lower relevance results | 190 Future<bool> futureAllCached = futureImportsCached.then((_) { |
| 145 * will be added to the cache when the search completes. | 191 return searchEngine.searchTopLevelDeclarations( |
| 146 */ | 192 '').then((List<SearchMatch> matches) { |
| 147 _importKey = _computeImportKey(unit); | 193 matches.forEach((SearchMatch match) { |
| 194 if (match.kind == MatchKind.DECLARATION) { |
| 195 Element element = match.element; |
| 196 if (element.isPublic && |
| 197 !excludedLibs.contains(element.library) && |
| 198 !_importedCompletions.contains(element.displayName)) { |
| 199 addSuggestion(element, CompletionRelevance.LOW); |
| 200 } |
| 201 } |
| 202 }); |
| 203 return true; |
| 204 }); |
| 205 }); |
| 148 | 206 |
| 149 // Add non-imported elements as low relevance | 207 return shouldWaitForLowPrioritySuggestions ? |
| 150 Future<List<SearchMatch>> future = | 208 futureAllCached : |
| 151 searchEngine.searchTopLevelDeclarations(''); | 209 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 } | 210 } |
| 166 | 211 |
| 167 /** | 212 /** |
| 168 * Return the [ClassElement] for Object. | 213 * Return the [ClassElement] for Object. |
| 169 */ | 214 */ |
| 170 ClassElement get objectClassElement { | 215 ClassElement get objectClassElement { |
| 171 if (_objectClassElement == null) { | 216 if (_objectClassElement == null) { |
| 172 Source coreUri = context.sourceFactory.forUri('dart:core'); | 217 Source coreUri = context.sourceFactory.forUri('dart:core'); |
| 173 LibraryElement coreLib = context.getLibraryElement(coreUri); | 218 LibraryElement coreLib = context.getLibraryElement(coreUri); |
| 174 Namespace coreNamespace = | 219 Namespace coreNamespace = |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 String _computeImportKey(CompilationUnit unit) { | 283 String _computeImportKey(CompilationUnit unit) { |
| 239 StringBuffer sb = new StringBuffer(); | 284 StringBuffer sb = new StringBuffer(); |
| 240 unit.directives.forEach((Directive directive) { | 285 unit.directives.forEach((Directive directive) { |
| 241 if (directive is ImportDirective) { | 286 if (directive is ImportDirective) { |
| 242 sb.write(directive.toSource()); | 287 sb.write(directive.toSource()); |
| 243 } | 288 } |
| 244 }); | 289 }); |
| 245 return sb.toString(); | 290 return sb.toString(); |
| 246 } | 291 } |
| 247 } | 292 } |
| 293 |
| 294 /** |
| 295 * A visitor for building suggestions based upon the elements defined by |
| 296 * a source file contained in the same library but not the same as |
| 297 * the source in which the completions are being requested. |
| 298 */ |
| 299 class _NonLocalElementCacheVisitor extends GeneralizingElementVisitor { |
| 300 final DartCompletionCache cache; |
| 301 |
| 302 _NonLocalElementCacheVisitor(this.cache); |
| 303 |
| 304 @override |
| 305 void visitClassElement(ClassElement element) { |
| 306 cache.addSuggestion(element, CompletionRelevance.DEFAULT); |
| 307 } |
| 308 |
| 309 @override |
| 310 void visitCompilationUnitElement(CompilationUnitElement element) { |
| 311 element.visitChildren(this); |
| 312 } |
| 313 |
| 314 @override |
| 315 void visitElement(Element element) { |
| 316 // ignored |
| 317 } |
| 318 |
| 319 @override |
| 320 void visitFunctionElement(FunctionElement element) { |
| 321 cache.addSuggestion(element, CompletionRelevance.DEFAULT); |
| 322 } |
| 323 |
| 324 @override |
| 325 void visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
| 326 cache.addSuggestion(element, CompletionRelevance.DEFAULT); |
| 327 } |
| 328 |
| 329 @override |
| 330 void visitTopLevelVariableElement(TopLevelVariableElement element) { |
| 331 cache.addSuggestion(element, CompletionRelevance.DEFAULT); |
| 332 } |
| 333 } |
| OLD | NEW |