OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, 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.computer.dart.invocation; |
| 6 |
| 7 import 'dart:async'; |
| 8 |
| 9 import 'package:analysis_server/src/protocol_server.dart' |
| 10 show CompletionSuggestion, CompletionSuggestionKind, SourceChange; |
| 11 import 'package:analysis_server/src/protocol_server.dart' as protocol |
| 12 hide CompletionSuggestion, CompletionSuggestionKind; |
| 13 import 'package:analysis_server/src/provisional/completion/completion_core.dart'
; |
| 14 import 'package:analysis_server/src/provisional/completion/completion_dart.dart'
; |
| 15 import 'package:analysis_server/src/provisional/completion/dart/completion_targe
t.dart'; |
| 16 import 'package:analysis_server/src/services/completion/dart_completion_manager.
dart' |
| 17 show DART_RELEVANCE_HIGH; |
| 18 import 'package:analyzer/src/generated/ast.dart'; |
| 19 import 'package:analyzer/src/generated/element.dart'; |
| 20 import 'package:analyzer/src/generated/resolver.dart'; |
| 21 import 'package:analyzer/src/generated/source.dart'; |
| 22 // import 'package:analysis_server/plugin/edit/utilities/change_builder_dart.dar
t'; |
| 23 // import 'package:analyzer/src/generated/engine.dart'; |
| 24 |
| 25 /** |
| 26 * A completion contributor used to suggest replacing partial identifiers inside |
| 27 * a class declaration with templates for inherited members. |
| 28 */ |
| 29 class InheritedContributor implements DartCompletionContributor { |
| 30 @override |
| 31 Future<List<CompletionSuggestion>> computeSuggestions( |
| 32 DartCompletionRequest request) async { |
| 33 // Determine if the target looks like a partial identifier |
| 34 // inside a class declaration |
| 35 SimpleIdentifier targetId = _getTargetId(request.target); |
| 36 if (targetId == null) { |
| 37 return EMPTY_LIST; |
| 38 } |
| 39 |
| 40 // Partially resolve the compilation unit |
| 41 CompilationUnit unit = await request.resolveDeclarationsInScope(); |
| 42 // Gracefully degrade if the compilation unit could not be resolved |
| 43 // e.g. detached part file or source change |
| 44 if (unit == null) { |
| 45 return EMPTY_LIST; |
| 46 } |
| 47 |
| 48 // Recompute the target since resolution may have changed it |
| 49 targetId = _getTargetId(request.target); |
| 50 if (targetId == null) { |
| 51 return EMPTY_LIST; |
| 52 } |
| 53 ClassDeclaration classDecl = |
| 54 targetId.getAncestor((p) => p is ClassDeclaration); |
| 55 if (classDecl == null) { |
| 56 return EMPTY_LIST; |
| 57 } |
| 58 |
| 59 // Generate a collection of inherited members |
| 60 ClassElement classElem = classDecl.element; |
| 61 InheritanceManager manager = new InheritanceManager(classElem.library); |
| 62 MemberMap map = manager.getMapOfMembersInheritedFromInterfaces(classElem); |
| 63 List<String> memberNames = _computeMemberNames(map, classElem); |
| 64 |
| 65 // Build suggestions |
| 66 List<CompletionSuggestion> suggestions = <CompletionSuggestion>[]; |
| 67 for (String memberName in memberNames) { |
| 68 ExecutableElement element = map.get(memberName); |
| 69 // Gracefully degrade if the overridden element has not been resolved. |
| 70 if (element.returnType != null) { |
| 71 CompletionSuggestion suggestion = |
| 72 _buildSuggestion(request, targetId, unit, element); |
| 73 if (suggestion != null) { |
| 74 suggestions.add(suggestion); |
| 75 } |
| 76 } |
| 77 } |
| 78 return suggestions; |
| 79 } |
| 80 |
| 81 /** |
| 82 * If the target looks like a partial identifier inside a class declaration |
| 83 * then return that identifier, otherwise return `null`. |
| 84 */ |
| 85 SimpleIdentifier _getTargetId(CompletionTarget target) { |
| 86 AstNode node = target.containingNode; |
| 87 if (node is ClassDeclaration) { |
| 88 Object entity = target.entity; |
| 89 if (entity is FieldDeclaration) { |
| 90 NodeList<VariableDeclaration> variables = entity.fields.variables; |
| 91 if (variables.length == 1) { |
| 92 SimpleIdentifier targetId = variables[0].name; |
| 93 if (targetId.name.isEmpty) { |
| 94 return targetId; |
| 95 } |
| 96 } |
| 97 } |
| 98 } |
| 99 return null; |
| 100 } |
| 101 |
| 102 /** |
| 103 * Return a template for an override of the given [element] in the given |
| 104 * [source]. If selected, the template will replace [targetId]. |
| 105 */ |
| 106 String _buildRepacementText(Source source, SimpleIdentifier targetId, |
| 107 CompilationUnit unit, ExecutableElement element) { |
| 108 // AnalysisContext context = element.context; |
| 109 // Inject partially resolved unit for use by change builder |
| 110 // DartChangeBuilder builder = new DartChangeBuilder(context, unit); |
| 111 // builder.addFileEdit(source, context.getModificationStamp(source), |
| 112 // (DartFileEditBuilder builder) { |
| 113 // builder.addReplacement(targetId.offset, targetId.length, |
| 114 // (DartEditBuilder builder) { |
| 115 // builder.writeOverrideOfInheritedMember(element); |
| 116 // }); |
| 117 // }); |
| 118 // return builder.sourceChange.edits[0].edits[0].replacement.trim(); |
| 119 return ''; |
| 120 } |
| 121 |
| 122 /** |
| 123 * Build a suggestion to replace [targetId] in the given [unit] |
| 124 * with an override of the given [element]. |
| 125 */ |
| 126 CompletionSuggestion _buildSuggestion( |
| 127 DartCompletionRequest request, |
| 128 SimpleIdentifier targetId, |
| 129 CompilationUnit unit, |
| 130 ExecutableElement element) { |
| 131 String completion = |
| 132 _buildRepacementText(request.source, targetId, unit, element); |
| 133 if (completion == null || completion.length == 0) { |
| 134 return null; |
| 135 } |
| 136 CompletionSuggestion suggestion = new CompletionSuggestion( |
| 137 CompletionSuggestionKind.IDENTIFIER, |
| 138 DART_RELEVANCE_HIGH, |
| 139 completion, |
| 140 targetId.offset, |
| 141 0, |
| 142 element.isDeprecated, |
| 143 false); |
| 144 suggestion.element = protocol.convertElement(element); |
| 145 return suggestion; |
| 146 } |
| 147 |
| 148 /** |
| 149 * Return a list containing the names of all of the inherited but not |
| 150 * implemented members of the class represented by the given [element]. |
| 151 * The [map] is used to find all of the members that are inherited. |
| 152 */ |
| 153 List<String> _computeMemberNames(MemberMap map, ClassElement element) { |
| 154 List<String> memberNames = <String>[]; |
| 155 int count = map.size; |
| 156 for (int i = 0; i < count; i++) { |
| 157 String memberName = map.getKey(i); |
| 158 if (!_hasMember(element, memberName)) { |
| 159 memberNames.add(memberName); |
| 160 } |
| 161 } |
| 162 return memberNames; |
| 163 } |
| 164 |
| 165 /** |
| 166 * Return `true` if the given [classElement] directly declares a member with |
| 167 * the given [memberName]. |
| 168 */ |
| 169 bool _hasMember(ClassElement classElement, String memberName) { |
| 170 return classElement.getField(memberName) != null || |
| 171 classElement.getGetter(memberName) != null || |
| 172 classElement.getMethod(memberName) != null || |
| 173 classElement.getSetter(memberName) != null; |
| 174 } |
| 175 } |
OLD | NEW |