aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api_light/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api_light/src')
-rw-r--r--crates/ra_ide_api_light/src/extend_selection.rs131
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
7pub fn extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange> { 7pub 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.
134fn 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
102fn extend_comments(node: &SyntaxNode) -> Option<TextRange> { 172fn 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#"
253const FOO: [usize; 2] = [
254 22,
255 <|>33,
256]"#,
257 &["33", "33,"],
258 );
259
260 do_check(
261 r#"
262const 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#"
151impl S { 274impl S {