| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library services.completion.contributor.dart.invocation; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 | |
| 9 import 'package:analysis_server/src/services/completion/dart_completion_manager.
dart'; | |
| 10 import 'package:analysis_server/src/services/completion/local_declaration_visito
r.dart'; | |
| 11 import 'package:analysis_server/src/services/completion/local_suggestion_builder
.dart' | |
| 12 hide createSuggestion; | |
| 13 import 'package:analysis_server/src/services/completion/optype.dart'; | |
| 14 import 'package:analysis_server/src/services/completion/suggestion_builder.dart'
; | |
| 15 import 'package:analysis_server/src/services/completion/suggestion_builder.dart' | |
| 16 show createSuggestion; | |
| 17 import 'package:analyzer/src/generated/ast.dart'; | |
| 18 import 'package:analyzer/src/generated/element.dart'; | |
| 19 | |
| 20 import '../../protocol_server.dart' | |
| 21 show CompletionSuggestion, CompletionSuggestionKind; | |
| 22 | |
| 23 /** | |
| 24 * A contributor for calculating invocation / access suggestions | |
| 25 * `completion.getSuggestions` request results. | |
| 26 */ | |
| 27 class PrefixedElementContributor extends DartCompletionContributor { | |
| 28 SuggestionBuilder builder; | |
| 29 | |
| 30 @override | |
| 31 bool computeFast(DartCompletionRequest request) { | |
| 32 OpType optype = request.optype; | |
| 33 if (optype.isPrefixed) { | |
| 34 builder = request.target.containingNode | |
| 35 .accept(new _InvocationAstVisitor(request)); | |
| 36 if (builder != null) { | |
| 37 return builder.computeFast(request.target.containingNode); | |
| 38 } | |
| 39 } | |
| 40 | |
| 41 return true; | |
| 42 } | |
| 43 | |
| 44 @override | |
| 45 Future<bool> computeFull(DartCompletionRequest request) { | |
| 46 if (builder != null) { | |
| 47 return builder.computeFull(request.target.containingNode); | |
| 48 } | |
| 49 return new Future.value(false); | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 class _ExpressionSuggestionBuilder implements SuggestionBuilder { | |
| 54 final DartCompletionRequest request; | |
| 55 | |
| 56 _ExpressionSuggestionBuilder(this.request); | |
| 57 | |
| 58 @override | |
| 59 bool computeFast(AstNode node) { | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 63 @override | |
| 64 Future<bool> computeFull(AstNode node) { | |
| 65 if (node is MethodInvocation) { | |
| 66 node = (node as MethodInvocation).realTarget; | |
| 67 } else if (node is PropertyAccess) { | |
| 68 node = (node as PropertyAccess).realTarget; | |
| 69 } | |
| 70 if (node is Identifier) { | |
| 71 Element elem = node.bestElement; | |
| 72 if (elem is ClassElement || elem is PrefixElement) { | |
| 73 elem.accept(new _PrefixedIdentifierSuggestionBuilder(request)); | |
| 74 return new Future.value(true); | |
| 75 } | |
| 76 } | |
| 77 if (node is Expression) { | |
| 78 String containingMethodName; | |
| 79 bool isSuper = node is SuperExpression; | |
| 80 if (isSuper) { | |
| 81 MethodDeclaration containingMethod = | |
| 82 node.getAncestor((p) => p is MethodDeclaration); | |
| 83 if (containingMethod != null) { | |
| 84 SimpleIdentifier id = containingMethod.name; | |
| 85 if (id != null) { | |
| 86 containingMethodName = id.name; | |
| 87 } | |
| 88 } | |
| 89 } | |
| 90 InterfaceTypeSuggestionBuilder.suggestionsFor(request, node.bestType, | |
| 91 isSuper: isSuper, containingMethodName: containingMethodName); | |
| 92 return new Future.value(true); | |
| 93 } | |
| 94 return new Future.value(false); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 /** | |
| 99 * An [AstNode] vistor for determining which suggestion builder | |
| 100 * should be used to build invocation/access suggestions. | |
| 101 */ | |
| 102 class _InvocationAstVisitor extends GeneralizingAstVisitor<SuggestionBuilder> { | |
| 103 final DartCompletionRequest request; | |
| 104 | |
| 105 _InvocationAstVisitor(this.request); | |
| 106 | |
| 107 @override | |
| 108 SuggestionBuilder visitConstructorName(ConstructorName node) { | |
| 109 // some PrefixedIdentifier nodes are transformed into | |
| 110 // ConstructorName nodes during the resolution process. | |
| 111 return new _PrefixedIdentifierSuggestionBuilder(request); | |
| 112 } | |
| 113 | |
| 114 @override | |
| 115 SuggestionBuilder visitMethodInvocation(MethodInvocation node) { | |
| 116 return new _ExpressionSuggestionBuilder(request); | |
| 117 } | |
| 118 | |
| 119 @override | |
| 120 SuggestionBuilder visitNode(AstNode node) { | |
| 121 return null; | |
| 122 } | |
| 123 | |
| 124 @override | |
| 125 SuggestionBuilder visitPrefixedIdentifier(PrefixedIdentifier node) { | |
| 126 // some PrefixedIdentifier nodes are transformed into | |
| 127 // ConstructorName nodes during the resolution process. | |
| 128 return new _PrefixedIdentifierSuggestionBuilder(request); | |
| 129 } | |
| 130 | |
| 131 @override | |
| 132 SuggestionBuilder visitPropertyAccess(PropertyAccess node) { | |
| 133 return new _ExpressionSuggestionBuilder(request); | |
| 134 } | |
| 135 } | |
| 136 | |
| 137 /** | |
| 138 * An [AstVisitor] which looks for a declaration with the given name | |
| 139 * and if found, tries to determine a type for that declaration. | |
| 140 */ | |
| 141 class _LocalBestTypeVisitor extends LocalDeclarationVisitor { | |
| 142 /** | |
| 143 * The name for the declaration to be found. | |
| 144 */ | |
| 145 final String targetName; | |
| 146 | |
| 147 /** | |
| 148 * The best type for the found declaration, | |
| 149 * or `null` if no declaration found or failed to determine a type. | |
| 150 */ | |
| 151 DartType typeFound; | |
| 152 | |
| 153 /** | |
| 154 * Construct a new instance to search for a declaration | |
| 155 */ | |
| 156 _LocalBestTypeVisitor(this.targetName, int offset) : super(offset); | |
| 157 | |
| 158 @override | |
| 159 void declaredClass(ClassDeclaration declaration) { | |
| 160 if (declaration.name.name == targetName) { | |
| 161 // no type | |
| 162 finished(); | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 @override | |
| 167 void declaredClassTypeAlias(ClassTypeAlias declaration) { | |
| 168 if (declaration.name.name == targetName) { | |
| 169 // no type | |
| 170 finished(); | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 @override | |
| 175 void declaredField(FieldDeclaration fieldDecl, VariableDeclaration varDecl) { | |
| 176 if (varDecl.name.name == targetName) { | |
| 177 // Type provided by the element in computeFull above | |
| 178 finished(); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 @override | |
| 183 void declaredFunction(FunctionDeclaration declaration) { | |
| 184 if (declaration.name.name == targetName) { | |
| 185 TypeName typeName = declaration.returnType; | |
| 186 if (typeName != null) { | |
| 187 typeFound = typeName.type; | |
| 188 } | |
| 189 finished(); | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 @override | |
| 194 void declaredFunctionTypeAlias(FunctionTypeAlias declaration) { | |
| 195 if (declaration.name.name == targetName) { | |
| 196 TypeName typeName = declaration.returnType; | |
| 197 if (typeName != null) { | |
| 198 typeFound = typeName.type; | |
| 199 } | |
| 200 finished(); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 @override | |
| 205 void declaredLabel(Label label, bool isCaseLabel) { | |
| 206 if (label.label.name == targetName) { | |
| 207 // no type | |
| 208 finished(); | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 @override | |
| 213 void declaredLocalVar(SimpleIdentifier name, TypeName type) { | |
| 214 if (name.name == targetName) { | |
| 215 typeFound = name.bestType; | |
| 216 finished(); | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 @override | |
| 221 void declaredMethod(MethodDeclaration declaration) { | |
| 222 if (declaration.name.name == targetName) { | |
| 223 TypeName typeName = declaration.returnType; | |
| 224 if (typeName != null) { | |
| 225 typeFound = typeName.type; | |
| 226 } | |
| 227 finished(); | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 @override | |
| 232 void declaredParam(SimpleIdentifier name, TypeName type) { | |
| 233 if (name.name == targetName) { | |
| 234 // Type provided by the element in computeFull above | |
| 235 finished(); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 @override | |
| 240 void declaredTopLevelVar( | |
| 241 VariableDeclarationList varList, VariableDeclaration varDecl) { | |
| 242 if (varDecl.name.name == targetName) { | |
| 243 // Type provided by the element in computeFull above | |
| 244 finished(); | |
| 245 } | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 /** | |
| 250 * An [Element] visitor for determining the appropriate invocation/access | |
| 251 * suggestions based upon the element for which the completion is requested. | |
| 252 */ | |
| 253 class _PrefixedIdentifierSuggestionBuilder | |
| 254 extends GeneralizingElementVisitor<Future<bool>> | |
| 255 implements SuggestionBuilder { | |
| 256 final DartCompletionRequest request; | |
| 257 | |
| 258 _PrefixedIdentifierSuggestionBuilder(this.request); | |
| 259 | |
| 260 @override | |
| 261 bool computeFast(AstNode node) { | |
| 262 return false; | |
| 263 } | |
| 264 | |
| 265 @override | |
| 266 Future<bool> computeFull(AstNode node) { | |
| 267 if (node is PrefixedIdentifier) { | |
| 268 SimpleIdentifier prefix = node.prefix; | |
| 269 if (prefix != null) { | |
| 270 Element element = prefix.bestElement; | |
| 271 DartType type = prefix.bestType; | |
| 272 if (element is! ClassElement) { | |
| 273 if (type == null || type.isDynamic) { | |
| 274 // | |
| 275 // Given `g. int y = 0;`, the parser interprets `g` as a prefixed | |
| 276 // identifier with no type. | |
| 277 // If the user is requesting completions for `g`, | |
| 278 // then check for a function, getter, or similar with a type. | |
| 279 // | |
| 280 _LocalBestTypeVisitor visitor = | |
| 281 new _LocalBestTypeVisitor(prefix.name, request.offset); | |
| 282 if (visitor.visit(prefix)) { | |
| 283 type = visitor.typeFound; | |
| 284 } | |
| 285 } | |
| 286 if (type != null && !type.isDynamic) { | |
| 287 InterfaceTypeSuggestionBuilder.suggestionsFor(request, type); | |
| 288 return new Future.value(true); | |
| 289 } | |
| 290 } | |
| 291 if (element != null) { | |
| 292 return element.accept(this); | |
| 293 } | |
| 294 } | |
| 295 } | |
| 296 return new Future.value(false); | |
| 297 } | |
| 298 | |
| 299 @override | |
| 300 Future<bool> visitClassElement(ClassElement element) { | |
| 301 if (element != null) { | |
| 302 InterfaceType type = element.type; | |
| 303 if (type != null) { | |
| 304 // Suggested by StaticMemberContributor | |
| 305 // StaticClassElementSuggestionBuilder.suggestionsFor( | |
| 306 // request, type.element); | |
| 307 } | |
| 308 } | |
| 309 return new Future.value(false); | |
| 310 } | |
| 311 | |
| 312 @override | |
| 313 Future<bool> visitElement(Element element) { | |
| 314 return new Future.value(false); | |
| 315 } | |
| 316 | |
| 317 @override | |
| 318 Future<bool> visitPrefixElement(PrefixElement element) { | |
| 319 bool modified = false; | |
| 320 | |
| 321 // Suggested by LibraryMemberContributor | |
| 322 | |
| 323 // Find the import directive with the given prefix | |
| 324 // for (Directive directive in request.unit.directives) { | |
| 325 // if (directive is ImportDirective) { | |
| 326 // if (directive.prefix != null) { | |
| 327 // if (directive.prefix.name == element.name) { | |
| 328 // // Suggest elements from the imported library | |
| 329 // LibraryElement library = directive.uriElement; | |
| 330 // AstNode node = request.target.containingNode; | |
| 331 // bool typesOnly = node.parent is TypeName; | |
| 332 // bool instCreation = | |
| 333 // typesOnly && node.parent.parent is ConstructorName; | |
| 334 // LibraryElementSuggestionBuilder.suggestionsFor( | |
| 335 // request, | |
| 336 // CompletionSuggestionKind.INVOCATION, | |
| 337 // library, | |
| 338 // typesOnly, | |
| 339 // instCreation); | |
| 340 // modified = true; | |
| 341 // if (directive.deferredKeyword != null) { | |
| 342 // FunctionElement loadLibFunct = library.loadLibraryFunction; | |
| 343 // request.addSuggestion(createSuggestion(loadLibFunct)); | |
| 344 // } | |
| 345 // } | |
| 346 // } | |
| 347 // } | |
| 348 // } | |
| 349 return new Future.value(modified); | |
| 350 } | |
| 351 | |
| 352 @override | |
| 353 Future<bool> visitPropertyAccessorElement(PropertyAccessorElement element) { | |
| 354 if (element != null) { | |
| 355 PropertyInducingElement elemVar = element.variable; | |
| 356 if (elemVar != null) { | |
| 357 InterfaceTypeSuggestionBuilder.suggestionsFor(request, elemVar.type); | |
| 358 } | |
| 359 return new Future.value(true); | |
| 360 } | |
| 361 return new Future.value(false); | |
| 362 } | |
| 363 | |
| 364 @override | |
| 365 Future<bool> visitVariableElement(VariableElement element) { | |
| 366 InterfaceTypeSuggestionBuilder.suggestionsFor(request, element.type); | |
| 367 return new Future.value(true); | |
| 368 } | |
| 369 } | |
| OLD | NEW |