@@ -4,12 +4,19 @@ use syntax::{
4
4
algo:: non_trivia_sibling,
5
5
ast:: { self , LoopBodyOwner } ,
6
6
match_ast, AstNode , Direction , NodeOrToken , SyntaxElement ,
7
- SyntaxKind :: { self , * } ,
7
+ SyntaxKind :: * ,
8
8
SyntaxNode , SyntaxToken , T ,
9
9
} ;
10
10
11
11
#[ cfg( test) ]
12
12
use crate :: test_utils:: { check_pattern_is_applicable, check_pattern_is_not_applicable} ;
13
+ /// Direct parent container of the cursor position
14
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
15
+ pub ( crate ) enum ImmediatePrevSibling {
16
+ IfExpr ,
17
+ TraitDefName ,
18
+ ImplDefType ,
19
+ }
13
20
14
21
/// Direct parent container of the cursor position
15
22
#[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
@@ -24,35 +31,61 @@ pub(crate) enum ImmediateLocation {
24
31
ItemList ,
25
32
}
26
33
27
- pub ( crate ) fn determine_location ( name_like : & ast:: NameLike ) -> Option < ImmediateLocation > {
28
- // First walk the element we are completing up to its highest node that has the same text range
29
- // as the element so that we can check in what context it immediately lies. We only do this for
30
- // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically.
31
- // We only wanna do this if the NameRef is the last segment of the path.
32
- let node = match name_like {
33
- ast:: NameLike :: NameRef ( name_ref) => {
34
- if let Some ( segment) = name_ref. syntax ( ) . parent ( ) . and_then ( ast:: PathSegment :: cast) {
35
- let p = segment. parent_path ( ) ;
36
- if p. parent_path ( ) . is_none ( ) {
37
- p. syntax ( )
38
- . ancestors ( )
39
- . take_while ( |it| it. text_range ( ) == p. syntax ( ) . text_range ( ) )
40
- . last ( ) ?
41
- } else {
42
- return None ;
34
+ pub ( crate ) fn determine_prev_sibling ( name_like : & ast:: NameLike ) -> Option < ImmediatePrevSibling > {
35
+ let node = maximize_name_ref ( name_like) ?;
36
+ let node = match node. parent ( ) . and_then ( ast:: MacroCall :: cast) {
37
+ // When a path is being typed after the name of a trait/type of an impl it is being
38
+ // parsed as a macro, so when the trait/impl has a block following it an we are between the
39
+ // name and block the macro will attach the block to itself so maximizing fails to take
40
+ // that into account
41
+ // FIXME path expr and statement have a similar problem with attrs
42
+ Some ( call)
43
+ if call. excl_token ( ) . is_none ( )
44
+ && call. token_tree ( ) . map_or ( false , |t| t. l_curly_token ( ) . is_some ( ) )
45
+ && call. semicolon_token ( ) . is_none ( ) =>
46
+ {
47
+ call. syntax ( ) . clone ( )
48
+ }
49
+ _ => node,
50
+ } ;
51
+ let prev_sibling = non_trivia_sibling ( node. into ( ) , Direction :: Prev ) ?. into_node ( ) ?;
52
+ let res = match_ast ! {
53
+ match prev_sibling {
54
+ ast:: ExprStmt ( it) => {
55
+ let node = it. expr( ) ?. syntax( ) . clone( ) ;
56
+ match_ast! {
57
+ match node {
58
+ ast:: IfExpr ( _it) => ImmediatePrevSibling :: IfExpr ,
59
+ _ => return None ,
60
+ }
43
61
}
44
- } else {
45
- return None ;
46
- }
62
+ } ,
63
+ ast:: Trait ( it) => if it. assoc_item_list( ) . is_none( ) {
64
+ ImmediatePrevSibling :: TraitDefName
65
+ } else {
66
+ return None
67
+ } ,
68
+ ast:: Impl ( it) => if it. assoc_item_list( ) . is_none( )
69
+ && ( it. for_token( ) . is_none( ) || it. self_ty( ) . is_some( ) ) {
70
+ ImmediatePrevSibling :: ImplDefType
71
+ } else {
72
+ return None
73
+ } ,
74
+ _ => return None ,
47
75
}
48
- it @ ast:: NameLike :: Name ( _) | it @ ast:: NameLike :: Lifetime ( _) => it. syntax ( ) . clone ( ) ,
49
76
} ;
77
+ Some ( res)
78
+ }
79
+
80
+ pub ( crate ) fn determine_location ( name_like : & ast:: NameLike ) -> Option < ImmediateLocation > {
81
+ let node = maximize_name_ref ( name_like) ?;
50
82
let parent = match node. parent ( ) {
51
83
Some ( parent) => match ast:: MacroCall :: cast ( parent. clone ( ) ) {
52
84
// When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call.
53
85
// This is usually fine as the node expansion code above already accounts for that with
54
86
// the ancestors call, but there is one exception to this which is that when an attribute
55
87
// precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ.
88
+ // FIXME path expr and statement have a similar problem
56
89
Some ( call)
57
90
if call. excl_token ( ) . is_none ( )
58
91
&& call. token_tree ( ) . is_none ( )
@@ -90,6 +123,32 @@ pub(crate) fn determine_location(name_like: &ast::NameLike) -> Option<ImmediateL
90
123
Some ( res)
91
124
}
92
125
126
+ fn maximize_name_ref ( name_like : & ast:: NameLike ) -> Option < SyntaxNode > {
127
+ // First walk the element we are completing up to its highest node that has the same text range
128
+ // as the element so that we can check in what context it immediately lies. We only do this for
129
+ // NameRef -> Path as that's the only thing that makes sense to being "expanded" semantically.
130
+ // We only wanna do this if the NameRef is the last segment of the path.
131
+ let node = match name_like {
132
+ ast:: NameLike :: NameRef ( name_ref) => {
133
+ if let Some ( segment) = name_ref. syntax ( ) . parent ( ) . and_then ( ast:: PathSegment :: cast) {
134
+ let p = segment. parent_path ( ) ;
135
+ if p. parent_path ( ) . is_none ( ) {
136
+ p. syntax ( )
137
+ . ancestors ( )
138
+ . take_while ( |it| it. text_range ( ) == p. syntax ( ) . text_range ( ) )
139
+ . last ( ) ?
140
+ } else {
141
+ return None ;
142
+ }
143
+ } else {
144
+ return None ;
145
+ }
146
+ }
147
+ it @ ast:: NameLike :: Name ( _) | it @ ast:: NameLike :: Lifetime ( _) => it. syntax ( ) . clone ( ) ,
148
+ } ;
149
+ Some ( node)
150
+ }
151
+
93
152
#[ cfg( test) ]
94
153
fn check_location ( code : & str , loc : ImmediateLocation ) {
95
154
check_pattern_is_applicable ( code, |e| {
@@ -192,17 +251,34 @@ fn test_for_is_prev2() {
192
251
check_pattern_is_applicable ( r"for i i$0" , for_is_prev2) ;
193
252
}
194
253
195
- pub ( crate ) fn has_prev_sibling ( element : SyntaxElement , kind : SyntaxKind ) -> bool {
196
- previous_sibling_or_ancestor_sibling ( element) . filter ( |it| it. kind ( ) == kind) . is_some ( )
254
+ #[ cfg( test) ]
255
+ fn check_prev_sibling ( code : & str , sibling : impl Into < Option < ImmediatePrevSibling > > ) {
256
+ check_pattern_is_applicable ( code, |e| {
257
+ let name = & e. parent ( ) . and_then ( ast:: NameLike :: cast) . expect ( "Expected a namelike" ) ;
258
+ assert_eq ! ( determine_prev_sibling( name) , sibling. into( ) ) ;
259
+ true
260
+ } ) ;
197
261
}
262
+
198
263
#[ test]
199
264
fn test_has_impl_as_prev_sibling ( ) {
200
- check_pattern_is_applicable ( r"impl A w$0 {}" , |it| has_prev_sibling ( it, IMPL ) ) ;
265
+ check_prev_sibling ( r"impl A w$0 " , ImmediatePrevSibling :: ImplDefType ) ;
266
+ check_prev_sibling ( r"impl A w$0 {}" , ImmediatePrevSibling :: ImplDefType ) ;
267
+ check_prev_sibling ( r"impl A for A w$0 " , ImmediatePrevSibling :: ImplDefType ) ;
268
+ check_prev_sibling ( r"impl A for A w$0 {}" , ImmediatePrevSibling :: ImplDefType ) ;
269
+ check_prev_sibling ( r"impl A for w$0 {}" , None ) ;
270
+ check_prev_sibling ( r"impl A for w$0" , None ) ;
201
271
}
202
272
203
273
#[ test]
204
274
fn test_has_trait_as_prev_sibling ( ) {
205
- check_pattern_is_applicable ( r"trait A w$0 {}" , |it| has_prev_sibling ( it, TRAIT ) ) ;
275
+ check_prev_sibling ( r"trait A w$0 " , ImmediatePrevSibling :: TraitDefName ) ;
276
+ check_prev_sibling ( r"trait A w$0 {}" , ImmediatePrevSibling :: TraitDefName ) ;
277
+ }
278
+
279
+ #[ test]
280
+ fn test_has_if_expr_as_prev_sibling ( ) {
281
+ check_prev_sibling ( r"fn foo() { if true {} w$0" , ImmediatePrevSibling :: IfExpr ) ;
206
282
}
207
283
208
284
pub ( crate ) fn is_in_loop_body ( element : SyntaxElement ) -> bool {
0 commit comments