Skip to content

Commit 4be9188

Browse files
DanTupCommit Queue
authored andcommitted
[analysis_server] Support occurrences (Document Highlights) with different lengths
+ split unnamed constructors from classes Fixes Dart-Code/Dart-Code#4454 Change-Id: I42e7346b7c22348cf6424d15c5c35a24e31c85b3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406822 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]>
1 parent 9b0ae33 commit 4be9188

File tree

6 files changed

+182
-70
lines changed

6 files changed

+182
-70
lines changed

pkg/analysis_server/lib/src/domains/analysis/occurrences_dart.dart

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,89 +5,124 @@
55
import 'package:analysis_server/plugin/analysis/occurrences/occurrences_core.dart';
66
import 'package:analysis_server/src/protocol_server.dart' as protocol;
77
import 'package:analyzer/dart/ast/ast.dart';
8+
import 'package:analyzer/dart/ast/token.dart';
89
import 'package:analyzer/dart/ast/visitor.dart';
910
import 'package:analyzer/dart/element/element2.dart';
1011
import 'package:analyzer/src/dart/ast/extensions.dart';
1112
import 'package:analyzer/src/dart/element/element.dart';
1213

1314
void addDartOccurrences(OccurrencesCollector collector, CompilationUnit unit) {
14-
var visitor = _DartUnitOccurrencesComputerVisitor();
15+
var visitor = DartUnitOccurrencesComputerVisitor();
1516
unit.accept(visitor);
16-
visitor.elementsOffsets.forEach((engineElement, offsets) {
17-
var length = engineElement.name3?.length ?? 0;
17+
visitor.elementsOffsetLengths.forEach((engineElement, offsetLengths) {
18+
// For legacy protocol, we only support occurrences with the same
19+
// length, so we must filter the offset to only those that match the length
20+
// from the element.
1821
var serverElement = protocol.convertElement2(engineElement);
22+
// Prefer the length from the mapped element over the element directly,
23+
// because 'name3' may contain 'new' for constructors which doesn't match
24+
// what is in the source.
25+
var length =
26+
serverElement.location?.length ?? engineElement.name3?.length ?? 0;
27+
var offsets =
28+
offsetLengths
29+
.where((offsetLength) => offsetLength.$2 == length)
30+
.map((offsetLength) => offsetLength.$1)
31+
.toList();
32+
1933
var occurrences = protocol.Occurrences(serverElement, offsets, length);
2034
collector.addOccurrences(occurrences);
2135
});
2236
}
2337

24-
class _DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
25-
final Map<Element2, List<int>> elementsOffsets = {};
38+
class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
39+
final Map<Element2, List<(int, int)>> elementsOffsetLengths = {};
2640

2741
@override
2842
void visitAssignedVariablePattern(AssignedVariablePattern node) {
2943
var element = node.element2;
3044
if (element != null) {
31-
_addOccurrence(element, node.name.offset);
45+
_addOccurrence(element, node.name);
3246
}
3347

3448
super.visitAssignedVariablePattern(node);
3549
}
3650

3751
@override
3852
void visitClassDeclaration(ClassDeclaration node) {
39-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
53+
_addOccurrence(node.declaredFragment!.element, node.name);
4054

4155
super.visitClassDeclaration(node);
4256
}
4357

4458
@override
4559
void visitClassTypeAlias(ClassTypeAlias node) {
46-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
60+
_addOccurrence(node.declaredFragment!.element, node.name);
4761

4862
super.visitClassTypeAlias(node);
4963
}
5064

5165
@override
5266
void visitConstructorDeclaration(ConstructorDeclaration node) {
5367
if (node.name case var name?) {
54-
_addOccurrence(node.declaredFragment!.element, name.offset);
68+
_addOccurrence(node.declaredFragment!.element, name);
69+
} else {
70+
_addOccurrenceAt(
71+
node.declaredFragment!.element,
72+
node.returnType.offset,
73+
node.returnType.length,
74+
);
5575
}
5676

5777
super.visitConstructorDeclaration(node);
5878
}
5979

80+
@override
81+
void visitConstructorName(ConstructorName node) {
82+
// For unnamed constructors, we add an occurence for the constructor at
83+
// the location of the returnType.
84+
if (node.name == null) {
85+
var element = node.element;
86+
if (element != null) {
87+
_addOccurrence(element, node.type.name2);
88+
}
89+
return; // skip visitNamedType.
90+
}
91+
92+
super.visitConstructorName(node);
93+
}
94+
6095
@override
6196
void visitDeclaredIdentifier(DeclaredIdentifier node) {
62-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
97+
_addOccurrence(node.declaredFragment!.element, node.name);
6398

6499
super.visitDeclaredIdentifier(node);
65100
}
66101

67102
@override
68103
void visitDeclaredVariablePattern(DeclaredVariablePattern node) {
69-
_addOccurrence(node.declaredElement2!, node.name.offset);
104+
_addOccurrence(node.declaredElement2!, node.name);
70105

71106
super.visitDeclaredVariablePattern(node);
72107
}
73108

74109
@override
75110
void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
76-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
111+
_addOccurrence(node.declaredFragment!.element, node.name);
77112

78113
super.visitEnumConstantDeclaration(node);
79114
}
80115

81116
@override
82117
void visitEnumDeclaration(EnumDeclaration node) {
83-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
118+
_addOccurrence(node.declaredFragment!.element, node.name);
84119

85120
super.visitEnumDeclaration(node);
86121
}
87122

88123
@override
89124
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
90-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
125+
_addOccurrence(node.declaredFragment!.element, node.name);
91126

92127
super.visitExtensionTypeDeclaration(node);
93128
}
@@ -98,7 +133,7 @@ class _DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
98133
if (declaredElement is FieldFormalParameterElement2) {
99134
var field = declaredElement.field2;
100135
if (field != null) {
101-
_addOccurrence(field, node.name.offset);
136+
_addOccurrence(field, node.name);
102137
}
103138
}
104139

