OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, 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.src.correction.selection_analyzer; | |
6 | |
7 import 'package:analysis_services/src/correction/source_range.dart'; | |
8 import 'package:analyzer/src/generated/ast.dart'; | |
9 import 'package:analyzer/src/generated/source.dart'; | |
10 | |
11 | |
12 /** | |
13 * A visitor for visiting [AstNode]s covered by a selection [SourceRange]. | |
14 */ | |
15 class SelectionAnalyzer extends GeneralizingAstVisitor<Object> { | |
16 final SourceRange selection; | |
17 | |
18 AstNode _coveringNode; | |
19 List<AstNode> _selectedNodes; | |
20 | |
21 SelectionAnalyzer(this.selection); | |
22 | |
23 /** | |
24 * Return the [AstNode] with the shortest length which completely covers the | |
25 * specified selection. | |
26 */ | |
27 AstNode get coveringNode => _coveringNode; | |
28 | |
29 /** | |
30 * Returns the first selected [AstNode], may be `null`. | |
31 */ | |
32 AstNode get firstSelectedNode { | |
33 if (_selectedNodes == null || _selectedNodes.isEmpty) { | |
34 return null; | |
35 } | |
36 return _selectedNodes[0]; | |
37 } | |
38 | |
39 /** | |
40 * Returns `true` if there are [AstNode]s fully covered by the | |
41 * selection [SourceRange]. | |
42 */ | |
43 bool get hasSelectedNodes => | |
44 _selectedNodes != null && !_selectedNodes.isEmpty; | |
45 | |
46 /** | |
47 * Returns `true` if there was no selected nodes yet. | |
48 */ | |
49 bool get isFirstNode => _selectedNodes == null; | |
50 | |
51 /** | |
52 * Returns the last selected [AstNode], may be `null`. | |
53 */ | |
54 AstNode get lastSelectedNode { | |
55 if (_selectedNodes == null || _selectedNodes.isEmpty) { | |
56 return null; | |
57 } | |
58 return _selectedNodes[_selectedNodes.length - 1]; | |
59 } | |
60 | |
61 /** | |
62 * Returns the [SourceRange] which covers selected [AstNode]s, may be `null` | |
63 * if there are no [AstNode]s under the selection. | |
64 */ | |
65 SourceRange get selectedNodeRange { | |
66 if (_selectedNodes == null || _selectedNodes.isEmpty) { | |
67 return null; | |
68 } | |
69 AstNode firstNode = _selectedNodes[0]; | |
70 AstNode lastNode = _selectedNodes[_selectedNodes.length - 1]; | |
71 return rangeStartEnd(firstNode, lastNode); | |
72 } | |
73 | |
74 /** | |
75 * Return the [AstNode]s fully covered by the selection [SourceRange]. | |
76 */ | |
77 List<AstNode> get selectedNodes { | |
78 if (_selectedNodes == null || _selectedNodes.isEmpty) { | |
79 return []; | |
80 } | |
81 return _selectedNodes; | |
82 } | |
83 | |
84 /** | |
85 * Adds first selected [AstNode]. | |
86 */ | |
87 void handleFirstSelectedNode(AstNode node) { | |
88 _selectedNodes = []; | |
89 _selectedNodes.add(node); | |
90 } | |
91 | |
92 /** | |
93 * Adds second or more selected [AstNode]. | |
94 */ | |
95 void handleNextSelectedNode(AstNode node) { | |
96 if (firstSelectedNode.parent == node.parent) { | |
97 _selectedNodes.add(node); | |
98 } | |
99 } | |
100 | |
101 /** | |
102 * Notifies that selection ends in given [AstNode]. | |
103 */ | |
104 void handleSelectionEndsIn(AstNode node) { | |
105 } | |
106 | |
107 /** | |
108 * Notifies that selection starts in given [AstNode]. | |
109 */ | |
110 void handleSelectionStartsIn(AstNode node) { | |
111 } | |
112 | |
113 /** | |
114 * Resets selected nodes. | |
115 */ | |
116 void reset() { | |
117 _selectedNodes = null; | |
118 } | |
119 | |
120 @override | |
121 Object visitNode(AstNode node) { | |
122 SourceRange nodeRange = rangeNode(node); | |
123 if (selection.covers(nodeRange)) { | |
124 if (isFirstNode) { | |
125 handleFirstSelectedNode(node); | |
126 } else { | |
127 handleNextSelectedNode(node); | |
128 } | |
129 return null; | |
130 } else if (selection.coveredBy(nodeRange)) { | |
131 _coveringNode = node; | |
132 node.visitChildren(this); | |
133 return null; | |
134 } else if (selection.startsIn(nodeRange)) { | |
135 handleSelectionStartsIn(node); | |
136 node.visitChildren(this); | |
137 return null; | |
138 } else if (selection.endsIn(nodeRange)) { | |
139 handleSelectionEndsIn(node); | |
140 node.visitChildren(this); | |
141 return null; | |
142 } | |
143 // no intersection | |
144 return null; | |
145 } | |
146 } | |
OLD | NEW |