diff options
author | Alexander Elís Ebenesersson <[email protected]> | 2019-10-27 12:18:54 +0000 |
---|---|---|
committer | Alexander Elís Ebenesersson <[email protected]> | 2019-10-27 12:29:06 +0000 |
commit | 3e7e3fdf163363896c1273d764e7665a00078ef8 (patch) | |
tree | e9879367f34a13abcbd8f610b3e583faf825ea91 /crates/ra_ide_api | |
parent | 7dfbe28211910d5d7c74a593bf0007c5db3e3496 (diff) |
extend selection in trait bound extends to plus
When multiple traits bounds are present, expanded selection
from a single trait bound will include the nearest plus sign
(and whitespace after) before including the whole trait bound.
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r-- | crates/ra_ide_api/src/extend_selection.rs | 82 |
1 files changed, 72 insertions, 10 deletions
diff --git a/crates/ra_ide_api/src/extend_selection.rs b/crates/ra_ide_api/src/extend_selection.rs index 602757e92..4b7bfc0b1 100644 --- a/crates/ra_ide_api/src/extend_selection.rs +++ b/crates/ra_ide_api/src/extend_selection.rs | |||
@@ -5,7 +5,7 @@ use ra_syntax::{ | |||
5 | algo::find_covering_element, | 5 | algo::find_covering_element, |
6 | ast::{self, AstNode, AstToken}, | 6 | ast::{self, AstNode, AstToken}, |
7 | Direction, NodeOrToken, | 7 | Direction, NodeOrToken, |
8 | SyntaxKind::*, | 8 | SyntaxKind::{self, *}, |
9 | SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T, | 9 | SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T, |
10 | }; | 10 | }; |
11 | 11 | ||
@@ -29,10 +29,12 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange | |||
29 | USE_TREE_LIST, | 29 | USE_TREE_LIST, |
30 | TYPE_PARAM_LIST, | 30 | TYPE_PARAM_LIST, |
31 | TYPE_ARG_LIST, | 31 | TYPE_ARG_LIST, |
32 | TYPE_BOUND_LIST, | ||
32 | PARAM_LIST, | 33 | PARAM_LIST, |
33 | ARG_LIST, | 34 | ARG_LIST, |
34 | ARRAY_EXPR, | 35 | ARRAY_EXPR, |
35 | TUPLE_EXPR, | 36 | TUPLE_EXPR, |
37 | WHERE_CLAUSE, | ||
36 | ]; | 38 | ]; |
37 | 39 | ||
38 | if range.is_empty() { | 40 | if range.is_empty() { |
@@ -146,13 +148,17 @@ fn pick_best<'a>(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken { | |||
146 | } | 148 | } |
147 | } | 149 | } |
148 | 150 | ||
149 | /// Extend list item selection to include nearby comma and whitespace. | 151 | /// Extend list item selection to include nearby delimiter and whitespace. |
150 | fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> { | 152 | fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> { |
151 | fn is_single_line_ws(node: &SyntaxToken) -> bool { | 153 | fn is_single_line_ws(node: &SyntaxToken) -> bool { |
152 | node.kind() == WHITESPACE && !node.text().contains('\n') | 154 | node.kind() == WHITESPACE && !node.text().contains('\n') |
153 | } | 155 | } |
154 | 156 | ||
155 | fn nearby_comma(node: &SyntaxNode, dir: Direction) -> Option<SyntaxToken> { | 157 | fn nearby_delimiter( |
158 | delimiter_kind: SyntaxKind, | ||
159 | node: &SyntaxNode, | ||
160 | dir: Direction, | ||
161 | ) -> Option<SyntaxToken> { | ||
156 | node.siblings_with_tokens(dir) | 162 | node.siblings_with_tokens(dir) |
157 | .skip(1) | 163 | .skip(1) |
158 | .skip_while(|node| match node { | 164 | .skip_while(|node| match node { |
@@ -161,19 +167,26 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> { | |||
161 | }) | 167 | }) |
162 | .next() | 168 | .next() |
163 | .and_then(|it| it.into_token()) | 169 | .and_then(|it| it.into_token()) |
164 | .filter(|node| node.kind() == T![,]) | 170 | .filter(|node| node.kind() == delimiter_kind) |
165 | } | 171 | } |
166 | 172 | ||
167 | if let Some(comma_node) = nearby_comma(node, Direction::Prev) { | 173 | let delimiter = match node.kind() { |
168 | return Some(TextRange::from_to(comma_node.text_range().start(), node.text_range().end())); | 174 | TYPE_BOUND => T![+], |
175 | _ => T![,], | ||
176 | }; | ||
177 | if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Prev) { | ||
178 | return Some(TextRange::from_to( | ||
179 | delimiter_node.text_range().start(), | ||
180 | node.text_range().end(), | ||
181 | )); | ||
169 | } | 182 | } |
170 | if let Some(comma_node) = nearby_comma(node, Direction::Next) { | 183 | if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Next) { |
171 | // Include any following whitespace when comma if after list item. | 184 | // Include any following whitespace when delimiter is after list item. |
172 | let final_node = comma_node | 185 | let final_node = delimiter_node |
173 | .next_sibling_or_token() | 186 | .next_sibling_or_token() |
174 | .and_then(|it| it.into_token()) | 187 | .and_then(|it| it.into_token()) |
175 | .filter(|node| is_single_line_ws(node)) | 188 | .filter(|node| is_single_line_ws(node)) |
176 | .unwrap_or(comma_node); | 189 | .unwrap_or(delimiter_node); |
177 | 190 | ||
178 | return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end())); | 191 | return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end())); |
179 | } | 192 | } |
@@ -387,4 +400,53 @@ fn bar(){} | |||
387 | &["foo", "\" fn foo() {\""], | 400 | &["foo", "\" fn foo() {\""], |
388 | ); | 401 | ); |
389 | } | 402 | } |
403 | |||
404 | #[test] | ||
405 | fn test_extend_trait_bounds_list_in_where_clause() { | ||
406 | do_check( | ||
407 | r#" | ||
408 | fn foo<R>() | ||
409 | where | ||
410 | R: req::Request + 'static, | ||
411 | R::Params: DeserializeOwned<|> + panic::UnwindSafe + 'static, | ||
412 | R::Result: Serialize + 'static, | ||
413 | "#, | ||
414 | &[ | ||
415 | "DeserializeOwned", | ||
416 | "DeserializeOwned + ", | ||
417 | "DeserializeOwned + panic::UnwindSafe + 'static", | ||
418 | "R::Params: DeserializeOwned + panic::UnwindSafe + 'static", | ||
419 | "R::Params: DeserializeOwned + panic::UnwindSafe + 'static,", | ||
420 | ], | ||
421 | ); | ||
422 | do_check(r#"fn foo<T>() where T: <|>Copy"#, &["Copy"]); | ||
423 | do_check(r#"fn foo<T>() where T: <|>Copy + Display"#, &["Copy", "Copy + "]); | ||
424 | do_check(r#"fn foo<T>() where T: <|>Copy +Display"#, &["Copy", "Copy +"]); | ||
425 | do_check(r#"fn foo<T>() where T: <|>Copy+Display"#, &["Copy", "Copy+"]); | ||
426 | do_check(r#"fn foo<T>() where T: Copy + <|>Display"#, &["Display", "+ Display"]); | ||
427 | do_check(r#"fn foo<T>() where T: Copy + <|>Display + Sync"#, &["Display", "+ Display"]); | ||
428 | do_check(r#"fn foo<T>() where T: Copy +<|>Display"#, &["Display", "+Display"]); | ||
429 | } | ||
430 | |||
431 | #[test] | ||
432 | fn test_extend_trait_bounds_list_inline() { | ||
433 | do_check(r#"fn foo<T: <|>Copy>() {}"#, &["Copy"]); | ||
434 | do_check(r#"fn foo<T: <|>Copy + Display>() {}"#, &["Copy", "Copy + "]); | ||
435 | do_check(r#"fn foo<T: <|>Copy +Display>() {}"#, &["Copy", "Copy +"]); | ||
436 | do_check(r#"fn foo<T: <|>Copy+Display>() {}"#, &["Copy", "Copy+"]); | ||
437 | do_check(r#"fn foo<T: Copy + <|>Display>() {}"#, &["Display", "+ Display"]); | ||
438 | do_check(r#"fn foo<T: Copy + <|>Display + Sync>() {}"#, &["Display", "+ Display"]); | ||
439 | do_check(r#"fn foo<T: Copy +<|>Display>() {}"#, &["Display", "+Display"]); | ||
440 | do_check( | ||
441 | r#"fn foo<T: Copy<|> + Display, U: Copy>() {}"#, | ||
442 | &[ | ||
443 | "Copy", | ||
444 | "Copy + ", | ||
445 | "Copy + Display", | ||
446 | "T: Copy + Display", | ||
447 | "T: Copy + Display, ", | ||
448 | "<T: Copy + Display, U: Copy>", | ||
449 | ], | ||
450 | ); | ||
451 | } | ||
390 | } | 452 | } |