| 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.computer.dart.toplevel; | 5 library services.completion.computer.dart.toplevel; |
| 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, |
| 11 ElementKind; | 11 ElementKind; |
| 12 import 'package:analysis_server/src/services/completion/dart_completion_cache.da
rt'; |
| 12 import 'package:analysis_server/src/services/completion/dart_completion_manager.
dart'; | 13 import 'package:analysis_server/src/services/completion/dart_completion_manager.
dart'; |
| 13 import 'package:analysis_server/src/services/completion/suggestion_builder.dart'
; | 14 import 'package:analysis_server/src/services/completion/suggestion_builder.dart'
; |
| 14 import 'package:analysis_server/src/services/search/search_engine.dart'; | |
| 15 import 'package:analyzer/src/generated/ast.dart'; | 15 import 'package:analyzer/src/generated/ast.dart'; |
| 16 import 'package:analyzer/src/generated/element.dart'; | 16 import 'package:analyzer/src/generated/element.dart'; |
| 17 import 'package:analyzer/src/generated/resolver.dart'; | |
| 18 import 'package:analyzer/src/generated/scanner.dart'; | 17 import 'package:analyzer/src/generated/scanner.dart'; |
| 19 import 'package:analyzer/src/generated/source.dart'; | |
| 20 | 18 |
| 21 /** | 19 /** |
| 22 * A computer for calculating imported class and top level variable | 20 * A computer for calculating imported class and top level variable |
| 23 * `completion.getSuggestions` request results. | 21 * `completion.getSuggestions` request results. |
| 24 */ | 22 */ |
| 25 class ImportedComputer extends DartCompletionComputer { | 23 class ImportedComputer extends DartCompletionComputer { |
| 26 _ImportedSuggestionBuilder builder; | 24 _ImportedSuggestionBuilder builder; |
| 27 | 25 |
| 28 @override | 26 @override |
| 29 bool computeFast(DartCompletionRequest request) { | 27 bool computeFast(DartCompletionRequest request) { |
| 30 builder = request.node.accept(new _ImportedAstVisitor(request)); | 28 builder = request.node.accept(new _ImportedAstVisitor(request)); |
| 31 if (builder != null) { | 29 if (builder != null) { |
| 32 return builder.computeFast(); | 30 return builder.computeFast(request.node); |
| 33 } | 31 } |
| 34 return true; | 32 return true; |
| 35 } | 33 } |
| 36 | 34 |
| 37 @override | 35 @override |
| 38 Future<bool> computeFull(DartCompletionRequest request) { | 36 Future<bool> computeFull(DartCompletionRequest request) { |
| 39 if (builder != null) { | 37 if (builder != null) { |
| 40 return builder.computeFull(request.node); | 38 return builder.computeFull(request.node); |
| 41 } | 39 } |
| 42 return new Future.value(false); | 40 return new Future.value(false); |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); | 203 return new _ImportedSuggestionBuilder(request, excludeVoidReturn: true); |
| 206 } | 204 } |
| 207 return null; | 205 return null; |
| 208 } | 206 } |
| 209 } | 207 } |
| 210 | 208 |
| 211 /** | 209 /** |
| 212 * [_ImportedSuggestionBuilder] traverses the imports and builds suggestions | 210 * [_ImportedSuggestionBuilder] traverses the imports and builds suggestions |
| 213 * based upon imported elements. | 211 * based upon imported elements. |
| 214 */ | 212 */ |
| 215 class _ImportedSuggestionBuilder { | 213 class _ImportedSuggestionBuilder implements SuggestionBuilder { |
| 216 final DartCompletionRequest request; | 214 final DartCompletionRequest request; |
| 217 final bool typesOnly; | 215 final bool typesOnly; |
| 218 final bool excludeVoidReturn; | 216 final bool excludeVoidReturn; |
| 219 final HashSet<String> completions = new HashSet(); | |
| 220 DartCompletionCache cache; | 217 DartCompletionCache cache; |
| 221 String importKey; | |
| 222 | 218 |
| 223 _ImportedSuggestionBuilder(this.request, {this.typesOnly: false, | 219 _ImportedSuggestionBuilder(this.request, {this.typesOnly: false, |
| 224 this.excludeVoidReturn: false}) { | 220 this.excludeVoidReturn: false}) { |
| 225 cache = request.cache; | 221 cache = request.cache; |
| 226 } | 222 } |
| 227 | 223 |
| 228 /** | 224 /** |
| 229 * Compute a hash of the import directives. | |
| 230 */ | |
| 231 String get computeImportKey { | |
| 232 if (importKey == null) { | |
| 233 StringBuffer sb = new StringBuffer(); | |
| 234 request.unit.directives.forEach((Directive directive) { | |
| 235 if (directive is ImportDirective) { | |
| 236 sb.write(directive.toSource()); | |
| 237 } | |
| 238 }); | |
| 239 importKey = sb.toString(); | |
| 240 } | |
| 241 return importKey; | |
| 242 } | |
| 243 | |
| 244 void addCachedSuggestions() { | |
| 245 DartCompletionCache cache = request.cache; | |
| 246 request.suggestions | |
| 247 ..addAll(cache.importedTypeSuggestions) | |
| 248 ..addAll(cache.libraryPrefixSuggestions); | |
| 249 if (!typesOnly) { | |
| 250 request.suggestions.addAll(cache.otherImportedSuggestions); | |
| 251 if (!excludeVoidReturn) { | |
| 252 request.suggestions.addAll(cache.importedVoidReturnSuggestions); | |
| 253 } | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 void addLibraryPrefixSuggestion(ImportElement importElem) { | |
| 258 CompletionSuggestion suggestion = null; | |
| 259 String completion = importElem.prefix.displayName; | |
| 260 if (completion != null && completion.length > 0) { | |
| 261 suggestion = new CompletionSuggestion( | |
| 262 CompletionSuggestionKind.INVOCATION, | |
| 263 CompletionRelevance.DEFAULT, | |
| 264 completion, | |
| 265 completion.length, | |
| 266 0, | |
| 267 importElem.isDeprecated, | |
| 268 false); | |
| 269 LibraryElement lib = importElem.importedLibrary; | |
| 270 if (lib != null) { | |
| 271 suggestion.element = newElement_fromEngine(lib); | |
| 272 } | |
| 273 cache.libraryPrefixSuggestions.add(suggestion); | |
| 274 completions.add(suggestion.completion); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 void addSuggestion(Element element, CompletionRelevance relevance) { | |
| 279 | |
| 280 if (element is ExecutableElement) { | |
| 281 if (element.isOperator) { | |
| 282 return; | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 String completion = element.displayName; | |
| 287 CompletionSuggestion suggestion = new CompletionSuggestion( | |
| 288 CompletionSuggestionKind.INVOCATION, | |
| 289 element.isDeprecated ? CompletionRelevance.LOW : relevance, | |
| 290 completion, | |
| 291 completion.length, | |
| 292 0, | |
| 293 element.isDeprecated, | |
| 294 false); | |
| 295 | |
| 296 suggestion.element = newElement_fromEngine(element); | |
| 297 | |
| 298 DartType type; | |
| 299 if (element is FunctionElement) { | |
| 300 type = element.returnType; | |
| 301 } else if (element is PropertyAccessorElement && element.isGetter) { | |
| 302 type = element.returnType; | |
| 303 } else if (element is TopLevelVariableElement) { | |
| 304 type = element.type; | |
| 305 } | |
| 306 if (type != null) { | |
| 307 String name = type.displayName; | |
| 308 if (name != null && name.length > 0 && name != 'dynamic') { | |
| 309 suggestion.returnType = name; | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 if (element is ExecutableElement) { | |
| 314 DartType returnType = element.returnType; | |
| 315 if (returnType != null && returnType.isVoid) { | |
| 316 cache.importedVoidReturnSuggestions.add(suggestion); | |
| 317 } else { | |
| 318 cache.otherImportedSuggestions.add(suggestion); | |
| 319 } | |
| 320 } else if (element is ClassElement) { | |
| 321 cache.importedTypeSuggestions.add(suggestion); | |
| 322 } else { | |
| 323 cache.otherImportedSuggestions.add(suggestion); | |
| 324 } | |
| 325 completions.add(suggestion.completion); | |
| 326 } | |
| 327 | |
| 328 void addSuggestions(List<Element> elements) { | |
| 329 elements.forEach((Element elem) { | |
| 330 addSuggestion(elem, CompletionRelevance.DEFAULT); | |
| 331 }); | |
| 332 } | |
| 333 | |
| 334 /** | |
| 335 * If the needed information is cached, then add suggestions and return `true` | 225 * If the needed information is cached, then add suggestions and return `true` |
| 336 * else return `false` indicating that additional work is necessary. | 226 * else return `false` indicating that additional work is necessary. |
| 337 */ | 227 */ |
| 338 bool computeFast() { | 228 bool computeFast(AstNode node) { |
| 339 if (cache.importKey == computeImportKey) { | 229 CompilationUnit unit = request.unit; |
| 340 addCachedSuggestions(); | 230 if (cache.isImportInfoCached(unit)) { |
| 231 _addInheritedSuggestions(node); |
| 232 _addTopLevelSuggestions(); |
| 341 return true; | 233 return true; |
| 342 } | 234 } |
| 343 return false; | 235 return false; |
| 344 } | 236 } |
| 345 | 237 |
| 346 /** | 238 /** |
| 347 * Compute suggested based upon imported elements. | 239 * Compute suggested based upon imported elements. |
| 348 */ | 240 */ |
| 349 computeFull(AstNode node) { | 241 Future<bool> computeFull(AstNode node) { |
| 350 CompilationUnit unit = node.getAncestor((p) => p is CompilationUnit); | 242 return cache.computeImportInfo( |
| 351 cache.importedTypeSuggestions = <CompletionSuggestion>[]; | 243 request.unit, |
| 352 cache.libraryPrefixSuggestions = <CompletionSuggestion>[]; | 244 request.searchEngine).then((_) { |
| 353 cache.otherImportedSuggestions = <CompletionSuggestion>[]; | 245 _addInheritedSuggestions(node); |
| 354 cache.importedVoidReturnSuggestions = <CompletionSuggestion>[]; | 246 _addTopLevelSuggestions(); |
| 247 return true; |
| 248 }); |
| 249 } |
| 355 | 250 |
| 356 // Exclude elements from local library | 251 /** |
| 357 // because they are provided by LocalComputer | 252 * Add imported element suggestions. |
| 358 Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); | 253 */ |
| 359 excludedLibs.add(unit.element.enclosingElement); | 254 void _addElementSuggestions(List<Element> elements) { |
| 360 | 255 elements.forEach((Element elem) { |
| 361 // Include explicitly imported elements | 256 if (elem is! ClassElement) { |
| 362 Map<String, ClassElement> classMap = new Map<String, ClassElement>(); | 257 if (typesOnly) { |
| 363 unit.directives.forEach((Directive directive) { | 258 return; |
| 364 if (directive is ImportDirective) { | 259 } |
| 365 ImportElement importElem = directive.element; | 260 if (elem is ExecutableElement) { |
| 366 if (importElem != null && importElem.importedLibrary != null) { | 261 if (elem.isOperator) { |
| 367 if (directive.prefix == null) { | 262 return; |
| 368 Namespace importNamespace = | 263 } |
| 369 new NamespaceBuilder().createImportNamespaceForDirective(importE
lem); | 264 DartType returnType = elem.returnType; |
| 370 // Include top level elements | 265 if (returnType != null && returnType.isVoid) { |
| 371 importNamespace.definedNames.forEach((String name, Element elem) { | 266 if (excludeVoidReturn) { |
| 372 if (elem is ClassElement) { | 267 return; |
| 373 classMap[name] = elem; | 268 } |
| 374 } | |
| 375 addSuggestion(elem, CompletionRelevance.DEFAULT); | |
| 376 }); | |
| 377 } else { | |
| 378 // Exclude elements from prefixed imports | |
| 379 // because they are provided by InvocationComputer | |
| 380 excludedLibs.add(importElem.importedLibrary); | |
| 381 addLibraryPrefixSuggestion(importElem); | |
| 382 } | 269 } |
| 383 } | 270 } |
| 384 } | 271 } |
| 272 request.suggestions.add( |
| 273 createElementSuggestion(elem, relevance: CompletionRelevance.DEFAULT))
; |
| 385 }); | 274 }); |
| 275 } |
| 386 | 276 |
| 387 // Include implicitly imported dart:core elements | 277 /** |
| 388 Source coreUri = request.context.sourceFactory.forUri('dart:core'); | 278 * Add suggestions for any inherited imported members. |
| 389 LibraryElement coreLib = request.context.getLibraryElement(coreUri); | 279 */ |
| 390 Namespace coreNamespace = | 280 void _addInheritedSuggestions(AstNode node) { |
| 391 new NamespaceBuilder().createPublicNamespaceForLibrary(coreLib); | |
| 392 coreNamespace.definedNames.forEach((String name, Element elem) { | |
| 393 if (elem is ClassElement) { | |
| 394 classMap[name] = elem; | |
| 395 } | |
| 396 addSuggestion(elem, CompletionRelevance.DEFAULT); | |
| 397 }); | |
| 398 | |
| 399 // Build a list of inherited types that are imported | |
| 400 // and include any inherited imported members | |
| 401 var classDecl = node.getAncestor((p) => p is ClassDeclaration); | 281 var classDecl = node.getAncestor((p) => p is ClassDeclaration); |
| 402 if (classDecl is ClassDeclaration) { | 282 if (classDecl is ClassDeclaration) { |
| 283 // Build a list of inherited types that are imported |
| 284 // and include any inherited imported members |
| 403 List<String> inheritedTypes = new List<String>(); | 285 List<String> inheritedTypes = new List<String>(); |
| 404 visitInheritedTypes(classDecl, (ClassDeclaration classDecl) { | 286 visitInheritedTypes(classDecl, (_) { |
| 405 // ignored | 287 // local declarations are handled by the local computer |
| 406 }, (String typeName) { | 288 }, (String typeName) { |
| 407 inheritedTypes.add(typeName); | 289 inheritedTypes.add(typeName); |
| 408 }); | 290 }); |
| 409 Set<String> visited = new Set<String>(); | 291 HashSet<String> visited = new HashSet<String>(); |
| 410 while (inheritedTypes.length > 0) { | 292 while (inheritedTypes.length > 0) { |
| 411 String name = inheritedTypes.removeLast(); | 293 String name = inheritedTypes.removeLast(); |
| 412 ClassElement elem = classMap[name]; | 294 ClassElement elem = cache.importedClassMap[name]; |
| 413 if (visited.add(name) && elem != null) { | 295 if (visited.add(name) && elem != null) { |
| 414 addSuggestions(elem.accessors); | 296 _addElementSuggestions(elem.accessors); |
| 415 addSuggestions(elem.methods); | 297 _addElementSuggestions(elem.methods); |
| 416 elem.allSupertypes.forEach((InterfaceType type) { | 298 elem.allSupertypes.forEach((InterfaceType type) { |
| 417 if (visited.add(type.name)) { | 299 if (visited.add(type.name)) { |
| 418 addSuggestions(type.accessors); | 300 _addElementSuggestions(type.accessors); |
| 419 addSuggestions(type.methods); | 301 _addElementSuggestions(type.methods); |
| 420 } | 302 } |
| 421 }); | 303 }); |
| 422 } | 304 } |
| 423 } | 305 } |
| 424 } | 306 } |
| 307 } |
| 425 | 308 |
| 426 // Add non-imported elements as low relevance | 309 void _addTopLevelSuggestions() { |
| 427 var future = request.searchEngine.searchTopLevelDeclarations(''); | 310 DartCompletionCache cache = request.cache; |
| 428 return future.then((List<SearchMatch> matches) { | 311 request.suggestions |
| 429 matches.forEach((SearchMatch match) { | 312 ..addAll(cache.importedTypeSuggestions) |
| 430 if (match.kind == MatchKind.DECLARATION) { | 313 ..addAll(cache.libraryPrefixSuggestions); |
| 431 Element element = match.element; | 314 if (!typesOnly) { |
| 432 if (element.isPublic && | 315 request.suggestions.addAll(cache.otherImportedSuggestions); |
| 433 !excludedLibs.contains(element.library) && | 316 if (!excludeVoidReturn) { |
| 434 !completions.contains(element.displayName)) { | 317 request.suggestions.addAll(cache.importedVoidReturnSuggestions); |
| 435 addSuggestion(element, CompletionRelevance.LOW); | 318 } |
| 436 } | 319 } |
| 437 } | |
| 438 }); | |
| 439 cache.importKey = computeImportKey; | |
| 440 addCachedSuggestions(); | |
| 441 return true; | |
| 442 }); | |
| 443 } | 320 } |
| 444 } | 321 } |
| OLD | NEW |