Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(370)

Side by Side Diff: pkg/analysis_server/lib/src/services/completion/suggestion_builder.dart

Issue 1504223005: extract TypeMemberContributor from prefixed element contributor (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: mege Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 import 'dart:collection';
9 8
10 import 'package:analysis_server/src/protocol_server.dart' 9 import 'package:analysis_server/src/protocol_server.dart'
11 hide Element, ElementKind; 10 hide Element, ElementKind;
12 import 'package:analysis_server/src/services/completion/dart/suggestion_builder. dart' 11 import 'package:analysis_server/src/services/completion/dart/suggestion_builder. dart'
13 show createSuggestion; 12 show createSuggestion;
14 import 'package:analysis_server/src/services/completion/dart_completion_manager. dart'; 13 import 'package:analysis_server/src/services/completion/dart_completion_manager. dart';
15 import 'package:analyzer/src/generated/ast.dart'; 14 import 'package:analyzer/src/generated/ast.dart';
16 import 'package:analyzer/src/generated/element.dart'; 15 import 'package:analyzer/src/generated/element.dart';
17 import 'package:analyzer/src/generated/engine.dart' as engine;
18 16
19 export 'package:analysis_server/src/services/completion/dart/suggestion_builder. dart' 17 export 'package:analysis_server/src/services/completion/dart/suggestion_builder. dart'
20 show createSuggestion; 18 show createSuggestion;
21 19
22 const String DYNAMIC = 'dynamic'; 20 const String DYNAMIC = 'dynamic';
23 21
24 /** 22 /**
25 * Call the given function with each non-null non-empty inherited type name 23 * Call the given function with each non-null non-empty inherited type name
26 * that is defined in the given class. 24 * that is defined in the given class.
27 */ 25 */
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 if (parent is ClassElement && parent.isEnum) { 165 if (parent is ClassElement && parent.isEnum) {
168 if (element.name == 'values') { 166 if (element.name == 'values') {
169 return true; 167 return true;
170 } 168 }
171 } 169 }
172 return false; 170 return false;
173 } 171 }
174 } 172 }
175 173
176 /** 174 /**
177 * This class provides suggestions based upon the visible instance members in
178 * an interface type. Clients should call
179 * [InterfaceTypeSuggestionBuilder.suggestionsFor].
180 */
181 class InterfaceTypeSuggestionBuilder {
182 /**
183 * Enumerated value indicating that we have not generated any completions for
184 * a given identifier yet.
185 */
186 static const int _COMPLETION_TYPE_NONE = 0;
187
188 /**
189 * Enumerated value indicating that we have generated a completion for a
190 * getter.
191 */
192 static const int _COMPLETION_TYPE_GETTER = 1;
193
194 /**
195 * Enumerated value indicating that we have generated a completion for a
196 * setter.
197 */
198 static const int _COMPLETION_TYPE_SETTER = 2;
199
200 /**
201 * Enumerated value indicating that we have generated a completion for a
202 * field, a method, or a getter/setter pair.
203 */
204 static const int _COMPLETION_TYPE_FIELD_OR_METHOD_OR_GETSET = 3;
205
206 final DartCompletionRequest request;
207
208 /**
209 * Map indicating, for each possible completion identifier, whether we have
210 * already generated completions for a getter, setter, or both. The "both"
211 * case also handles the case where have generated a completion for a method
212 * or a field.
213 *
214 * Note: the enumerated values stored in this map are intended to be bitwise
215 * compared.
216 */
217 Map<String, int> _completionTypesGenerated = new HashMap<String, int>();
218
219 InterfaceTypeSuggestionBuilder(this.request);
220
221 CompletionSuggestionKind get kind => CompletionSuggestionKind.INVOCATION;
222
223 /**
224 * Add a suggestion based upon the given element, provided that it is not
225 * shadowed by a previously added suggestion.
226 */
227 void addSuggestion(Element element, {int relevance: DART_RELEVANCE_DEFAULT}) {
228 if (element.isPrivate) {
229 LibraryElement elementLibrary = element.library;
230 LibraryElement unitLibrary = request.unit.element.library;
231 if (elementLibrary != unitLibrary) {
232 return;
233 }
234 }
235 String identifier = element.displayName;
236 int alreadyGenerated = _completionTypesGenerated.putIfAbsent(
237 identifier, () => _COMPLETION_TYPE_NONE);
238 if (element is MethodElement) {
239 // Anything shadows a method.
240 if (alreadyGenerated != _COMPLETION_TYPE_NONE) {
241 return;
242 }
243 _completionTypesGenerated[identifier] =
244 _COMPLETION_TYPE_FIELD_OR_METHOD_OR_GETSET;
245 } else if (element is PropertyAccessorElement) {
246 if (element.isGetter) {
247 // Getters, fields, and methods shadow a getter.
248 if ((alreadyGenerated & _COMPLETION_TYPE_GETTER) != 0) {
249 return;
250 }
251 _completionTypesGenerated[identifier] |= _COMPLETION_TYPE_GETTER;
252 } else {
253 // Setters, fields, and methods shadow a setter.
254 if ((alreadyGenerated & _COMPLETION_TYPE_SETTER) != 0) {
255 return;
256 }
257 _completionTypesGenerated[identifier] |= _COMPLETION_TYPE_SETTER;
258 }
259 } else if (element is FieldElement) {
260 // Fields and methods shadow a field. A getter/setter pair shadows a
261 // field, but a getter or setter by itself doesn't.
262 if (alreadyGenerated == _COMPLETION_TYPE_FIELD_OR_METHOD_OR_GETSET) {
263 return;
264 }
265 _completionTypesGenerated[identifier] =
266 _COMPLETION_TYPE_FIELD_OR_METHOD_OR_GETSET;
267 } else {
268 // Unexpected element type; skip it.
269 assert(false);
270 return;
271 }
272 CompletionSuggestion suggestion =
273 createSuggestion(element, kind: kind, relevance: relevance);
274 if (suggestion != null) {
275 request.addSuggestion(suggestion);
276 }
277 }
278
279 void _buildSuggestions(InterfaceType type, LibraryElement library,
280 bool isSuper, String containingMethodName) {
281 if (isSuper) {
282 // Suggest members from superclass if the target is "super"
283 type = type.superclass;
284 if (type == null) {
285 return;
286 }
287 }
288 // Visit all of the types in the class hierarchy, collecting possible
289 // completions. If multiple elements are found that complete to the same
290 // identifier, addSuggestion will discard all but the first (with a few
291 // exceptions to handle getter/setter pairs).
292 List<InterfaceType> types = _getTypeOrdering(type);
293 for (InterfaceType targetType in types) {
294 for (MethodElement method in targetType.methods) {
295 // Exclude static methods when completion on an instance
296 if (!method.isStatic) {
297 addSuggestion(method,
298 relevance: method.name == containingMethodName
299 ? DART_RELEVANCE_HIGH
300 : DART_RELEVANCE_DEFAULT);
301 }
302 }
303 for (PropertyAccessorElement propertyAccessor in targetType.accessors) {
304 if (!propertyAccessor.isStatic) {
305 if (propertyAccessor.isSynthetic) {
306 // Avoid visiting a field twice
307 if (propertyAccessor.isGetter) {
308 addSuggestion(propertyAccessor.variable);
309 }
310 } else {
311 addSuggestion(propertyAccessor);
312 }
313 }
314 }
315 }
316 }
317
318 /**
319 * Get a list of [InterfaceType]s that should be searched to find the
320 * possible completions for an object having type [type].
321 */
322 List<InterfaceType> _getTypeOrdering(InterfaceType type) {
323 // Candidate completions can come from [type] as well as any types above it
324 // in the class hierarchy (including mixins, superclasses, and interfaces).
325 // If a given completion identifier shows up in multiple types, we should
326 // use the element that is nearest in the superclass chain, so we will
327 // visit [type] first, then its mixins, then its superclass, then its
328 // superclass's mixins, etc., and only afterwards visit interfaces.
329 //
330 // We short-circuit loops in the class hierarchy by keeping track of the
331 // classes seen (not the interfaces) so that we won't be fooled by nonsense
332 // like "class C<T> extends C<List<T>> {}"
333 List<InterfaceType> result = <InterfaceType>[];
334 Set<ClassElement> classesSeen = new HashSet<ClassElement>();
335 List<InterfaceType> typesToVisit = <InterfaceType>[type];
336 while (typesToVisit.isNotEmpty) {
337 InterfaceType nextType = typesToVisit.removeLast();
338 if (!classesSeen.add(nextType.element)) {
339 // Class had already been seen, so ignore this type.
340 continue;
341 }
342 result.add(nextType);
343 // typesToVisit is a stack, so push on the interfaces first, then the
344 // superclass, then the mixins. This will ensure that they are visited
345 // in the reverse order.
346 typesToVisit.addAll(nextType.interfaces);
347 if (nextType.superclass != null) {
348 typesToVisit.add(nextType.superclass);
349 }
350 typesToVisit.addAll(nextType.mixins);
351 }
352 return result;
353 }
354
355 /**
356 * Add suggestions for the visible members in the given interface
357 */
358 static void suggestionsFor(DartCompletionRequest request, DartType type,
359 {bool isSuper: false, String containingMethodName: null}) {
360 CompilationUnit compilationUnit =
361 request.target.containingNode.getAncestor((n) => n is CompilationUnit);
362 CompilationUnitElement unitElem = compilationUnit.element;
363 if (unitElem == null) {
364 engine.AnalysisEngine.instance.logger
365 .logInformation('Completion expected resolved AST');
366 return;
367 }
368 LibraryElement library = unitElem.library;
369 if (type is DynamicTypeImpl) {
370 type = request.cache.objectClassElement.type;
371 }
372 if (type is InterfaceType) {
373 new InterfaceTypeSuggestionBuilder(request)
374 ._buildSuggestions(type, library, isSuper, containingMethodName);
375 }
376 }
377 }
378
379 /**
380 * This class visits elements in a library and provides suggestions based upon
381 * the visible members in that library. Clients should call
382 * [LibraryElementSuggestionBuilder.suggestionsFor].
383 */
384 class LibraryElementSuggestionBuilder extends GeneralizingElementVisitor
385 with ElementSuggestionBuilder {
386 final DartCompletionRequest request;
387 final CompletionSuggestionKind kind;
388 final bool typesOnly;
389 final bool instCreation;
390
391 LibraryElementSuggestionBuilder(
392 this.request, this.kind, this.typesOnly, this.instCreation);
393
394 @override
395 visitClassElement(ClassElement element) {
396 if (instCreation) {
397 element.visitChildren(this);
398 } else {
399 addSuggestion(element);
400 }
401 }
402
403 @override
404 visitCompilationUnitElement(CompilationUnitElement element) {
405 element.visitChildren(this);
406 LibraryElement containingLibrary = element.library;
407 if (containingLibrary != null) {
408 for (var lib in containingLibrary.exportedLibraries) {
409 lib.visitChildren(this);
410 }
411 }
412 }
413
414 @override
415 visitConstructorElement(ConstructorElement element) {
416 if (instCreation) {
417 ClassElement classElem = element.enclosingElement;
418 if (classElem != null) {
419 String prefix = classElem.name;
420 if (prefix != null && prefix.length > 0) {
421 addSuggestion(element, prefix: prefix);
422 }
423 }
424 }
425 }
426
427 @override
428 visitElement(Element element) {
429 // ignored
430 }
431
432 @override
433 visitFunctionElement(FunctionElement element) {
434 if (!typesOnly) {
435 addSuggestion(element);
436 }
437 }
438
439 @override
440 visitFunctionTypeAliasElement(FunctionTypeAliasElement element) {
441 if (!instCreation) {
442 addSuggestion(element);
443 }
444 }
445
446 @override
447 visitTopLevelVariableElement(TopLevelVariableElement element) {
448 if (!typesOnly) {
449 addSuggestion(element);
450 }
451 }
452
453 /**
454 * Add suggestions for the visible members in the given library
455 */
456 static void suggestionsFor(
457 DartCompletionRequest request,
458 CompletionSuggestionKind kind,
459 LibraryElement library,
460 bool typesOnly,
461 bool instCreation) {
462 if (library != null) {
463 library.visitChildren(new LibraryElementSuggestionBuilder(
464 request, kind, typesOnly, instCreation));
465 }
466 }
467 }
468
469 /**
470 * This class visits elements in a class and provides suggestions based upon
471 * the visible static members in that class. Clients should call
472 * [StaticClassElementSuggestionBuilder.suggestionsFor].
473 */
474 class StaticClassElementSuggestionBuilder extends GeneralizingElementVisitor
475 with ElementSuggestionBuilder {
476 final DartCompletionRequest request;
477
478 StaticClassElementSuggestionBuilder(this.request);
479
480 @override
481 CompletionSuggestionKind get kind => CompletionSuggestionKind.INVOCATION;
482
483 @override
484 visitClassElement(ClassElement element) {
485 element.visitChildren(this);
486 element.allSupertypes.forEach((InterfaceType type) {
487 type.element.visitChildren(this);
488 });
489 }
490
491 @override
492 visitElement(Element element) {
493 // ignored
494 }
495
496 @override
497 visitFieldElement(FieldElement element) {
498 if (!element.isStatic) {
499 return;
500 }
501 addSuggestion(element);
502 }
503
504 @override
505 visitMethodElement(MethodElement element) {
506 if (!element.isStatic) {
507 return;
508 }
509 if (element.isOperator) {
510 return;
511 }
512 addSuggestion(element);
513 }
514
515 @override
516 visitPropertyAccessorElement(PropertyAccessorElement element) {
517 if (!element.isStatic) {
518 return;
519 }
520 addSuggestion(element);
521 }
522
523 /**
524 * Add suggestions for the visible members in the given class
525 */
526 static void suggestionsFor(DartCompletionRequest request, Element element) {
527 if (element == DynamicElementImpl.instance) {
528 element = request.cache.objectClassElement;
529 }
530 if (element is ClassElement) {
531 return element.accept(new StaticClassElementSuggestionBuilder(request));
532 }
533 }
534 }
535
536 /**
537 * Common interface implemented by suggestion builders. 175 * Common interface implemented by suggestion builders.
538 */ 176 */
539 abstract class SuggestionBuilder { 177 abstract class SuggestionBuilder {
540 /** 178 /**
541 * Compute suggestions and return `true` if building is complete, 179 * Compute suggestions and return `true` if building is complete,
542 * or `false` if [computeFull] should be called. 180 * or `false` if [computeFull] should be called.
543 */ 181 */
544 bool computeFast(AstNode node); 182 bool computeFast(AstNode node);
545 183
546 /** 184 /**
547 * Return a future that computes the suggestions given a fully resolved AST. 185 * Return a future that computes the suggestions given a fully resolved AST.
548 * The future returns `true` if suggestions were added, else `false`. 186 * The future returns `true` if suggestions were added, else `false`.
549 */ 187 */
550 Future<bool> computeFull(AstNode node); 188 Future<bool> computeFull(AstNode node);
551 } 189 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698