diff --git a/crates/ra_ide_api/src/extend_selection.rs b/crates/ra_ide_api/src/extend_selection.rs index 602757e92eb..4b7bfc0b11e 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::{ algo::find_covering_element, ast::{self, AstNode, AstToken}, Direction, NodeOrToken, - SyntaxKind::*, + SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T, }; @@ -29,10 +29,12 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken { } } -/// Extend list item selection to include nearby comma and whitespace. +/// Extend list item selection to include nearby delimiter and whitespace. fn extend_list_item(node: &SyntaxNode) -> Option { fn is_single_line_ws(node: &SyntaxToken) -> bool { node.kind() == WHITESPACE && !node.text().contains('\n') } - fn nearby_comma(node: &SyntaxNode, dir: Direction) -> Option { + fn nearby_delimiter( + delimiter_kind: SyntaxKind, + node: &SyntaxNode, + dir: Direction, + ) -> Option { node.siblings_with_tokens(dir) .skip(1) .skip_while(|node| match node { @@ -161,19 +167,26 @@ fn extend_list_item(node: &SyntaxNode) -> Option { }) .next() .and_then(|it| it.into_token()) - .filter(|node| node.kind() == T![,]) + .filter(|node| node.kind() == delimiter_kind) } - if let Some(comma_node) = nearby_comma(node, Direction::Prev) { - return Some(TextRange::from_to(comma_node.text_range().start(), node.text_range().end())); + let delimiter = match node.kind() { + TYPE_BOUND => T![+], + _ => T![,], + }; + if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Prev) { + return Some(TextRange::from_to( + delimiter_node.text_range().start(), + node.text_range().end(), + )); } - if let Some(comma_node) = nearby_comma(node, Direction::Next) { - // Include any following whitespace when comma if after list item. - let final_node = comma_node + if let Some(delimiter_node) = nearby_delimiter(delimiter, node, Direction::Next) { + // Include any following whitespace when delimiter is after list item. + let final_node = delimiter_node .next_sibling_or_token() .and_then(|it| it.into_token()) .filter(|node| is_single_line_ws(node)) - .unwrap_or(comma_node); + .unwrap_or(delimiter_node); return Some(TextRange::from_to(node.text_range().start(), final_node.text_range().end())); } @@ -387,4 +400,53 @@ fn bar(){} &["foo", "\" fn foo() {\""], ); } + + #[test] + fn test_extend_trait_bounds_list_in_where_clause() { + do_check( + r#" +fn foo() + where + R: req::Request + 'static, + R::Params: DeserializeOwned<|> + panic::UnwindSafe + 'static, + R::Result: Serialize + 'static, +"#, + &[ + "DeserializeOwned", + "DeserializeOwned + ", + "DeserializeOwned + panic::UnwindSafe + 'static", + "R::Params: DeserializeOwned + panic::UnwindSafe + 'static", + "R::Params: DeserializeOwned + panic::UnwindSafe + 'static,", + ], + ); + do_check(r#"fn foo() where T: <|>Copy"#, &["Copy"]); + do_check(r#"fn foo() where T: <|>Copy + Display"#, &["Copy", "Copy + "]); + do_check(r#"fn foo() where T: <|>Copy +Display"#, &["Copy", "Copy +"]); + do_check(r#"fn foo() where T: <|>Copy+Display"#, &["Copy", "Copy+"]); + do_check(r#"fn foo() where T: Copy + <|>Display"#, &["Display", "+ Display"]); + do_check(r#"fn foo() where T: Copy + <|>Display + Sync"#, &["Display", "+ Display"]); + do_check(r#"fn foo() where T: Copy +<|>Display"#, &["Display", "+Display"]); + } + + #[test] + fn test_extend_trait_bounds_list_inline() { + do_check(r#"fn fooCopy>() {}"#, &["Copy"]); + do_check(r#"fn fooCopy + Display>() {}"#, &["Copy", "Copy + "]); + do_check(r#"fn fooCopy +Display>() {}"#, &["Copy", "Copy +"]); + do_check(r#"fn fooCopy+Display>() {}"#, &["Copy", "Copy+"]); + do_check(r#"fn fooDisplay>() {}"#, &["Display", "+ Display"]); + do_check(r#"fn fooDisplay + Sync>() {}"#, &["Display", "+ Display"]); + do_check(r#"fn fooDisplay>() {}"#, &["Display", "+Display"]); + do_check( + r#"fn foo + Display, U: Copy>() {}"#, + &[ + "Copy", + "Copy + ", + "Copy + Display", + "T: Copy + Display", + "T: Copy + Display, ", + "", + ], + ); + } }