| // 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.computer.dart.toplevel; |
| |
| import 'dart:async'; |
| |
| import 'package:analysis_services/completion/completion_computer.dart'; |
| import 'package:analysis_services/completion/completion_suggestion.dart'; |
| import 'package:analysis_services/search/search_engine.dart'; |
| import 'package:analyzer/src/generated/ast.dart'; |
| import 'package:analyzer/src/generated/element.dart'; |
| |
| /** |
| * A computer for calculating imported class and top level variable |
| * `completion.getSuggestions` request results. |
| */ |
| class ImportedTypeComputer extends CompletionComputer { |
| |
| @override |
| bool computeFast(CompilationUnit unit, AstNode node, |
| List<CompletionSuggestion> suggestions) { |
| // TODO: implement computeFast |
| // - compute results based upon current search, then replace those results |
| // during the full compute phase |
| // - filter results based upon completion offset |
| return false; |
| } |
| |
| @override |
| Future<bool> computeFull(CompilationUnit unit, AstNode node, |
| List<CompletionSuggestion> suggestions) { |
| return node.accept( |
| new _ImportedTypeVisitor(searchEngine, unit, suggestions)); |
| } |
| } |
| |
| /** |
| * Visits the node at which the completion is requested |
| * and builds the list of suggestions. |
| */ |
| class _ImportedTypeVisitor extends GeneralizingAstVisitor<Future<bool>> { |
| final SearchEngine searchEngine; |
| final CompilationUnit unit; |
| final List<CompletionSuggestion> suggestions; |
| |
| _ImportedTypeVisitor(this.searchEngine, this.unit, this.suggestions); |
| |
| Future<bool> visitCombinator(Combinator node) { |
| var directive = node.getAncestor((parent) => parent is NamespaceDirective); |
| if (directive is NamespaceDirective) { |
| return _addLibraryElements(directive.uriElement); |
| } |
| return new Future.value(true); |
| } |
| |
| Future<bool> visitNode(AstNode node) { |
| return _addImportedElements(); |
| } |
| |
| Future<bool> visitSimpleIdentifier(SimpleIdentifier node) { |
| return node.parent.accept(this); |
| } |
| |
| Future<bool> _addImportedElements() { |
| var future = searchEngine.searchTopLevelDeclarations(''); |
| return future.then((List<SearchMatch> matches) { |
| |
| Set<LibraryElement> visibleLibs = new Set<LibraryElement>(); |
| Set<LibraryElement> excludedLibs = new Set<LibraryElement>(); |
| |
| Map<LibraryElement, Set<String>> showNames = |
| new Map<LibraryElement, Set<String>>(); |
| Map<LibraryElement, Set<String>> hideNames = |
| new Map<LibraryElement, Set<String>>(); |
| |
| // Exclude elements from the local library |
| // as they will be included by the LocalComputer |
| excludedLibs.add(unit.element.library); |
| unit.directives.forEach((Directive directive) { |
| if (directive is ImportDirective) { |
| LibraryElement lib = directive.element.importedLibrary; |
| if (directive.prefix == null) { |
| visibleLibs.add(lib); |
| directive.combinators.forEach((Combinator combinator) { |
| if (combinator is ShowCombinator) { |
| showNames[lib] = combinator.shownNames.map( |
| (SimpleIdentifier id) => id.name).toSet(); |
| } else if (combinator is HideCombinator) { |
| hideNames[lib] = combinator.hiddenNames.map( |
| (SimpleIdentifier id) => id.name).toSet(); |
| } |
| }); |
| } else { |
| excludedLibs.add(lib); |
| } |
| } |
| }); |
| |
| // Compute the set of possible classes, functions, and top level variables |
| matches.forEach((SearchMatch match) { |
| if (match.kind == MatchKind.DECLARATION) { |
| Element element = match.element; |
| LibraryElement lib = element.library; |
| if (element.isPublic && !excludedLibs.contains(lib)) { |
| String completion = element.displayName; |
| Set<String> show = showNames[lib]; |
| Set<String> hide = hideNames[lib]; |
| if ((show == null || show.contains(completion)) && |
| (hide == null || !hide.contains(completion))) { |
| suggestions.add( |
| new CompletionSuggestion( |
| CompletionSuggestionKind.fromElementKind(element.kind), |
| visibleLibs.contains(lib) || lib.isDartCore ? |
| CompletionRelevance.DEFAULT : |
| CompletionRelevance.LOW, |
| completion, |
| completion.length, |
| 0, |
| element.isDeprecated, |
| false // isPotential |
| )); |
| } |
| } |
| } |
| }); |
| return true; |
| }); |
| } |
| |
| Future<bool> _addLibraryElements(LibraryElement library) { |
| library.visitChildren(new _LibraryElementVisitor(suggestions)); |
| return new Future.value(true); |
| } |
| } |
| |
| /** |
| * Provides suggestions from a single library for the show/hide combinators |
| * as in `import "foo.dart" show ` where the completion offset is after |
| * the `show`. |
| */ |
| class _LibraryElementVisitor extends GeneralizingElementVisitor { |
| final List<CompletionSuggestion> suggestions; |
| |
| _LibraryElementVisitor(this.suggestions); |
| |
| visitClassElement(ClassElement element) { |
| _addSuggestion(element); |
| } |
| |
| visitCompilationUnitElement(CompilationUnitElement element) { |
| element.visitChildren(this); |
| } |
| |
| visitElement(Element element) { |
| // ignored |
| } |
| |
| visitFunctionTypeAliasElement(FunctionTypeAliasElement element) { |
| _addSuggestion(element); |
| } |
| |
| visitTopLevelVariableElement(TopLevelVariableElement element) { |
| _addSuggestion(element); |
| } |
| |
| void _addSuggestion(Element element) { |
| if (element != null) { |
| String completion = element.name; |
| if (completion != null && completion.length > 0) { |
| suggestions.add( |
| new CompletionSuggestion( |
| CompletionSuggestionKind.fromElementKind(element.kind), |
| CompletionRelevance.DEFAULT, |
| completion, |
| completion.length, |
| 0, |
| element.isDeprecated, |
| false // isPotential |
| )); |
| } |
| } |
| } |
| } |