| 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.suggestion.builder; | 5 library services.completion.suggestion.builder; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/protocol_server.dart' as protocol; | 9 import 'package:analysis_server/src/protocol_server.dart' as protocol; |
| 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_manager.
dart'; | 12 import 'package:analysis_server/src/services/completion/dart_completion_manager.
dart'; |
| 13 import 'package:analyzer/src/generated/ast.dart'; | 13 import 'package:analyzer/src/generated/ast.dart'; |
| 14 import 'package:analyzer/src/generated/element.dart'; | 14 import 'package:analyzer/src/generated/element.dart'; |
| 15 import 'package:analyzer/src/generated/scanner.dart'; | |
| 16 import 'package:analyzer/src/generated/utilities_dart.dart'; | 15 import 'package:analyzer/src/generated/utilities_dart.dart'; |
| 17 | 16 |
| 18 final DYNAMIC = 'dynamic'; | 17 final DYNAMIC = 'dynamic'; |
| 19 final DartType NO_RETURN_TYPE = new _NoReturnType(); | 18 final DartType NO_RETURN_TYPE = new _NoReturnType(); |
| 20 | 19 |
| 21 /** | 20 /** |
| 22 * Create a suggestion based upon the given imported element. | 21 * Return a suggestion based upon the given element |
| 22 * or `null` if a suggestion is not appropriate for the given element. |
| 23 */ | 23 */ |
| 24 CompletionSuggestion createElementSuggestion(Element element, {int relevance: | 24 CompletionSuggestion createSuggestion(Element element, |
| 25 COMPLETION_RELEVANCE_DEFAULT}) { | 25 {CompletionSuggestionKind kind: CompletionSuggestionKind.INVOCATION, |
| 26 int relevance: COMPLETION_RELEVANCE_DEFAULT}) { |
| 27 DartType type; |
| 28 if (element is ExecutableElement) { |
| 29 if (element.isOperator) { |
| 30 return null; |
| 31 } |
| 32 if (element is PropertyAccessorElement && element.isSetter) { |
| 33 type = NO_RETURN_TYPE; |
| 34 } else { |
| 35 type = element.returnType; |
| 36 } |
| 37 } else if (element is VariableElement) { |
| 38 type = element.type; |
| 39 } else if (element is FunctionTypeAliasElement) { |
| 40 type = element.returnType; |
| 41 } else { |
| 42 type = NO_RETURN_TYPE; |
| 43 } |
| 26 String completion = element.displayName; | 44 String completion = element.displayName; |
| 45 bool isDeprecated = element.isDeprecated; |
| 27 CompletionSuggestion suggestion = new CompletionSuggestion( | 46 CompletionSuggestion suggestion = new CompletionSuggestion( |
| 28 CompletionSuggestionKind.INVOCATION, | 47 kind, |
| 29 element.isDeprecated ? COMPLETION_RELEVANCE_LOW : relevance, | 48 isDeprecated ? COMPLETION_RELEVANCE_LOW : relevance, |
| 30 completion, | 49 completion, |
| 31 completion.length, | 50 completion.length, |
| 32 0, | 51 0, |
| 33 element.isDeprecated, | 52 isDeprecated, |
| 34 false); | 53 false); |
| 35 | 54 suggestion.element = protocol.newElement_fromEngine(element); |
| 36 suggestion.element = newElement_fromEngine(element); | 55 if (element is ClassMemberElement) { |
| 37 | 56 ClassElement enclosingElement = element.enclosingElement; |
| 38 DartType type; | 57 if (enclosingElement != null) { |
| 39 if (element is ExecutableElement) { | 58 suggestion.declaringType = enclosingElement.displayName; |
| 40 type = element.returnType; | 59 } |
| 41 } else if (element is VariableElement) { | |
| 42 type = element.type; | |
| 43 } else { | |
| 44 type = NO_RETURN_TYPE; | |
| 45 } | 60 } |
| 46 suggestion.returnType = _nameForType(type); | 61 suggestion.returnType = _nameForType(type); |
| 62 if (element is ExecutableElement && element is! PropertyAccessorElement) { |
| 63 suggestion.parameterNames = element.parameters.map( |
| 64 (ParameterElement parameter) => parameter.name).toList(); |
| 65 suggestion.parameterTypes = element.parameters.map( |
| 66 (ParameterElement parameter) => parameter.type.displayName).toList(); |
| 67 suggestion.requiredParameterCount = element.parameters.where( |
| 68 (ParameterElement parameter) => |
| 69 parameter.parameterKind == ParameterKind.REQUIRED).length; |
| 70 suggestion.hasNamedParameters = element.parameters.any( |
| 71 (ParameterElement parameter) => parameter.parameterKind == ParameterKind
.NAMED); |
| 72 } |
| 47 return suggestion; | 73 return suggestion; |
| 48 } | 74 } |
| 49 | 75 |
| 50 /** | 76 /** |
| 51 * Call the given function with each non-null non-empty inherited type name | 77 * Call the given function with each non-null non-empty inherited type name |
| 52 * that is defined in the given class. | 78 * that is defined in the given class. |
| 53 */ | 79 */ |
| 54 visitInheritedTypeNames(ClassDeclaration node, void inherited(String name)) { | 80 visitInheritedTypeNames(ClassDeclaration node, void inherited(String name)) { |
| 55 | 81 |
| 56 void visit(TypeName type) { | 82 void visit(TypeName type) { |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 } | 168 } |
| 143 //TODO (danrubel) include type arguments ?? | 169 //TODO (danrubel) include type arguments ?? |
| 144 return name; | 170 return name; |
| 145 } | 171 } |
| 146 | 172 |
| 147 /** | 173 /** |
| 148 * This class visits elements in a class and provides suggestions based upon | 174 * This class visits elements in a class and provides suggestions based upon |
| 149 * the visible members in that class. Clients should call | 175 * the visible members in that class. Clients should call |
| 150 * [ClassElementSuggestionBuilder.suggestionsFor]. | 176 * [ClassElementSuggestionBuilder.suggestionsFor]. |
| 151 */ | 177 */ |
| 152 class ClassElementSuggestionBuilder extends _AbstractSuggestionBuilder { | 178 class ClassElementSuggestionBuilder extends GeneralizingElementVisitor with |
| 179 ElementSuggestionBuilder { |
| 153 final bool staticOnly; | 180 final bool staticOnly; |
| 181 final DartCompletionRequest request; |
| 154 | 182 |
| 155 ClassElementSuggestionBuilder(DartCompletionRequest request, bool staticOnly) | 183 ClassElementSuggestionBuilder(this.request, bool staticOnly) |
| 156 : super(request, CompletionSuggestionKind.INVOCATION), | 184 : this.staticOnly = staticOnly; |
| 157 this.staticOnly = staticOnly; | 185 |
| 186 @override |
| 187 CompletionSuggestionKind get kind => CompletionSuggestionKind.INVOCATION; |
| 158 | 188 |
| 159 @override | 189 @override |
| 160 visitClassElement(ClassElement element) { | 190 visitClassElement(ClassElement element) { |
| 161 element.visitChildren(this); | 191 element.visitChildren(this); |
| 162 element.allSupertypes.forEach((InterfaceType type) { | 192 element.allSupertypes.forEach((InterfaceType type) { |
| 163 type.element.visitChildren(this); | 193 type.element.visitChildren(this); |
| 164 }); | 194 }); |
| 165 } | 195 } |
| 166 | 196 |
| 167 @override | 197 @override |
| 168 visitElement(Element element) { | 198 visitElement(Element element) { |
| 169 // ignored | 199 // ignored |
| 170 } | 200 } |
| 171 | 201 |
| 172 @override | 202 @override |
| 173 visitFieldElement(FieldElement element) { | 203 visitFieldElement(FieldElement element) { |
| 174 if (staticOnly && !element.isStatic) { | 204 if (staticOnly && !element.isStatic) { |
| 175 return; | 205 return; |
| 176 } | 206 } |
| 177 _addElementSuggestion(element, element.type, element.enclosingElement); | 207 addSuggestion(element); |
| 178 } | 208 } |
| 179 | 209 |
| 180 @override | 210 @override |
| 181 visitMethodElement(MethodElement element) { | 211 visitMethodElement(MethodElement element) { |
| 182 if (staticOnly && !element.isStatic) { | 212 if (staticOnly && !element.isStatic) { |
| 183 return; | 213 return; |
| 184 } | 214 } |
| 185 if (element.isOperator) { | 215 if (element.isOperator) { |
| 186 return; | 216 return; |
| 187 } | 217 } |
| 188 _addElementSuggestion( | 218 addSuggestion(element); |
| 189 element, | |
| 190 element.returnType, | |
| 191 element.enclosingElement); | |
| 192 } | 219 } |
| 193 | 220 |
| 194 @override | 221 @override |
| 195 visitPropertyAccessorElement(PropertyAccessorElement element) { | 222 visitPropertyAccessorElement(PropertyAccessorElement element) { |
| 196 if (staticOnly && !element.isStatic) { | 223 if (staticOnly && !element.isStatic) { |
| 197 return; | 224 return; |
| 198 } | 225 } |
| 199 if (element.isGetter) { | 226 addSuggestion(element); |
| 200 _addElementSuggestion( | |
| 201 element, | |
| 202 element.returnType, | |
| 203 element.enclosingElement); | |
| 204 } else if (element.isSetter) { | |
| 205 _addElementSuggestion(element, NO_RETURN_TYPE, element.enclosingElement); | |
| 206 } | |
| 207 } | 227 } |
| 208 | 228 |
| 209 /** | 229 /** |
| 210 * Add suggestions for the visible members in the given class | 230 * Add suggestions for the visible members in the given class |
| 211 */ | 231 */ |
| 212 static void suggestionsFor(DartCompletionRequest request, Element element, | 232 static void suggestionsFor(DartCompletionRequest request, Element element, |
| 213 {bool staticOnly: false}) { | 233 {bool staticOnly: false}) { |
| 214 if (element == DynamicElementImpl.instance) { | 234 if (element == DynamicElementImpl.instance) { |
| 215 element = request.cache.objectClassElement; | 235 element = request.cache.objectClassElement; |
| 216 } | 236 } |
| 217 if (element is ClassElement) { | 237 if (element is ClassElement) { |
| 218 return element.accept( | 238 return element.accept( |
| 219 new ClassElementSuggestionBuilder(request, staticOnly)); | 239 new ClassElementSuggestionBuilder(request, staticOnly)); |
| 220 } | 240 } |
| 221 } | 241 } |
| 222 } | 242 } |
| 223 | 243 |
| 224 /** | 244 /** |
| 245 * Common mixin for sharing behavior |
| 246 */ |
| 247 abstract class ElementSuggestionBuilder { |
| 248 |
| 249 /** |
| 250 * Internal collection of completions to prevent duplicate completions. |
| 251 */ |
| 252 final Set<String> _completions = new Set<String>(); |
| 253 |
| 254 /** |
| 255 * Return the kind of suggestions that should be built. |
| 256 */ |
| 257 CompletionSuggestionKind get kind; |
| 258 |
| 259 /** |
| 260 * Return the request on which the builder is operating. |
| 261 */ |
| 262 DartCompletionRequest get request; |
| 263 |
| 264 /** |
| 265 * Add a suggestion based upon the given element. |
| 266 */ |
| 267 void addSuggestion(Element element) { |
| 268 if (element.isPrivate) { |
| 269 LibraryElement elementLibrary = element.library; |
| 270 LibraryElement unitLibrary = request.unit.element.library; |
| 271 if (elementLibrary != unitLibrary) { |
| 272 return; |
| 273 } |
| 274 } |
| 275 if (element.isSynthetic) { |
| 276 if (element is PropertyAccessorElement || element is FieldElement) { |
| 277 return; |
| 278 } |
| 279 } |
| 280 String completion = element.displayName; |
| 281 if (completion == null || |
| 282 completion.length <= 0 || |
| 283 !_completions.add(completion)) { |
| 284 return; |
| 285 } |
| 286 CompletionSuggestion suggestion = createSuggestion(element, kind: kind); |
| 287 if (suggestion != null) { |
| 288 request.suggestions.add(suggestion); |
| 289 } |
| 290 } |
| 291 } |
| 292 |
| 293 /** |
| 225 * This class visits elements in a library and provides suggestions based upon | 294 * This class visits elements in a library and provides suggestions based upon |
| 226 * the visible members in that library. Clients should call | 295 * the visible members in that library. Clients should call |
| 227 * [LibraryElementSuggestionBuilder.suggestionsFor]. | 296 * [LibraryElementSuggestionBuilder.suggestionsFor]. |
| 228 */ | 297 */ |
| 229 class LibraryElementSuggestionBuilder extends _AbstractSuggestionBuilder { | 298 class LibraryElementSuggestionBuilder extends GeneralizingElementVisitor with |
| 299 ElementSuggestionBuilder { |
| 300 final DartCompletionRequest request; |
| 301 final CompletionSuggestionKind kind; |
| 230 | 302 |
| 231 LibraryElementSuggestionBuilder(DartCompletionRequest request, | 303 LibraryElementSuggestionBuilder(this.request, this.kind); |
| 232 CompletionSuggestionKind kind) | |
| 233 : super(request, kind); | |
| 234 | 304 |
| 235 @override | 305 @override |
| 236 visitClassElement(ClassElement element) { | 306 visitClassElement(ClassElement element) { |
| 237 _addElementSuggestion(element, NO_RETURN_TYPE, null); | 307 addSuggestion(element); |
| 238 } | 308 } |
| 239 | 309 |
| 240 @override | 310 @override |
| 241 visitCompilationUnitElement(CompilationUnitElement element) { | 311 visitCompilationUnitElement(CompilationUnitElement element) { |
| 242 element.visitChildren(this); | 312 element.visitChildren(this); |
| 243 } | 313 } |
| 244 | 314 |
| 245 @override | 315 @override |
| 246 visitElement(Element element) { | 316 visitElement(Element element) { |
| 247 // ignored | 317 // ignored |
| 248 } | 318 } |
| 249 | 319 |
| 250 @override | 320 @override |
| 251 visitFunctionElement(FunctionElement element) { | 321 visitFunctionElement(FunctionElement element) { |
| 252 _addElementSuggestion(element, element.returnType, null); | 322 addSuggestion(element); |
| 253 } | 323 } |
| 254 | 324 |
| 255 @override | 325 @override |
| 256 visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { | 326 visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
| 257 _addElementSuggestion(element, element.returnType, null); | 327 addSuggestion(element); |
| 258 } | 328 } |
| 259 | 329 |
| 260 @override | 330 @override |
| 261 visitTopLevelVariableElement(TopLevelVariableElement element) { | 331 visitTopLevelVariableElement(TopLevelVariableElement element) { |
| 262 _addElementSuggestion(element, element.type, null); | 332 addSuggestion(element); |
| 263 } | 333 } |
| 264 | 334 |
| 265 /** | 335 /** |
| 266 * Add suggestions for the visible members in the given library | 336 * Add suggestions for the visible members in the given library |
| 267 */ | 337 */ |
| 268 static void suggestionsFor(DartCompletionRequest request, | 338 static void suggestionsFor(DartCompletionRequest request, |
| 269 CompletionSuggestionKind kind, LibraryElement library) { | 339 CompletionSuggestionKind kind, LibraryElement library) { |
| 270 if (library != null) { | 340 if (library != null) { |
| 271 library.visitChildren(new LibraryElementSuggestionBuilder(request, kind)); | 341 library.visitChildren(new LibraryElementSuggestionBuilder(request, kind)); |
| 272 } | 342 } |
| 273 } | 343 } |
| 274 } | 344 } |
| 275 | 345 |
| 276 /** | 346 /** |
| 277 * This class visits elements in a class and provides suggestions based upon | 347 * This class visits elements in a class and provides suggestions based upon |
| 278 * the visible named constructors in that class. | 348 * the visible named constructors in that class. |
| 279 */ | 349 */ |
| 280 class NamedConstructorSuggestionBuilder extends _AbstractSuggestionBuilder | 350 class NamedConstructorSuggestionBuilder extends GeneralizingElementVisitor with |
| 281 implements SuggestionBuilder { | 351 ElementSuggestionBuilder implements SuggestionBuilder { |
| 352 final DartCompletionRequest request; |
| 282 | 353 |
| 283 NamedConstructorSuggestionBuilder(DartCompletionRequest request) | 354 NamedConstructorSuggestionBuilder(this.request); |
| 284 : super(request, CompletionSuggestionKind.INVOCATION); | 355 |
| 356 @override |
| 357 CompletionSuggestionKind get kind => CompletionSuggestionKind.INVOCATION; |
| 285 | 358 |
| 286 @override | 359 @override |
| 287 bool computeFast(AstNode node) { | 360 bool computeFast(AstNode node) { |
| 288 return false; | 361 return false; |
| 289 } | 362 } |
| 290 | 363 |
| 291 @override | 364 @override |
| 292 Future<bool> computeFull(AstNode node) { | 365 Future<bool> computeFull(AstNode node) { |
| 293 if (node is SimpleIdentifier) { | 366 if (node is SimpleIdentifier) { |
| 294 node = node.parent; | 367 node = node.parent; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 308 return new Future.value(false); | 381 return new Future.value(false); |
| 309 } | 382 } |
| 310 | 383 |
| 311 @override | 384 @override |
| 312 visitClassElement(ClassElement element) { | 385 visitClassElement(ClassElement element) { |
| 313 element.visitChildren(this); | 386 element.visitChildren(this); |
| 314 } | 387 } |
| 315 | 388 |
| 316 @override | 389 @override |
| 317 visitConstructorElement(ConstructorElement element) { | 390 visitConstructorElement(ConstructorElement element) { |
| 318 _addElementSuggestion( | 391 addSuggestion(element); |
| 319 element, | |
| 320 element.returnType, | |
| 321 element.enclosingElement); | |
| 322 } | 392 } |
| 323 | 393 |
| 324 @override | 394 @override |
| 325 visitElement(Element element) { | 395 visitElement(Element element) { |
| 326 // ignored | 396 // ignored |
| 327 } | 397 } |
| 328 } | 398 } |
| 399 |
| 329 /** | 400 /** |
| 330 * Common interface implemented by suggestion builders. | 401 * Common interface implemented by suggestion builders. |
| 331 */ | 402 */ |
| 332 abstract class SuggestionBuilder { | 403 abstract class SuggestionBuilder { |
| 333 /** | 404 /** |
| 334 * Compute suggestions and return `true` if building is complete, | 405 * Compute suggestions and return `true` if building is complete, |
| 335 * or `false` if [computeFull] should be called. | 406 * or `false` if [computeFull] should be called. |
| 336 */ | 407 */ |
| 337 bool computeFast(AstNode node); | 408 bool computeFast(AstNode node); |
| 338 | 409 |
| 339 /** | 410 /** |
| 340 * Return a future that computes the suggestions given a fully resolved AST. | 411 * Return a future that computes the suggestions given a fully resolved AST. |
| 341 * The future returns `true` if suggestions were added, else `false`. | 412 * The future returns `true` if suggestions were added, else `false`. |
| 342 */ | 413 */ |
| 343 Future<bool> computeFull(AstNode node); | 414 Future<bool> computeFull(AstNode node); |
| 344 } | 415 } |
| 345 | 416 |
| 346 /** | |
| 347 * Common superclass for sharing behavior | |
| 348 */ | |
| 349 class _AbstractSuggestionBuilder extends GeneralizingElementVisitor { | |
| 350 final DartCompletionRequest request; | |
| 351 final CompletionSuggestionKind kind; | |
| 352 final Set<String> _completions = new Set<String>(); | |
| 353 | |
| 354 _AbstractSuggestionBuilder(this.request, this.kind); | |
| 355 | |
| 356 /** | |
| 357 * Add a suggestion based upon the given element. | |
| 358 */ | |
| 359 void _addElementSuggestion(Element element, DartType type, | |
| 360 ClassElement enclosingElement) { | |
| 361 if (element.isSynthetic) { | |
| 362 return; | |
| 363 } | |
| 364 if (element.isPrivate) { | |
| 365 LibraryElement elementLibrary = element.library; | |
| 366 LibraryElement unitLibrary = request.unit.element.library; | |
| 367 if (elementLibrary != unitLibrary) { | |
| 368 return; | |
| 369 } | |
| 370 } | |
| 371 String completion = element.displayName; | |
| 372 if (completion == null || | |
| 373 completion.length <= 0 || | |
| 374 !_completions.add(completion)) { | |
| 375 return; | |
| 376 } | |
| 377 bool isDeprecated = element.isDeprecated; | |
| 378 CompletionSuggestion suggestion = new CompletionSuggestion( | |
| 379 kind, | |
| 380 isDeprecated ? COMPLETION_RELEVANCE_LOW : COMPLETION_RELEVANCE_DEFAULT, | |
| 381 completion, | |
| 382 completion.length, | |
| 383 0, | |
| 384 isDeprecated, | |
| 385 false); | |
| 386 suggestion.element = protocol.newElement_fromEngine(element); | |
| 387 if (enclosingElement != null) { | |
| 388 suggestion.declaringType = enclosingElement.displayName; | |
| 389 } | |
| 390 suggestion.returnType = _nameForType(type); | |
| 391 if (element is ExecutableElement && element is! PropertyAccessorElement) { | |
| 392 suggestion.parameterNames = element.parameters.map( | |
| 393 (ParameterElement parameter) => parameter.name).toList(); | |
| 394 suggestion.parameterTypes = element.parameters.map( | |
| 395 (ParameterElement parameter) => parameter.type.displayName).toList(); | |
| 396 suggestion.requiredParameterCount = element.parameters.where( | |
| 397 (ParameterElement parameter) => | |
| 398 parameter.parameterKind == ParameterKind.REQUIRED).length; | |
| 399 suggestion.hasNamedParameters = element.parameters.any( | |
| 400 (ParameterElement parameter) => parameter.parameterKind == ParameterKi
nd.NAMED); | |
| 401 } | |
| 402 request.suggestions.add(suggestion); | |
| 403 } | |
| 404 } | |
| 405 | |
| 406 class _NoReturnType extends DartType { | 417 class _NoReturnType extends DartType { |
| 407 | 418 |
| 408 @override | 419 @override |
| 409 String get displayName => name; | 420 String get displayName => name; |
| 410 | 421 |
| 411 @override | 422 @override |
| 412 Element get element => null; | 423 Element get element => null; |
| 413 | 424 |
| 414 @override | 425 @override |
| 415 bool get isBottom => false; | 426 bool get isBottom => false; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 445 bool isSubtypeOf(DartType type) => false; | 456 bool isSubtypeOf(DartType type) => false; |
| 446 | 457 |
| 447 @override | 458 @override |
| 448 bool isSupertypeOf(DartType type) => false; | 459 bool isSupertypeOf(DartType type) => false; |
| 449 | 460 |
| 450 @override | 461 @override |
| 451 DartType substitute2(List<DartType> argumentTypes, | 462 DartType substitute2(List<DartType> argumentTypes, |
| 452 List<DartType> parameterTypes) => | 463 List<DartType> parameterTypes) => |
| 453 this; | 464 this; |
| 454 } | 465 } |
| OLD | NEW |