@@ -5,7 +5,7 @@ use ra_syntax::{
5
5
algo:: find_covering_element,
6
6
ast:: { self , AstNode , AstToken } ,
7
7
Direction , NodeOrToken ,
8
- SyntaxKind :: * ,
8
+ SyntaxKind :: { self , * } ,
9
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
29
USE_TREE_LIST ,
30
30
TYPE_PARAM_LIST ,
31
31
TYPE_ARG_LIST ,
32
+ TYPE_BOUND_LIST ,
32
33
PARAM_LIST ,
33
34
ARG_LIST ,
34
35
ARRAY_EXPR ,
35
36
TUPLE_EXPR ,
37
+ WHERE_CLAUSE ,
36
38
] ;
37
39
38
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
152
fn extend_list_item ( node : & SyntaxNode ) -> Option < TextRange > {
151
153
fn is_single_line_ws ( node : & SyntaxToken ) -> bool {
152
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
162
node. siblings_with_tokens ( dir)
157
163
. skip ( 1 )
158
164
. skip_while ( |node| match node {
@@ -161,19 +167,26 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
161
167
} )
162
168
. next ( )
163
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 ) {
168
- return Some ( TextRange :: from_to ( comma_node. text_range ( ) . start ( ) , node. text_range ( ) . end ( ) ) ) ;
173
+ let delimiter = match node. kind ( ) {
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 ) {
171
- // Include any following whitespace when comma if after list item.
172
- let final_node = comma_node
183
+ if let Some ( delimiter_node ) = nearby_delimiter ( delimiter , node, Direction :: Next ) {
184
+ // Include any following whitespace when delimiter is after list item.
185
+ let final_node = delimiter_node
173
186
. next_sibling_or_token ( )
174
187
. and_then ( |it| it. into_token ( ) )
175
188
. filter ( |node| is_single_line_ws ( node) )
176
- . unwrap_or ( comma_node ) ;
189
+ . unwrap_or ( delimiter_node ) ;
177
190
178
191
return Some ( TextRange :: from_to ( node. text_range ( ) . start ( ) , final_node. text_range ( ) . end ( ) ) ) ;
179
192
}
@@ -387,4 +400,53 @@ fn bar(){}
387
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
}
0 commit comments