@@ -107,40 +142,40 @@ class _DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
107142

108143
@override
109144
void visitFunctionDeclaration(FunctionDeclaration node) {
110-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
145+
_addOccurrence(node.declaredFragment!.element, node.name);
111146
super.visitFunctionDeclaration(node);
112147
}
113148

114149
@override
115150
void visitFunctionTypeAlias(FunctionTypeAlias node) {
116-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
151+
_addOccurrence(node.declaredFragment!.element, node.name);
117152

118153
super.visitFunctionTypeAlias(node);
119154
}
120155

121156
@override
122157
void visitGenericTypeAlias(GenericTypeAlias node) {
123-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
158+
_addOccurrence(node.declaredFragment!.element, node.name);
124159

125160
super.visitGenericTypeAlias(node);
126161
}
127162

128163
@override
129164
void visitImportPrefixReference(ImportPrefixReference node) {
130-
_addOccurrence(node.element2!, node.offset);
165+
_addOccurrence(node.element2!, node.name);
131166

132167
super.visitImportPrefixReference(node);
133168
}
134169

135170
@override
136171
void visitMethodDeclaration(MethodDeclaration node) {
137-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
172+
_addOccurrence(node.declaredFragment!.element, node.name);
138173
super.visitMethodDeclaration(node);
139174
}
140175

141176
@override
142177
void visitMixinDeclaration(MixinDeclaration node) {
143-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
178+
_addOccurrence(node.declaredFragment!.element, node.name);
144179

145180
super.visitMixinDeclaration(node);
146181
}
@@ -149,7 +184,7 @@ class _DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
149184
void visitNamedType(NamedType node) {
150185
var element = node.element2;
151186
if (element != null) {
152-
_addOccurrence(element, node.name2.offset);
187+
_addOccurrence(element, node.name2);
153188
}
154189

155190
super.visitNamedType(node);
@@ -165,18 +200,15 @@ class _DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
165200
? pattern.name
166201
: node.name?.name;
167202
if (element != null && name != null) {
168-
_addOccurrence(element, name.offset);
203+
_addOccurrence(element, name);
169204
}
170205
super.visitPatternField(node);
171206
}
172207

