Chromium Code Reviews| 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 * Return the [ClassElement] for Object. | |
| 85 */ | |
| 86 ClassElement get objectClassElement { | |
| 87 if (_objectClassElement == null) { | |
| 88 Source coreUri = context.sourceFactory.forUri('dart:core'); | |
| 89 LibraryElement coreLib = context.getLibraryElement(coreUri); | |
| 90 Namespace coreNamespace = | |
| 91 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); | |
| 92 _objectClassElement = coreNamespace.definedNames['Object']; | |
|
scheglov
2014/12/21 04:38:05
Using Namespace seems too heavyweight here.
You co
danrubel
2014/12/22 22:06:11
Great suggestion. Done.
| |
| 93 } | |
| 94 return _objectClassElement; | |
| 95 } | |
| 96 | |
| 97 /** | |
| 84 * Given a resolved compilation unit, compute suggestions based upon the | 98 * Given a resolved compilation unit, compute suggestions based upon the |
| 85 * imports and other dart files (e.g. "part" files) in the library containing | 99 * imports and other dart files (e.g. "part" files) in the library containing |
| 86 * the given compilation unit. The returned future completes when the cache | 100 * the given compilation unit. The returned future completes when the cache |
| 87 * is populated. | 101 * is populated. |
| 88 * | 102 * |
| 89 * If [shouldWaitForLowPrioritySuggestions] is `true` then the returned | 103 * If [shouldWaitForLowPrioritySuggestions] is `true` then the returned |
| 90 * future will complete when the cache is fully populated. If `false`, | 104 * future will complete when the cache is fully populated. If `false`, |
| 91 * the returned future will complete sooner, but the cache will not include | 105 * 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. | 106 * the lower priority suggestions added as a result of a global search. |
| 93 * In this case, those lower priority suggestions will be added later | 107 * In this case, those lower priority suggestions will be added later |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 107 assert(unit.element.source == source); | 121 assert(unit.element.source == source); |
| 108 | 122 |
| 109 // Exclude elements from local library | 123 // Exclude elements from local library |
| 110 // because they are provided by LocalComputer | 124 // because they are provided by LocalComputer |
| 111 Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); | 125 Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); |
| 112 excludedLibs.add(unit.element.enclosingElement); | 126 excludedLibs.add(unit.element.enclosingElement); |
| 113 | 127 |
| 114 // Determine the compilation unit defining the library containing | 128 // Determine the compilation unit defining the library containing |
| 115 // this compilation unit | 129 // this compilation unit |
| 116 List<Source> libraries = context.getLibrariesContaining(source); | 130 List<Source> libraries = context.getLibrariesContaining(source); |
| 117 Source libSource = null; | 131 assert(libraries != null); |
| 118 Future<CompilationUnit> futureLibUnit; | 132 Source libSource = libraries.length > 0 ? libraries[0] : null; |
| 119 if (libraries != null && libraries.length > 0) { | 133 Future<CompilationUnit> futureLibUnit = _computeLibUnit(libSource, unit); |
| 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 | 134 |
| 134 // Include explicitly imported elements | 135 // Include implicitly imported dart:core elements |
| 136 _addDartCoreSuggestions(); | |
| 137 | |
| 138 // Include explicitly imported and part elements | |
| 135 Future futureImportsCached = futureLibUnit.then((CompilationUnit libUnit) { | 139 Future futureImportsCached = futureLibUnit.then((CompilationUnit libUnit) { |
| 136 if (libUnit != null) { | 140 _addImportedElemSuggestions(libSource, libUnit, excludedLibs); |
| 137 libUnit.directives.forEach((Directive directive) { | |
| 138 if (directive is ImportDirective) { | |
| 139 ImportElement importElem = directive.element; | |
| 140 if (importElem != null && importElem.importedLibrary != null) { | |
| 141 if (directive.prefix == null) { | |
| 142 Namespace importNamespace = | |
| 143 new NamespaceBuilder().createImportNamespaceForDirective(imp ortElem); | |
| 144 // Include top level elements | |
| 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); | |
| 157 } | |
| 158 } | |
| 159 } else if (directive is PartDirective) { | |
| 160 CompilationUnitElement partElem = directive.element; | |
| 161 if (partElem != null && partElem.source != source) { | |
| 162 partElem.accept(new _NonLocalElementCacheVisitor(this)); | |
| 163 } | |
| 164 } | |
| 165 }); | |
| 166 if (libSource != source) { | |
| 167 libUnit.element.accept(new _NonLocalElementCacheVisitor(this)); | |
| 168 } | |
| 169 } | |
| 170 // Don't wait for search of lower relevance results to complete. | 141 // Don't wait for search of lower relevance results to complete. |
| 171 // Set key indicating results are ready, and lower relevance results | 142 // Set key indicating results are ready, and lower relevance results |
| 172 // will be added to the cache when the search completes. | 143 // will be added to the cache when the search completes. |
| 173 _importKey = _computeImportKey(unit); | 144 _importKey = _computeImportKey(unit); |
| 174 return true; | 145 return true; |
| 175 }); | 146 }); |
| 176 | 147 |
| 177 // Include implicitly imported dart:core elements | |
| 178 Source coreUri = context.sourceFactory.forUri('dart:core'); | |
| 179 LibraryElement coreLib = context.getLibraryElement(coreUri); | |
| 180 Namespace coreNamespace = | |
| 181 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); | |
| 182 coreNamespace.definedNames.forEach((String name, Element elem) { | |
| 183 if (elem is ClassElement) { | |
| 184 importedClassMap[name] = elem; | |
| 185 } | |
| 186 addSuggestion(elem, CompletionRelevance.DEFAULT); | |
| 187 }); | |
| 188 _objectClassElement = importedClassMap['Object']; | |
| 189 | |
| 190 // Add non-imported elements as low relevance | 148 // Add non-imported elements as low relevance |
| 191 // after the imported element suggestions have been added | 149 // after the imported element suggestions have been added |
| 192 Future<bool> futureAllCached = futureImportsCached.then((_) { | 150 Future<bool> futureAllCached = futureImportsCached.then((_) { |
| 193 return searchEngine.searchTopLevelDeclarations( | 151 return searchEngine.searchTopLevelDeclarations( |
| 194 '').then((List<SearchMatch> matches) { | 152 '').then((List<SearchMatch> matches) { |
| 195 matches.forEach((SearchMatch match) { | 153 _addNonImportedElementSuggestions(matches, excludedLibs); |
| 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; | 154 return true; |
| 206 }); | 155 }); |
| 207 }); | 156 }); |
| 208 | 157 |
| 209 return shouldWaitForLowPrioritySuggestions ? | 158 return shouldWaitForLowPrioritySuggestions ? |
| 210 futureAllCached : | 159 futureAllCached : |
| 211 futureImportsCached; | 160 futureImportsCached; |
| 212 } | 161 } |
| 213 | 162 |
| 214 /** | 163 /** |
| 215 * Return the [ClassElement] for Object. | |
| 216 */ | |
| 217 ClassElement get objectClassElement { | |
| 218 if (_objectClassElement == null) { | |
| 219 Source coreUri = context.sourceFactory.forUri('dart:core'); | |
| 220 LibraryElement coreLib = context.getLibraryElement(coreUri); | |
| 221 Namespace coreNamespace = | |
| 222 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); | |
| 223 _objectClassElement = coreNamespace.definedNames['Object']; | |
| 224 } | |
| 225 return _objectClassElement; | |
| 226 } | |
| 227 | |
| 228 /** | |
| 229 * Return `true` if the import information is cached for the given | 164 * Return `true` if the import information is cached for the given |
| 230 * compilation unit. | 165 * compilation unit. |
| 231 */ | 166 */ |
| 232 bool isImportInfoCached(CompilationUnit unit) => | 167 bool isImportInfoCached(CompilationUnit unit) => |
| 233 _importKey != null && _importKey == _computeImportKey(unit); | 168 _importKey != null && _importKey == _computeImportKey(unit); |
| 234 | 169 |
| 170 /** | |
| 171 * Add suggestions for implicitly imported elements in dart:core. | |
| 172 */ | |
| 173 void _addDartCoreSuggestions() { | |
| 174 Source coreUri = context.sourceFactory.forUri('dart:core'); | |
| 175 LibraryElement coreLib = context.getLibraryElement(coreUri); | |
| 176 Namespace coreNamespace = | |
| 177 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); | |
| 178 coreNamespace.definedNames.forEach((String name, Element elem) { | |
| 179 if (elem is ClassElement) { | |
| 180 importedClassMap[name] = elem; | |
| 181 } | |
| 182 _addSuggestion(elem, CompletionRelevance.DEFAULT); | |
| 183 }); | |
| 184 _objectClassElement = importedClassMap['Object']; | |
| 185 } | |
| 186 | |
| 187 /** | |
| 188 * Add suggestions for explicitly imported and part elements in the given | |
| 189 * library. Add libraries that should not have their elements suggested | |
| 190 * even as low priority to [excludedLibs]. | |
| 191 */ | |
| 192 void _addImportedElemSuggestions(Source libSource, CompilationUnit libUnit, | |
| 193 Set<LibraryElement> excludedLibs) { | |
| 194 if (libUnit != null) { | |
| 195 libUnit.directives.forEach((Directive directive) { | |
| 196 if (directive is ImportDirective) { | |
| 197 ImportElement importElem = directive.element; | |
| 198 if (importElem != null && importElem.importedLibrary != null) { | |
| 199 if (directive.prefix == null) { | |
| 200 Namespace importNamespace = | |
| 201 new NamespaceBuilder().createImportNamespaceForDirective(impor tElem); | |
| 202 // Include top level elements | |
| 203 importNamespace.definedNames.forEach((String name, Element elem) { | |
| 204 if (elem is ClassElement) { | |
| 205 importedClassMap[name] = elem; | |
| 206 } | |
| 207 _addSuggestion(elem, CompletionRelevance.DEFAULT); | |
| 208 }); | |
| 209 } else { | |
| 210 // Exclude elements from prefixed imports | |
| 211 // because they are provided by InvocationComputer | |
| 212 _addLibraryPrefixSuggestion(importElem); | |
| 213 excludedLibs.add(importElem.importedLibrary); | |
| 214 } | |
| 215 } | |
| 216 } else if (directive is PartDirective) { | |
| 217 CompilationUnitElement partElem = directive.element; | |
| 218 if (partElem != null && partElem.source != source) { | |
| 219 partElem.accept(new _NonLocalElementCacheVisitor(this)); | |
| 220 } | |
| 221 } | |
| 222 }); | |
| 223 if (libSource != source) { | |
| 224 libUnit.element.accept(new _NonLocalElementCacheVisitor(this)); | |
| 225 } | |
| 226 } | |
| 227 } | |
| 228 | |
| 235 void _addLibraryPrefixSuggestion(ImportElement importElem) { | 229 void _addLibraryPrefixSuggestion(ImportElement importElem) { |
| 236 CompletionSuggestion suggestion = null; | 230 CompletionSuggestion suggestion = null; |
| 237 String completion = importElem.prefix.displayName; | 231 String completion = importElem.prefix.displayName; |
| 238 if (completion != null && completion.length > 0) { | 232 if (completion != null && completion.length > 0) { |
| 239 suggestion = new CompletionSuggestion( | 233 suggestion = new CompletionSuggestion( |
| 240 CompletionSuggestionKind.INVOCATION, | 234 CompletionSuggestionKind.INVOCATION, |
| 241 CompletionRelevance.DEFAULT, | 235 CompletionRelevance.DEFAULT, |
| 242 completion, | 236 completion, |
| 243 completion.length, | 237 completion.length, |
| 244 0, | 238 0, |
| 245 importElem.isDeprecated, | 239 importElem.isDeprecated, |
| 246 false); | 240 false); |
| 247 LibraryElement lib = importElem.importedLibrary; | 241 LibraryElement lib = importElem.importedLibrary; |
| 248 if (lib != null) { | 242 if (lib != null) { |
| 249 suggestion.element = newElement_fromEngine(lib); | 243 suggestion.element = newElement_fromEngine(lib); |
| 250 } | 244 } |
| 251 libraryPrefixSuggestions.add(suggestion); | 245 libraryPrefixSuggestions.add(suggestion); |
| 252 _importedCompletions.add(suggestion.completion); | 246 _importedCompletions.add(suggestion.completion); |
| 253 } | 247 } |
| 254 } | 248 } |
| 255 | 249 |
| 256 void addSuggestion(Element element, CompletionRelevance relevance) { | 250 /** |
| 251 * Add suggestions for all top level elements in the context | |
| 252 * excluding those elemnents for which suggestions have already been added. | |
| 253 */ | |
| 254 void _addNonImportedElementSuggestions(List<SearchMatch> matches, | |
| 255 Set<LibraryElement> excludedLibs) { | |
| 256 matches.forEach((SearchMatch match) { | |
| 257 if (match.kind == MatchKind.DECLARATION) { | |
| 258 Element element = match.element; | |
| 259 if (element.isPublic && | |
| 260 !excludedLibs.contains(element.library) && | |
| 261 !_importedCompletions.contains(element.displayName)) { | |
| 262 _addSuggestion(element, CompletionRelevance.LOW); | |
| 263 } | |
| 264 } | |
| 265 }); | |
| 266 } | |
| 267 | |
| 268 /** | |
| 269 * Add a suggestion for the given element. | |
| 270 */ | |
| 271 void _addSuggestion(Element element, CompletionRelevance relevance) { | |
| 257 | 272 |
| 258 if (element is ExecutableElement) { | 273 if (element is ExecutableElement) { |
| 259 if (element.isOperator) { | 274 if (element.isOperator) { |
| 260 return; | 275 return; |
| 261 } | 276 } |
| 262 } | 277 } |
| 263 | 278 |
| 264 CompletionSuggestion suggestion = | 279 CompletionSuggestion suggestion = |
| 265 createElementSuggestion(element, relevance: relevance); | 280 createElementSuggestion(element, relevance: relevance); |
| 266 | 281 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 282 /** | 297 /** |
| 283 * Compute the hash of the imports for the given compilation unit. | 298 * Compute the hash of the imports for the given compilation unit. |
| 284 */ | 299 */ |
| 285 String _computeImportKey(CompilationUnit unit) { | 300 String _computeImportKey(CompilationUnit unit) { |
| 286 StringBuffer sb = new StringBuffer(); | 301 StringBuffer sb = new StringBuffer(); |
| 287 unit.directives.forEach((Directive directive) { | 302 unit.directives.forEach((Directive directive) { |
| 288 sb.write(directive.toSource()); | 303 sb.write(directive.toSource()); |
| 289 }); | 304 }); |
| 290 return sb.toString(); | 305 return sb.toString(); |
| 291 } | 306 } |
| 307 | |
| 308 /** | |
| 309 * Compute the library unit for the given library source, | |
| 310 * where the [unit] is the resolved compilation unit associated with [source]. | |
| 311 */ | |
| 312 Future<CompilationUnit> _computeLibUnit(Source libSource, | |
| 313 CompilationUnit unit) { | |
| 314 // If the sources are the same then we already have the library unit | |
| 315 if (libSource == source) { | |
| 316 return new Future.value(unit); | |
| 317 } | |
| 318 // If [source] is a part, then compute the library unit | |
| 319 if (libSource != null) { | |
| 320 return context.computeResolvedCompilationUnitAsync(libSource, libSource); | |
| 321 } | |
| 322 return new Future.value(null); | |
| 323 } | |
| 292 } | 324 } |
| 293 | 325 |
| 294 /** | 326 /** |
| 295 * A visitor for building suggestions based upon the elements defined by | 327 * 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 | 328 * a source file contained in the same library but not the same as |
| 297 * the source in which the completions are being requested. | 329 * the source in which the completions are being requested. |
| 298 */ | 330 */ |
| 299 class _NonLocalElementCacheVisitor extends GeneralizingElementVisitor { | 331 class _NonLocalElementCacheVisitor extends GeneralizingElementVisitor { |
| 300 final DartCompletionCache cache; | 332 final DartCompletionCache cache; |
| 301 | 333 |
| 302 _NonLocalElementCacheVisitor(this.cache); | 334 _NonLocalElementCacheVisitor(this.cache); |
| 303 | 335 |
| 304 @override | 336 @override |
| 305 void visitClassElement(ClassElement element) { | 337 void visitClassElement(ClassElement element) { |
| 306 cache.addSuggestion(element, CompletionRelevance.DEFAULT); | 338 cache._addSuggestion(element, CompletionRelevance.DEFAULT); |
| 307 } | 339 } |
| 308 | 340 |
| 309 @override | 341 @override |
| 310 void visitCompilationUnitElement(CompilationUnitElement element) { | 342 void visitCompilationUnitElement(CompilationUnitElement element) { |
| 311 element.visitChildren(this); | 343 element.visitChildren(this); |
| 312 } | 344 } |
| 313 | 345 |
| 314 @override | 346 @override |
| 315 void visitElement(Element element) { | 347 void visitElement(Element element) { |
| 316 // ignored | 348 // ignored |
| 317 } | 349 } |
| 318 | 350 |
| 319 @override | 351 @override |
| 320 void visitFunctionElement(FunctionElement element) { | 352 void visitFunctionElement(FunctionElement element) { |
| 321 cache.addSuggestion(element, CompletionRelevance.DEFAULT); | 353 cache._addSuggestion(element, CompletionRelevance.DEFAULT); |
| 322 } | 354 } |
| 323 | 355 |
| 324 @override | 356 @override |
| 325 void visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { | 357 void visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
| 326 cache.addSuggestion(element, CompletionRelevance.DEFAULT); | 358 cache._addSuggestion(element, CompletionRelevance.DEFAULT); |
| 327 } | 359 } |
| 328 | 360 |
| 329 @override | 361 @override |
| 330 void visitTopLevelVariableElement(TopLevelVariableElement element) { | 362 void visitTopLevelVariableElement(TopLevelVariableElement element) { |
| 331 cache.addSuggestion(element, CompletionRelevance.DEFAULT); | 363 cache._addSuggestion(element, CompletionRelevance.DEFAULT); |
| 332 } | 364 } |
| 333 } | 365 } |
| OLD | NEW |