@@ -11,102 +11,133 @@ use syntax::{
11
11
#[ cfg( test) ]
12
12
use crate :: test_utils:: { check_pattern_is_applicable, check_pattern_is_not_applicable} ;
13
13
14
- pub ( crate ) fn has_trait_parent ( element : SyntaxElement ) -> bool {
15
- not_same_range_ancestor ( element)
16
- . filter ( |it| it. kind ( ) == ASSOC_ITEM_LIST )
17
- . and_then ( |it| it. parent ( ) )
18
- . filter ( |it| it. kind ( ) == TRAIT )
19
- . is_some ( )
20
- }
21
- #[ test]
22
- fn test_has_trait_parent ( ) {
23
- check_pattern_is_applicable ( r"trait A { f$0 }" , has_trait_parent) ;
14
+ /// Direct parent container of the cursor position
15
+ #[ derive( Copy , Clone , Debug , PartialEq , Eq ) ]
16
+ pub ( crate ) enum ImmediateLocation {
17
+ Impl ,
18
+ Trait ,
19
+ RecordField ,
20
+ RefExpr ,
21
+ IdentPat ,
22
+ BlockExpr ,
23
+ ItemList ,
24
+ }
25
+
26
+ pub ( crate ) fn determine_location ( tok : SyntaxToken ) -> Option < ImmediateLocation > {
27
+ // First "expand" the element we are completing to its maximum so that we can check in what
28
+ // context it immediately lies. This for example means if the token is a NameRef at the end of
29
+ // a path, we want to look at where the path is in the tree.
30
+ let node = match tok. parent ( ) . and_then ( ast:: NameLike :: cast) ? {
31
+ ast:: NameLike :: NameRef ( name_ref) => {
32
+ if let Some ( segment) = name_ref. syntax ( ) . parent ( ) . and_then ( ast:: PathSegment :: cast) {
33
+ let p = segment. parent_path ( ) ;
34
+ if p. parent_path ( ) . is_none ( ) {
35
+ p. syntax ( )
36
+ . ancestors ( )
37
+ . take_while ( |it| it. text_range ( ) == p. syntax ( ) . text_range ( ) )
38
+ . last ( ) ?
39
+ } else {
40
+ return None ;
41
+ }
42
+ } else {
43
+ return None ;
44
+ }
45
+ }
46
+ it @ ast:: NameLike :: Name ( _) | it @ ast:: NameLike :: Lifetime ( _) => it. syntax ( ) . clone ( ) ,
47
+ } ;
48
+ let parent = match node. parent ( ) {
49
+ Some ( parent) => parent,
50
+ // SourceFile
51
+ None => {
52
+ return match node. kind ( ) {
53
+ MACRO_ITEMS | SOURCE_FILE => Some ( ImmediateLocation :: ItemList ) ,
54
+ _ => None ,
55
+ }
56
+ }
57
+ } ;
58
+ let res = match_ast ! {
59
+ match parent {
60
+ ast:: IdentPat ( _it) => ImmediateLocation :: IdentPat ,
61
+ ast:: BlockExpr ( _it) => ImmediateLocation :: BlockExpr ,
62
+ ast:: SourceFile ( _it) => ImmediateLocation :: ItemList ,
63
+ ast:: ItemList ( _it) => ImmediateLocation :: ItemList ,
64
+ ast:: RefExpr ( _it) => ImmediateLocation :: RefExpr ,
65
+ ast:: RefPat ( _it) => ImmediateLocation :: RefExpr ,
66
+ ast:: RecordField ( _it) => ImmediateLocation :: RecordField ,
67
+ ast:: AssocItemList ( it) => match it. syntax( ) . parent( ) . map( |it| it. kind( ) ) {
68
+ Some ( IMPL ) => ImmediateLocation :: Impl ,
69
+ Some ( TRAIT ) => ImmediateLocation :: Trait ,
70
+ _ => return None ,
71
+ } ,
72
+ _ => return None ,
73
+ }
74
+ } ;
75
+ Some ( res)
24
76
}
25
77
26
- pub ( crate ) fn has_impl_parent ( element : SyntaxElement ) -> bool {
27
- not_same_range_ancestor ( element)
28
- . filter ( |it| it. kind ( ) == ASSOC_ITEM_LIST )
29
- . and_then ( |it| it. parent ( ) )
30
- . filter ( |it| it. kind ( ) == IMPL )
31
- . is_some ( )
32
- }
33
- #[ test]
34
- fn test_has_impl_parent ( ) {
35
- check_pattern_is_applicable ( r"impl A { f$0 }" , has_impl_parent) ;
78
+ #[ cfg( test) ]
79
+ fn check_location ( code : & str , loc : ImmediateLocation ) {
80
+ check_pattern_is_applicable ( code, |e| {
81
+ assert_eq ! ( determine_location( e. into_token( ) . expect( "Expected a token" ) ) , Some ( loc) ) ;
82
+ true
83
+ } ) ;
36
84
}
37
85
38
- pub ( crate ) fn inside_impl_trait_block ( element : SyntaxElement ) -> bool {
39
- // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
40
- // where we only check the first parent with different text range.
41
- element
42
- . ancestors ( )
43
- . find ( |it| it. kind ( ) == IMPL )
44
- . map ( |it| ast:: Impl :: cast ( it) . unwrap ( ) )
45
- . map ( |it| it. trait_ ( ) . is_some ( ) )
46
- . unwrap_or ( false )
47
- }
48
86
#[ test]
49
- fn test_inside_impl_trait_block ( ) {
50
- check_pattern_is_applicable ( r"impl Foo for Bar { f$0 }" , inside_impl_trait_block) ;
51
- check_pattern_is_applicable ( r"impl Foo for Bar { fn f$0 }" , inside_impl_trait_block) ;
52
- check_pattern_is_not_applicable ( r"impl A { f$0 }" , inside_impl_trait_block) ;
53
- check_pattern_is_not_applicable ( r"impl A { fn f$0 }" , inside_impl_trait_block) ;
87
+ fn test_has_trait_parent ( ) {
88
+ check_location ( r"trait A { f$0 }" , ImmediateLocation :: Trait ) ;
54
89
}
55
90
56
- pub ( crate ) fn has_field_list_parent ( element : SyntaxElement ) -> bool {
57
- not_same_range_ancestor ( element) . filter ( |it| it. kind ( ) == RECORD_FIELD_LIST ) . is_some ( )
91
+ #[ test]
92
+ fn test_has_impl_parent ( ) {
93
+ check_location ( r"impl A { f$0 }" , ImmediateLocation :: Impl ) ;
58
94
}
59
95
#[ test]
60
96
fn test_has_field_list_parent ( ) {
61
- check_pattern_is_applicable ( r"struct Foo { f$0 }" , has_field_list_parent ) ;
62
- check_pattern_is_applicable ( r"struct Foo { f$0 pub f: i32}" , has_field_list_parent ) ;
97
+ check_location ( r"struct Foo { f$0 }" , ImmediateLocation :: RecordField ) ;
98
+ check_location ( r"struct Foo { f$0 pub f: i32}" , ImmediateLocation :: RecordField ) ;
63
99
}
64
100
65
- pub ( crate ) fn has_block_expr_parent ( element : SyntaxElement ) -> bool {
66
- not_same_range_ancestor ( element) . filter ( |it| it. kind ( ) == BLOCK_EXPR ) . is_some ( )
67
- }
68
101
#[ test]
69
102
fn test_has_block_expr_parent ( ) {
70
- check_pattern_is_applicable ( r"fn my_fn() { let a = 2; f$0 }" , has_block_expr_parent ) ;
103
+ check_location ( r"fn my_fn() { let a = 2; f$0 }" , ImmediateLocation :: BlockExpr ) ;
71
104
}
72
105
73
- pub ( crate ) fn has_bind_pat_parent ( element : SyntaxElement ) -> bool {
74
- element. ancestors ( ) . any ( |it| it. kind ( ) == IDENT_PAT )
106
+ #[ test]
107
+ fn test_has_ident_pat_parent ( ) {
108
+ check_location ( r"fn my_fn(m$0) {}" , ImmediateLocation :: IdentPat ) ;
109
+ check_location ( r"fn my_fn() { let m$0 }" , ImmediateLocation :: IdentPat ) ;
110
+ check_location ( r"fn my_fn(&m$0) {}" , ImmediateLocation :: IdentPat ) ;
111
+ check_location ( r"fn my_fn() { let &m$0 }" , ImmediateLocation :: IdentPat ) ;
75
112
}
76
113
77
114
#[ test]
78
- fn test_has_bind_pat_parent ( ) {
79
- check_pattern_is_applicable ( r"fn my_fn(m$0) {}" , has_bind_pat_parent) ;
80
- check_pattern_is_applicable ( r"fn my_fn() { let m$0 }" , has_bind_pat_parent) ;
115
+ fn test_has_ref_expr_parent ( ) {
116
+ check_location ( r"fn my_fn() { let x = &m$0 foo; }" , ImmediateLocation :: RefExpr ) ;
81
117
}
82
118
83
- pub ( crate ) fn has_ref_parent ( element : SyntaxElement ) -> bool {
84
- not_same_range_ancestor ( element)
85
- . filter ( |it| it. kind ( ) == REF_PAT || it. kind ( ) == REF_EXPR )
86
- . is_some ( )
87
- }
88
119
#[ test]
89
- fn test_has_ref_parent ( ) {
90
- check_pattern_is_applicable ( r"fn my_fn(&m$0) {} " , has_ref_parent ) ;
91
- check_pattern_is_applicable ( r"fn my() { let &m $0 }" , has_ref_parent ) ;
120
+ fn test_has_item_list_or_source_file_parent ( ) {
121
+ check_location ( r"i$0 " , ImmediateLocation :: ItemList ) ;
122
+ check_location ( r"mod foo { f $0 }" , ImmediateLocation :: ItemList ) ;
92
123
}
93
124
94
- pub ( crate ) fn has_item_list_or_source_file_parent ( element : SyntaxElement ) -> bool {
95
- let it = element
125
+ pub ( crate ) fn inside_impl_trait_block ( element : SyntaxElement ) -> bool {
126
+ // Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
127
+ // where we only check the first parent with different text range.
128
+ element
96
129
. ancestors ( )
97
- . take_while ( |it| it. text_range ( ) == element. text_range ( ) )
98
- . last ( )
99
- . map ( |it| ( it. kind ( ) , it. parent ( ) ) ) ;
100
- match it {
101
- Some ( ( _, Some ( it) ) ) => it. kind ( ) == SOURCE_FILE || it. kind ( ) == ITEM_LIST ,
102
- Some ( ( MACRO_ITEMS , None ) | ( SOURCE_FILE , None ) ) => true ,
103
- _ => false ,
104
- }
130
+ . find ( |it| it. kind ( ) == IMPL )
131
+ . map ( |it| ast:: Impl :: cast ( it) . unwrap ( ) )
132
+ . map ( |it| it. trait_ ( ) . is_some ( ) )
133
+ . unwrap_or ( false )
105
134
}
106
135
#[ test]
107
- fn test_has_item_list_or_source_file_parent ( ) {
108
- check_pattern_is_applicable ( r"i$0" , has_item_list_or_source_file_parent) ;
109
- check_pattern_is_applicable ( r"mod foo { f$0 }" , has_item_list_or_source_file_parent) ;
136
+ fn test_inside_impl_trait_block ( ) {
137
+ check_pattern_is_applicable ( r"impl Foo for Bar { f$0 }" , inside_impl_trait_block) ;
138
+ check_pattern_is_applicable ( r"impl Foo for Bar { fn f$0 }" , inside_impl_trait_block) ;
139
+ check_pattern_is_not_applicable ( r"impl A { f$0 }" , inside_impl_trait_block) ;
140
+ check_pattern_is_not_applicable ( r"impl A { fn f$0 }" , inside_impl_trait_block) ;
110
141
}
111
142
112
143
pub ( crate ) fn is_match_arm ( element : SyntaxElement ) -> bool {
@@ -166,12 +197,8 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
166
197
. is_some ( )
167
198
}
168
199
169
- fn not_same_range_ancestor ( element : SyntaxElement ) -> Option < SyntaxNode > {
170
- element
171
- . ancestors ( )
172
- . take_while ( |it| it. text_range ( ) == element. text_range ( ) )
173
- . last ( )
174
- . and_then ( |it| it. parent ( ) )
200
+ pub ( crate ) fn not_same_range_ancestor ( element : SyntaxElement ) -> Option < SyntaxNode > {
201
+ element. ancestors ( ) . skip_while ( |it| it. text_range ( ) == element. text_range ( ) ) . next ( )
175
202
}
176
203
177
204
fn previous_non_trivia_token ( token : SyntaxToken ) -> Option < SyntaxToken > {
0 commit comments