2
2
// for details. All rights reserved. Use of this source code is governed by a
3
3
// BSD-style license that can be found in the LICENSE file.
4
4
5
+ import 'package:analysis_server/src/protocol_server.dart'
6
+ show CompletionSuggestionKind;
5
7
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart' ;
6
8
import 'package:analyzer/dart/ast/ast.dart' ;
7
9
import 'package:analyzer/dart/ast/token.dart' ;
@@ -21,6 +23,49 @@ sealed class CandidateSuggestion {
21
23
String get completion;
22
24
}
23
25
26
+ /// The information about a candidate suggestion based on a constructor.
27
+ final class ConstructorSuggestion extends CandidateSuggestion {
28
+ /// The element on which the suggestion is based.
29
+ final ConstructorElement element;
30
+
31
+ /// Initialize a newly created candidate suggestion to suggest the [element] .
32
+ ConstructorSuggestion (this .element);
33
+
34
+ @override
35
+ String get completion => element.displayName;
36
+ }
37
+
38
+ /// The information about a candidate suggestion based on an executable element,
39
+ /// either a method or function.
40
+ sealed class ExecutableSuggestion extends CandidateSuggestion {
41
+ /// The kind of suggestion to be made, either
42
+ /// [CompletionSuggestionKind.IDENTIFIER] or
43
+ /// [CompletionSuggestionKind.INVOCATION] .
44
+ final CompletionSuggestionKind kind;
45
+
46
+ /// Initialize a newly created suggestion to use the given [kind] of
47
+ /// suggestion.
48
+ ExecutableSuggestion (this .kind)
49
+ : assert (kind == CompletionSuggestionKind .IDENTIFIER ||
50
+ kind == CompletionSuggestionKind .INVOCATION );
51
+ }
52
+
53
+ /// The information about a candidate suggestion based on a field.
54
+ final class FieldSuggestion extends CandidateSuggestion {
55
+ /// The element on which the suggestion is based.
56
+ final FieldElement element;
57
+
58
+ /// The class from which the field is being referenced, or `null` if the class
59
+ /// is not being referenced from within a class.
60
+ final ClassElement ? referencingClass;
61
+
62
+ /// Initialize a newly created candidate suggestion to suggest the [element] .
63
+ FieldSuggestion (this .element, this .referencingClass);
64
+
65
+ @override
66
+ String get completion => element.name;
67
+ }
68
+
24
69
/// The information about a candidate suggestion based on a formal parameter.
25
70
final class FormalParameterSuggestion extends CandidateSuggestion {
26
71
/// The element on which the suggestion is based.
@@ -33,7 +78,8 @@ final class FormalParameterSuggestion extends CandidateSuggestion {
33
78
String get completion => element.name;
34
79
}
35
80
36
- /// The information about a candidate suggestion based on an identifier.
81
+ /// The information about a candidate suggestion based on an identifier being
82
+ /// guessed for a declaration site.
37
83
final class IdentifierSuggestion extends CandidateSuggestion {
38
84
/// The identifier to be inserted.
39
85
final String identifier;
@@ -118,12 +164,12 @@ final class LabelSuggestion extends CandidateSuggestion {
118
164
}
119
165
120
166
/// The information about a candidate suggestion based on a local function.
121
- final class LocalFunctionSuggestion extends CandidateSuggestion {
167
+ final class LocalFunctionSuggestion extends ExecutableSuggestion {
122
168
/// The element on which the suggestion is based.
123
169
final FunctionElement element;
124
170
125
171
/// Initialize a newly created candidate suggestion to suggest the [element] .
126
- LocalFunctionSuggestion (this .element);
172
+ LocalFunctionSuggestion (super .kind, this .element);
127
173
128
174
@override
129
175
String get completion => element.name;
@@ -145,13 +191,50 @@ final class LocalVariableSuggestion extends CandidateSuggestion {
145
191
String get completion => element.name;
146
192
}
147
193
194
+ /// The information about a candidate suggestion based on a method.
195
+ final class MethodSuggestion extends ExecutableSuggestion {
196
+ /// The element on which the suggestion is based.
197
+ final MethodElement element;
198
+
199
+ final ClassElement ? referencingClass;
200
+
201
+ /// Initialize a newly created candidate suggestion to suggest the [element] .
202
+ MethodSuggestion (super .kind, this .element, this .referencingClass);
203
+
204
+ @override
205
+ String get completion => element.name;
206
+ }
207
+
208
+ /// The information about a candidate suggestion based on a method.
209
+ final class PropertyAccessSuggestion extends CandidateSuggestion {
210
+ /// The element on which the suggestion is based.
211
+ final PropertyAccessorElement element;
212
+
213
+ final ClassElement ? referencingClass;
214
+
215
+ /// Initialize a newly created candidate suggestion to suggest the [element] .
216
+ PropertyAccessSuggestion (this .element, this .referencingClass);
217
+
218
+ @override
219
+ String get completion => element.name;
220
+ }
221
+
148
222
extension SuggestionBuilderExtension on SuggestionBuilder {
149
223
// TODO(brianwilkerson) Move these to `SuggestionBuilder`, possibly as part
150
224
// of splitting it into a legacy builder and an LSP builder.
151
225
152
226
/// Add a suggestion based on the candidate [suggestion] .
153
227
void suggestFromCandidate (CandidateSuggestion suggestion) {
154
228
switch (suggestion) {
229
+ case ConstructorSuggestion ():
230
+ suggestConstructor (suggestion.element);
231
+ case FieldSuggestion ():
232
+ suggestField (suggestion.element,
233
+ inheritanceDistance: _inheritanceDistance (
234
+ suggestion.referencingClass,
235
+ suggestion.element.enclosingElement));
236
+ case FormalParameterSuggestion ():
237
+ suggestParameter (suggestion.element);
155
238
case IdentifierSuggestion ():
156
239
suggestName (suggestion.identifier);
157
240
case KeywordSuggestion ():
@@ -165,8 +248,31 @@ extension SuggestionBuilderExtension on SuggestionBuilder {
165
248
// TODO(brianwilkerson) Enhance `suggestLocalVariable` to allow the
166
249
// distance to be passed in.
167
250
suggestLocalVariable (suggestion.element);
168
- case FormalParameterSuggestion ():
169
- suggestParameter (suggestion.element);
251
+ case MethodSuggestion ():
252
+ // TODO(brianwilkerson) Correctly set the kind of suggestion in cases
253
+ // where `isFunctionalArgument` would return `true` so we can stop
254
+ // using the `request.target`.
255
+ var kind = request.target.isFunctionalArgument ()
256
+ ? CompletionSuggestionKind .IDENTIFIER
257
+ : suggestion.kind;
258
+ suggestMethod (
259
+ suggestion.element,
260
+ kind: kind,
261
+ inheritanceDistance: _inheritanceDistance (
262
+ suggestion.referencingClass, suggestion.element.enclosingElement),
263
+ );
264
+ case PropertyAccessSuggestion ():
265
+ var inheritanceDistance = 0.0 ;
266
+ var referencingClass = suggestion.referencingClass;
267
+ var declaringClass = suggestion.element.enclosingElement;
268
+ if (referencingClass != null && declaringClass is InterfaceElement ) {
269
+ inheritanceDistance = request.featureComputer
270
+ .inheritanceDistanceFeature (referencingClass, declaringClass);
271
+ }
272
+ suggestAccessor (
273
+ suggestion.element,
274
+ inheritanceDistance: inheritanceDistance,
275
+ );
170
276
}
171
277
}
172
278
@@ -176,4 +282,16 @@ extension SuggestionBuilderExtension on SuggestionBuilder {
176
282
suggestFromCandidate (suggestion);
177
283
}
178
284
}
285
+
286
+ /// Returns the inheritance distance from the [referencingClass] to the
287
+ /// [declaringClass] .
288
+ double _inheritanceDistance (
289
+ ClassElement ? referencingClass, Element ? declaringClass) {
290
+ var distance = 0.0 ;
291
+ if (referencingClass != null && declaringClass is InterfaceElement ) {
292
+ distance = request.featureComputer
293
+ .inheritanceDistanceFeature (referencingClass, declaringClass);
294
+ }
295
+ return distance;
296
+ }
179
297
}
0 commit comments