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