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 |