diff options
Diffstat (limited to 'crates/ra_ide_api_light')
-rw-r--r-- | crates/ra_ide_api_light/src/extend_selection.rs | 131 |
1 files changed, 127 insertions, 4 deletions
diff --git a/crates/ra_ide_api_light/src/extend_selection.rs b/crates/ra_ide_api_light/src/extend_selection.rs index 08cae5a51..db93db208 100644 --- a/crates/ra_ide_api_light/src/extend_selection.rs +++ b/crates/ra_ide_api_light/src/extend_selection.rs | |||
@@ -6,6 +6,21 @@ use ra_syntax::{ | |||
6 | 6 | ||
7 | pub fn extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange> { | 7 | pub fn extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange> { |
8 | let string_kinds = [COMMENT, STRING, RAW_STRING, BYTE_STRING, RAW_BYTE_STRING]; | 8 | let string_kinds = [COMMENT, STRING, RAW_STRING, BYTE_STRING, RAW_BYTE_STRING]; |
9 | let list_kinds = [ | ||
10 | FIELD_PAT_LIST, | ||
11 | MATCH_ARM_LIST, | ||
12 | NAMED_FIELD_LIST, | ||
13 | NAMED_FIELD_DEF_LIST, | ||
14 | POS_FIELD_LIST, | ||
15 | ENUM_VARIANT_LIST, | ||
16 | USE_TREE_LIST, | ||
17 | TYPE_PARAM_LIST, | ||
18 | TYPE_ARG_LIST, | ||
19 | PARAM_LIST, | ||
20 | ARG_LIST, | ||
21 | ARRAY_EXPR, | ||
22 | ]; | ||
23 | |||
9 | if range.is_empty() { | 24 | if range.is_empty() { |
10 | let offset = range.start(); | 25 | let offset = range.start(); |
11 | let mut leaves = find_leaf_at_offset(root, offset); | 26 | let mut leaves = find_leaf_at_offset(root, offset); |
@@ -26,9 +41,25 @@ pub fn extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange | |||
26 | return Some(leaf_range); | 41 | return Some(leaf_range); |
27 | }; | 42 | }; |
28 | let node = find_covering_node(root, range); | 43 | let node = find_covering_node(root, range); |
29 | if string_kinds.contains(&node.kind()) && range == node.range() { | 44 | |
30 | if let Some(range) = extend_comments(node) { | 45 | // Using shallowest node with same range allows us to traverse siblings. |
31 | return Some(range); | 46 | let node = node |
47 | .ancestors() | ||
48 | .take_while(|n| n.range() == node.range()) | ||
49 | .last() | ||
50 | .unwrap(); | ||
51 | |||
52 | if range == node.range() { | ||
53 | if string_kinds.contains(&node.kind()) { | ||
54 | if let Some(range) = extend_comments(node) { | ||
55 | return Some(range); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) { | ||
60 | if let Some(range) = extend_list_item(node) { | ||
61 | return Some(range); | ||
62 | } | ||
32 | } | 63 | } |
33 | } | 64 | } |
34 | 65 | ||
@@ -99,6 +130,45 @@ fn pick_best<'a>(l: &'a SyntaxNode, r: &'a SyntaxNode) -> &'a SyntaxNode { | |||
99 | } | 130 | } |
100 | } | 131 | } |
101 | 132 | ||
133 | /// Extend list item selection to include nearby comma and whitespace. | ||
134 | fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> { | ||
135 | fn is_single_line_ws(node: &SyntaxNode) -> bool { | ||
136 | node.kind() == WHITESPACE && !node.leaf_text().unwrap().contains('\n') | ||
137 | } | ||
138 | |||
139 | fn nearby_comma(node: &SyntaxNode, dir: Direction) -> Option<&SyntaxNode> { | ||
140 | node.siblings(dir) | ||
141 | .skip(1) | ||
142 | .skip_while(|node| is_single_line_ws(node)) | ||
143 | .next() | ||
144 | .filter(|node| node.kind() == COMMA) | ||
145 | } | ||
146 | |||
147 | if let Some(comma_node) = nearby_comma(node, Direction::Prev) { | ||
148 | return Some(TextRange::from_to( | ||
149 | comma_node.range().start(), | ||
150 | node.range().end(), | ||
151 | )); | ||
152 | } | ||
153 | |||
154 | if let Some(comma_node) = nearby_comma(node, Direction::Next) { | ||
155 | // Include any following whitespace when comma if after list item. | ||
156 | let final_node = comma_node | ||
157 | .siblings(Direction::Next) | ||
158 | .skip(1) | ||
159 | .next() | ||
160 | .filter(|node| is_single_line_ws(node)) | ||
161 | .unwrap_or(comma_node); | ||
162 | |||
163 | return Some(TextRange::from_to( | ||
164 | node.range().start(), | ||
165 | final_node.range().end(), | ||
166 | )); | ||
167 | } | ||
168 | |||
169 | return None; | ||
170 | } | ||
171 | |||
102 | fn extend_comments(node: &SyntaxNode) -> Option<TextRange> { | 172 | fn extend_comments(node: &SyntaxNode) -> Option<TextRange> { |
103 | let prev = adj_comments(node, Direction::Prev); | 173 | let prev = adj_comments(node, Direction::Prev); |
104 | let next = adj_comments(node, Direction::Next); | 174 | let next = adj_comments(node, Direction::Next); |
@@ -145,7 +215,60 @@ mod tests { | |||
145 | } | 215 | } |
146 | 216 | ||
147 | #[test] | 217 | #[test] |
148 | fn test_extend_selection_start_of_the_lind() { | 218 | fn test_extend_selection_list() { |
219 | do_check(r#"fn foo(<|>x: i32) {}"#, &["x", "x: i32"]); | ||
220 | do_check( | ||
221 | r#"fn foo(<|>x: i32, y: i32) {}"#, | ||
222 | &["x", "x: i32", "x: i32, "], | ||
223 | ); | ||
224 | do_check( | ||
225 | r#"fn foo(<|>x: i32,y: i32) {}"#, | ||
226 | &["x", "x: i32", "x: i32,"], | ||
227 | ); | ||
228 | do_check( | ||
229 | r#"fn foo(x: i32, <|>y: i32) {}"#, | ||
230 | &["y", "y: i32", ", y: i32"], | ||
231 | ); | ||
232 | do_check( | ||
233 | r#"fn foo(x: i32, <|>y: i32, ) {}"#, | ||
234 | &["y", "y: i32", ", y: i32"], | ||
235 | ); | ||
236 | do_check( | ||
237 | r#"fn foo(x: i32,<|>y: i32) {}"#, | ||
238 | &["y", "y: i32", ",y: i32"], | ||
239 | ); | ||
240 | |||
241 | do_check( | ||
242 | r#"const FOO: [usize; 2] = [ 22<|> , 33];"#, | ||
243 | &["22", "22 , "], | ||
244 | ); | ||
245 | do_check(r#"const FOO: [usize; 2] = [ 22 , 33<|>];"#, &["33", ", 33"]); | ||
246 | do_check( | ||
247 | r#"const FOO: [usize; 2] = [ 22 , 33<|> ,];"#, | ||
248 | &["33", ", 33"], | ||
249 | ); | ||
250 | |||
251 | do_check( | ||
252 | r#" | ||
253 | const FOO: [usize; 2] = [ | ||
254 | 22, | ||
255 | <|>33, | ||
256 | ]"#, | ||
257 | &["33", "33,"], | ||
258 | ); | ||
259 | |||
260 | do_check( | ||
261 | r#" | ||
262 | const FOO: [usize; 2] = [ | ||
263 | 22 | ||
264 | , 33<|>, | ||
265 | ]"#, | ||
266 | &["33", ", 33"], | ||
267 | ); | ||
268 | } | ||
269 | |||
270 | #[test] | ||
271 | fn test_extend_selection_start_of_the_line() { | ||
149 | do_check( | 272 | do_check( |
150 | r#" | 273 | r#" |
151 | impl S { | 274 | impl S { |