@@ -9,11 +9,12 @@ import 'package:analyzer/dart/ast/token.dart';
99import 'package:analyzer/dart/ast/visitor.dart' ;
1010import 'package:analyzer/dart/element/element.dart' ;
1111import 'package:analyzer/src/dart/ast/extensions.dart' ;
12+ import 'package:analyzer/src/utilities/extensions/collection.dart' ;
1213
1314void addDartOccurrences (OccurrencesCollector collector, CompilationUnit unit) {
1415 var visitor = DartUnitOccurrencesComputerVisitor ();
1516 unit.accept (visitor);
16- visitor.elementsOffsetLengths .forEach ((engineElement, offsetLengths ) {
17+ visitor.elementOccurrences .forEach ((engineElement, nodes ) {
1718 // For legacy protocol, we only support occurrences with the same
1819 // length, so we must filter the offset to only those that match the length
1920 // from the element.
@@ -24,18 +25,51 @@ void addDartOccurrences(OccurrencesCollector collector, CompilationUnit unit) {
2425 var length =
2526 serverElement.location? .length ?? engineElement.name? .length ?? 0 ;
2627 var offsets =
27- offsetLengths
28- .where ((offsetLength ) => offsetLength.$2 == length)
29- .map ((offsetLength ) => offsetLength.$1 )
28+ nodes
29+ .where ((node ) => node.length == length)
30+ .map ((node ) => node.offset )
3031 .toList ();
3132
3233 var occurrences = protocol.Occurrences (serverElement, offsets, length);
3334 collector.addOccurrences (occurrences);
3435 });
3536}
3637
37- class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor <void > {
38- final Map <Element , List <(int , int )>> elementsOffsetLengths = {};
38+ /// Returns both element-based and node-based occurrences for a file.
39+ ///
40+ /// The legacy protocol does not support occurrences that are not based on
41+ /// elements and therefore use [addDartOccurrences] . This method is for LSP
42+ /// which does not use elements and instead only cares about the resulting
43+ /// ranges of code, allowing ranges for keywords like `return` and `break` to be
44+ /// included.
45+ List <Occurrences > getAllOccurrences (CompilationUnit unit) {
46+ // TODO(dantup): Since LSP always starts with a target element or node,
47+ // we should consider passing it in here to avoid building the occurrences
48+ // for the whole file and then extracting only the matches we want.
49+ var visitor = DartUnitOccurrencesComputerVisitor ();
50+ unit.accept (visitor);
51+
52+ return [
53+ // Element-based occurrences
54+ for (var MapEntry (key: element, value: tokens)
55+ in visitor.elementOccurrences.entries)
56+ ElementOccurrences (element, tokens),
57+ // Node-based occurrences
58+ for (var MapEntry (key: node, value: tokens)
59+ in visitor.nodeOccurrences.entries)
60+ NodeOccurrences (node, tokens),
61+ ];
62+ }
63+
64+ class DartUnitOccurrencesComputerVisitor extends GeneralizingAstVisitor <void > {
65+ /// Occurrences tracked by their elements.
66+ final Map <Element , List <Token >> elementOccurrences = {};
67+
68+ /// Occurrences tracked by nodes (such as loops and their exit keywords).
69+ final Map <AstNode , List <Token >> nodeOccurrences = {};
70+
71+ // Stack to track the current function for return/yield keywords
72+ final List <AstNode > _functionStack = [];
3973
4074 @override
4175 void visitAssignedVariablePattern (AssignedVariablePattern node) {
@@ -47,6 +81,13 @@ class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
4781 super .visitAssignedVariablePattern (node);
4882 }
4983
84+ @override
85+ void visitBreakStatement (BreakStatement node) {
86+ _addNodeOccurrence (node.target, node.breakKeyword);
87+
88+ super .visitBreakStatement (node);
89+ }
90+
5091 @override
5192 void visitClassDeclaration (ClassDeclaration node) {
5293 _addOccurrence (node.declaredFragment! .element, node.name);
@@ -66,10 +107,9 @@ class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
66107 if (node.name case var name? ) {
67108 _addOccurrence (node.declaredFragment! .element, name);
68109 } else {
69- _addOccurrenceAt (
110+ _addOccurrence (
70111 node.declaredFragment! .element,
71- node.returnType.offset,
72- node.returnType.length,
112+ node.returnType.beginToken,
73113 );
74114 }
75115
@@ -91,6 +131,13 @@ class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
91131 super .visitConstructorName (node);
92132 }
93133
134+ @override
135+ void visitContinueStatement (ContinueStatement node) {
136+ _addNodeOccurrence (node.target, node.continueKeyword);
137+
138+ super .visitContinueStatement (node);
139+ }
140+
94141 @override
95142 void visitDeclaredIdentifier (DeclaredIdentifier node) {
96143 _addOccurrence (node.declaredFragment! .element, node.name);
@@ -109,6 +156,13 @@ class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
109156 super .visitDeclaredVariablePattern (node);
110157 }
111158
159+ @override
160+ void visitDoStatement (DoStatement node) {
161+ _addNodeOccurrence (node, node.doKeyword);
162+
163+ super .visitDoStatement (node);
164+ }
165+
112166 @override
113167 void visitEnumConstantDeclaration (EnumConstantDeclaration node) {
114168 _addOccurrence (node.declaredFragment! .element, node.name);
@@ -159,6 +213,20 @@ class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
159213 super .visitFieldFormalParameter (node);
160214 }
161215
216+ @override
217+ void visitForStatement (ForStatement node) {
218+ _addNodeOccurrence (node, node.forKeyword);
219+
220+ super .visitForStatement (node);
221+ }
222+
223+ @override
224+ void visitFunctionBody (FunctionBody node) {
225+ _functionStack.add (node);
226+ super .visitFunctionBody (node);
227+ _functionStack.removeLastOrNull ();
228+ }
229+
162230 @override
163231 void visitFunctionDeclaration (FunctionDeclaration node) {
164232 _addOccurrence (node.declaredFragment! .element, node.name);
@@ -235,6 +303,13 @@ class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
235303 super .visitRepresentationDeclaration (node);
236304 }
237305
306+ @override
307+ void visitReturnStatement (ReturnStatement node) {
308+ _addNodeOccurrence (_functionStack.lastOrNull, node.returnKeyword);
309+
310+ super .visitReturnStatement (node);
311+ }
312+
238313 @override
239314 void visitSimpleFormalParameter (SimpleFormalParameter node) {
240315 var nameToken = node.name;
@@ -270,6 +345,13 @@ class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
270345 super .visitSuperFormalParameter (node);
271346 }
272347
348+ @override
349+ void visitSwitchStatement (SwitchStatement node) {
350+ _addNodeOccurrence (node, node.switchKeyword);
351+
352+ super .visitSwitchStatement (node);
353+ }
354+
273355 @override
274356 void visitTypeParameter (TypeParameter node) {
275357 if (node case TypeParameter (: var declaredFragment? )) {
@@ -285,21 +367,32 @@ class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
285367 super .visitVariableDeclaration (node);
286368 }
287369
288- void _addOccurrence (Element element, Token token) {
289- _addOccurrenceAt (element, token.offset, token.length);
370+ @override
371+ void visitWhileStatement (WhileStatement node) {
372+ _addNodeOccurrence (node, node.whileKeyword);
373+
374+ super .visitWhileStatement (node);
375+ }
376+
377+ @override
378+ void visitYieldStatement (YieldStatement node) {
379+ _addNodeOccurrence (_functionStack.lastOrNull, node.yieldKeyword);
380+
381+ super .visitYieldStatement (node);
382+ }
383+
384+ void _addNodeOccurrence (AstNode ? node, Token token) {
385+ if (node == null ) return ;
386+
387+ (nodeOccurrences[node] ?? = []).add (token);
290388 }
291389
292- void _addOccurrenceAt (Element element, int offset, int length ) {
390+ void _addOccurrence (Element element, Token token ) {
293391 var canonicalElement = _canonicalizeElement (element);
294392 if (canonicalElement == null ) {
295393 return ;
296394 }
297- var offsetLengths = elementsOffsetLengths[canonicalElement];
298- if (offsetLengths == null ) {
299- offsetLengths = < (int , int )> [];
300- elementsOffsetLengths[canonicalElement] = offsetLengths;
301- }
302- offsetLengths.add ((offset, length));
395+ (elementOccurrences[canonicalElement] ?? = []).add (token);
303396 }
304397
305398 Element ? _canonicalizeElement (Element element) {
@@ -314,3 +407,28 @@ class DartUnitOccurrencesComputerVisitor extends RecursiveAstVisitor<void> {
314407 return canonicalElement? .baseElement;
315408 }
316409}
410+
411+ /// Occurrences grouped by an Element.
412+ class ElementOccurrences extends Occurrences {
413+ final Element element;
414+
415+ @override
416+ final List <Token > tokens;
417+
418+ ElementOccurrences (this .element, this .tokens);
419+ }
420+
421+ /// Occurrences grouped by a node (for example exit keywords grouped by a loop).
422+ class NodeOccurrences extends Occurrences {
423+ final AstNode node;
424+
425+ @override
426+ final List <Token > tokens;
427+
428+ NodeOccurrences (this .node, this .tokens);
429+ }
430+
431+ /// Base class for protocol-agnostic occurrences.
432+ sealed class Occurrences {
433+ List <Token > get tokens;
434+ }
0 commit comments