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 |