173208
@override
174209
void visitRepresentationDeclaration(RepresentationDeclaration node) {
175210
if (node.constructorName case var constructorName?) {
176-
_addOccurrence(
177-
node.constructorFragment!.element,
178-
constructorName.name.offset,
179-
);
211+
_addOccurrence(node.constructorFragment!.element, constructorName.name);
180212
}
181213

182214
super.visitRepresentationDeclaration(node);
@@ -186,44 +218,58 @@ class _DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
186218
void visitSimpleFormalParameter(SimpleFormalParameter node) {
187219
var nameToken = node.name;
188220
if (nameToken != null) {
189-
_addOccurrence(node.declaredFragment!.element, nameToken.offset);
221+
_addOccurrence(node.declaredFragment!.element, nameToken);
190222
}
191223

192224
super.visitSimpleFormalParameter(node);
193225
}
194226

195227
@override
196228
void visitSimpleIdentifier(SimpleIdentifier node) {
229+
// For unnamed constructors, we don't want to add an occurrence for the
230+
// class name here because visitConstructorDeclaration will have added one
231+
// for the constructor (not the type).
232+
if (node.parent case ConstructorDeclaration(
233+
:var name,
234+
:var returnType,
235+
) when name == null && node == returnType) {
236+
return;
237+
}
238+
197239
var element = node.writeOrReadElement2;
198240
if (element != null) {
199-
_addOccurrence(element, node.offset);
241+
_addOccurrence(element, node.token);
200242
}
201243
return super.visitSimpleIdentifier(node);
202244
}
203245

204246
@override
205247
void visitSuperFormalParameter(SuperFormalParameter node) {
206-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
248+
_addOccurrence(node.declaredFragment!.element, node.name);
207249
super.visitSuperFormalParameter(node);
208250
}
209251

210252
@override
211253
void visitVariableDeclaration(VariableDeclaration node) {
212-
_addOccurrence(node.declaredFragment!.element, node.name.offset);
254+
_addOccurrence(node.declaredFragment!.element, node.name);
213255
super.visitVariableDeclaration(node);
214256
}
215257

216-
void _addOccurrence(Element2 element, int offset) {
258+
void _addOccurrence(Element2 element, Token token) {
259+
_addOccurrenceAt(element, token.offset, token.length);
260+
}
261+
262+
void _addOccurrenceAt(Element2 element, int offset, int length) {
217263
var canonicalElement = _canonicalizeElement(element);
218264
if (canonicalElement == null || element == DynamicElementImpl.instance) {
219265
return;
220266
}
221-
var offsets = elementsOffsets[canonicalElement];
222-
if (offsets == null) {
223-
offsets = <int>[];
224-
elementsOffsets[canonicalElement] = offsets;
267+
var offsetLengths = elementsOffsetLengths[canonicalElement];
268+
if (offsetLengths == null) {
269+
offsetLengths = <(int, int)>[];
270+
elementsOffsetLengths[canonicalElement] = offsetLengths;
225271
}
226-
offsets.add(offset);
272+
offsetLengths.add((offset, length));
227273
}
228274

229275
Element2? _canonicalizeElement(Element2 element) {

pkg/analysis_server/lib/src/lsp/handlers/handler_document_highlights.dart

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:analysis_server/lsp_protocol/protocol.dart';
6-
import 'package:analysis_server/src/domains/analysis/occurrences.dart';
76
import 'package:analysis_server/src/domains/analysis/occurrences_dart.dart';
87
import 'package:analysis_server/src/lsp/error_or.dart';
98
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
109
import 'package:analysis_server/src/lsp/mapping.dart';
1110
import 'package:analysis_server/src/lsp/registration/feature_registration.dart';
11+
import 'package:collection/collection.dart';
1212

1313
typedef StaticOptions = Either2<bool, DocumentHighlightOptions>;
1414

@@ -45,32 +45,47 @@ class DocumentHighlightsHandler
4545
var offset = unit.mapResultSync((unit) => toOffset(unit.lineInfo, pos));
4646

4747
return (unit, offset).mapResults((unit, requestedOffset) async {
48-
var collector = OccurrencesCollectorImpl();
49-
addDartOccurrences(collector, unit.unit);
48+
var visitor = DartUnitOccurrencesComputerVisitor();
49+
unit.unit.accept(visitor);
5050

5151
/// Checks whether an Occurrence offset/length spans the requested
5252
/// offset.
5353
///
5454
/// It's possible multiple occurrences might match because some nodes
5555
/// such as object destructuring might match multiple elements (for
5656
/// example the object getter and a declared variable).
57-
bool spansRequestedPosition(int offset, int length) {
57+
bool spansRequestedPosition((int, int) offsetLength) {
58+
var (offset, length) = offsetLength;
5859
return offset <= requestedOffset && offset + length >= requestedOffset;
5960
}
6061

61-
// Find an occurrence that has an instance that spans the position.
62-
var occurrences =
63-
collector.allOccurrences
62+
// Find the groups (elements) of offset/lengths that contains an
63+
// offset/length that spans the requested range. There may be multiple
64+
// matches here if the source element is in multiple groups.
65+
var matchingSet =
66+
visitor.elementsOffsetLengths.values
6467
.where(
65-
(occurrence) => occurrence.offsets.any(
66-
(offset) => spansRequestedPosition(offset, occurrence.length),
67-
),
68+
(offsetLengths) => offsetLengths.any(spansRequestedPosition),
6869
)
69-
.toList();
70+
.flattened
71+
.toSet();
7072

7173
// No matches will return an empty list (not null) because that prevents
7274
// the editor falling back to a text search.
73-
return success(toHighlights(unit.lineInfo, occurrences));
75+
var highlights =
76+
matchingSet
77+
.map(
78+
(offsetLength) => DocumentHighlight(
79+
range: toRange(
80+
unit.lineInfo,
81+
offsetLength.$1,
82+
offsetLength.$2,
83+
),
84+
),
85+
)
86+
.toList();
87+
88+
return success(highlights);
7489
});
7590
}
7691
}

pkg/analysis_server/lib/src/lsp/mapping.dart

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,22 +1438,6 @@ lsp.FoldingRangeKind? toFoldingRangeKind(server.FoldingKind kind) {
14381438
}
14391439
}
14401440

1441-
List<lsp.DocumentHighlight> toHighlights(
1442-
server.LineInfo lineInfo,
1443-
List<server.Occurrences> occurrences,
1444-
) {
1445-
return occurrences
1446-
.map(
1447-
(occurrence) => occurrence.offsets.map(
1448-
(offset) => lsp.DocumentHighlight(
1449-
range: toRange(lineInfo, offset, occurrence.length),
1450-
),
1451-
),
1452-
)
1453-
.flattenedToSet
1454-
.toList();
1455-
}
1456-
14571441
lsp.Location toLocation(
14581442
ClientUriConverter uriConverter,
14591443
server.Location location,

0 commit comments

Comments
 (0)