| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library services.completion.contributor.dart.local_lib; |
| |
| import 'dart:async'; |
| |
| import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart'; |
| import 'package:analysis_server/src/services/completion/dart/completion_manager.dart'; |
| import 'package:analysis_server/src/services/completion/dart/optype.dart'; |
| import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart' |
| show createSuggestion, ElementSuggestionBuilder; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/dart/element/visitor.dart'; |
| |
| import '../../../protocol_server.dart' |
| show CompletionSuggestion, CompletionSuggestionKind; |
| |
| /** |
| * A visitor for building suggestions based upon the elements defined by |
| * a source file contained in the same library but not the same as |
| * the source in which the completions are being requested. |
| */ |
| class LibraryElementSuggestionBuilder extends GeneralizingElementVisitor |
| with ElementSuggestionBuilder { |
| final DartCompletionRequest request; |
| final OpType optype; |
| CompletionSuggestionKind kind; |
| final String prefix; |
| List<String> showNames; |
| List<String> hiddenNames; |
| |
| LibraryElementSuggestionBuilder(this.request, this.optype, [this.prefix]) { |
| this.kind = request.target.isFunctionalArgument() |
| ? CompletionSuggestionKind.IDENTIFIER |
| : optype.suggestKind; |
| } |
| |
| @override |
| LibraryElement get containingLibrary => request.libraryElement; |
| |
| @override |
| void visitClassElement(ClassElement element) { |
| if (optype.includeTypeNameSuggestions) { |
| // if includeTypeNameSuggestions, then use the filter |
| int relevance = optype.typeNameSuggestionsFilter( |
| element.type, DART_RELEVANCE_DEFAULT); |
| if (relevance != null) { |
| addSuggestion(element, request.ideOptions, |
| prefix: prefix, relevance: relevance); |
| } |
| } |
| if (optype.includeConstructorSuggestions) { |
| int relevance = optype.constructorSuggestionsFilter( |
| element.type, DART_RELEVANCE_DEFAULT); |
| if (relevance != null) { |
| _addConstructorSuggestions(element, relevance); |
| } |
| } |
| } |
| |
| @override |
| void visitCompilationUnitElement(CompilationUnitElement element) { |
| element.visitChildren(this); |
| } |
| |
| @override |
| void visitElement(Element element) { |
| // ignored |
| } |
| |
| @override |
| void visitFunctionElement(FunctionElement element) { |
| // Do not suggest operators or local functions |
| if (element.isOperator) { |
| return; |
| } |
| if (element.enclosingElement is! CompilationUnitElement) { |
| return; |
| } |
| int relevance = element.library == containingLibrary |
| ? DART_RELEVANCE_LOCAL_FUNCTION |
| : DART_RELEVANCE_DEFAULT; |
| DartType returnType = element.returnType; |
| if (returnType != null && returnType.isVoid) { |
| if (optype.includeVoidReturnSuggestions) { |
| addSuggestion(element, request.ideOptions, |
| prefix: prefix, relevance: relevance); |
| } |
| } else { |
| if (optype.includeReturnValueSuggestions) { |
| addSuggestion(element, request.ideOptions, |
| prefix: prefix, relevance: relevance); |
| } |
| } |
| } |
| |
| @override |
| void visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
| if (optype.includeTypeNameSuggestions) { |
| int relevance = element.library == containingLibrary |
| ? DART_RELEVANCE_LOCAL_FUNCTION |
| : DART_RELEVANCE_DEFAULT; |
| addSuggestion(element, request.ideOptions, |
| prefix: prefix, relevance: relevance); |
| } |
| } |
| |
| @override |
| void visitLibraryElement(LibraryElement element) { |
| element.visitChildren(this); |
| } |
| |
| @override |
| void visitPropertyAccessorElement(PropertyAccessorElement element) { |
| if (optype.includeReturnValueSuggestions) { |
| int relevance; |
| if (element.library == containingLibrary) { |
| if (element.enclosingElement is ClassElement) { |
| relevance = DART_RELEVANCE_LOCAL_FIELD; |
| } else { |
| relevance = DART_RELEVANCE_LOCAL_TOP_LEVEL_VARIABLE; |
| } |
| } else { |
| relevance = DART_RELEVANCE_DEFAULT; |
| } |
| addSuggestion(element, request.ideOptions, |
| prefix: prefix, relevance: relevance); |
| } |
| } |
| |
| @override |
| void visitTopLevelVariableElement(TopLevelVariableElement element) { |
| if (optype.includeReturnValueSuggestions) { |
| int relevance = element.library == containingLibrary |
| ? DART_RELEVANCE_LOCAL_TOP_LEVEL_VARIABLE |
| : DART_RELEVANCE_DEFAULT; |
| addSuggestion(element, request.ideOptions, |
| prefix: prefix, relevance: relevance); |
| } |
| } |
| |
| /** |
| * Add constructor suggestions for the given class. |
| */ |
| void _addConstructorSuggestions(ClassElement classElem, int relevance) { |
| String className = classElem.name; |
| for (ConstructorElement constructor in classElem.constructors) { |
| if (!constructor.isPrivate) { |
| CompletionSuggestion suggestion = createSuggestion( |
| constructor, request.ideOptions, |
| relevance: relevance); |
| if (suggestion != null) { |
| String name = suggestion.completion; |
| name = name.length > 0 ? '$className.$name' : className; |
| if (prefix != null && prefix.length > 0) { |
| name = '$prefix.$name'; |
| } |
| suggestion.completion = name; |
| suggestion.selectionOffset = suggestion.completion.length; |
| suggestions.add(suggestion); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * A contributor for calculating suggestions for top level members |
| * in the library in which the completion is requested |
| * but outside the file in which the completion is requested. |
| */ |
| class LocalLibraryContributor extends DartCompletionContributor { |
| @override |
| Future<List<CompletionSuggestion>> computeSuggestions( |
| DartCompletionRequest request) async { |
| if (!request.includeIdentifiers) { |
| return EMPTY_LIST; |
| } |
| |
| List<CompilationUnitElement> libraryUnits = await request.resolveUnits(); |
| if (libraryUnits == null) { |
| return EMPTY_LIST; |
| } |
| |
| AstNode node = request.target.containingNode; |
| |
| // If the target is in an expression |
| // then resolve the outermost/entire expression |
| await request.resolveContainingExpression(node); |
| |
| // Discard any cached target information |
| // because it may have changed as a result of the resolution |
| node = request.target.containingNode; |
| |
| OpType optype = (request as DartCompletionRequestImpl).opType; |
| LibraryElementSuggestionBuilder visitor = |
| new LibraryElementSuggestionBuilder(request, optype); |
| for (CompilationUnitElement unit in libraryUnits) { |
| if (unit != null && unit.source != request.source) { |
| unit.accept(visitor); |
| } |
| } |
| return visitor.suggestions; |
| } |
| } |