aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/patterns.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/patterns.rs')
-rw-r--r--crates/completion/src/patterns.rs249
1 files changed, 249 insertions, 0 deletions
diff --git a/crates/completion/src/patterns.rs b/crates/completion/src/patterns.rs
new file mode 100644
index 000000000..b0f35f9bf
--- /dev/null
+++ b/crates/completion/src/patterns.rs
@@ -0,0 +1,249 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2
3use syntax::{
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken,
9};
10
11#[cfg(test)]
12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
13
14pub(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]
22fn test_has_trait_parent() {
23 check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent);
24}
25
26pub(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]
34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent);
36}
37
38pub(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#[test]
49fn test_inside_impl_trait_block() {
50 check_pattern_is_applicable(r"impl Foo for Bar { f<|> }", inside_impl_trait_block);
51 check_pattern_is_applicable(r"impl Foo for Bar { fn f<|> }", inside_impl_trait_block);
52 check_pattern_is_not_applicable(r"impl A { f<|> }", inside_impl_trait_block);
53 check_pattern_is_not_applicable(r"impl A { fn f<|> }", inside_impl_trait_block);
54}
55
56pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool {
57 not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some()
58}
59#[test]
60fn test_has_field_list_parent() {
61 check_pattern_is_applicable(r"struct Foo { f<|> }", has_field_list_parent);
62 check_pattern_is_applicable(r"struct Foo { f<|> pub f: i32}", has_field_list_parent);
63}
64
65pub(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#[test]
69fn test_has_block_expr_parent() {
70 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent);
71}
72
73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
74 element.ancestors().find(|it| it.kind() == IDENT_PAT).is_some()
75}
76#[test]
77fn test_has_bind_pat_parent() {
78 check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent);
79 check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent);
80}
81
82pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
83 not_same_range_ancestor(element)
84 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
85 .is_some()
86}
87#[test]
88fn test_has_ref_parent() {
89 check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent);
90 check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent);
91}
92
93pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
94 let ancestor = not_same_range_ancestor(element);
95 if !ancestor.is_some() {
96 return true;
97 }
98 ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
99}
100#[test]
101fn test_has_item_list_or_source_file_parent() {
102 check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent);
103 check_pattern_is_applicable(r"mod foo { f<|> }", has_item_list_or_source_file_parent);
104}
105
106pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
107 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
108 && previous_sibling_or_ancestor_sibling(element)
109 .and_then(|it| it.into_token())
110 .filter(|it| it.kind() == FAT_ARROW)
111 .is_some()
112}
113#[test]
114fn test_is_match_arm() {
115 check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm);
116}
117
118pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
119 element
120 .into_token()
121 .and_then(|it| previous_non_trivia_token(it))
122 .filter(|it| it.kind() == UNSAFE_KW)
123 .is_some()
124}
125#[test]
126fn test_unsafe_is_prev() {
127 check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev);
128}
129
130pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
131 element
132 .into_token()
133 .and_then(|it| previous_non_trivia_token(it))
134 .filter(|it| it.kind() == IF_KW)
135 .is_some()
136}
137
138pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool {
139 element
140 .into_token()
141 .and_then(|it| previous_non_trivia_token(it))
142 .filter(|it| it.kind() == FN_KW)
143 .is_some()
144}
145#[test]
146fn test_fn_is_prev() {
147 check_pattern_is_applicable(r"fn l<|>", fn_is_prev);
148}
149
150/// Check if the token previous to the previous one is `for`.
151/// For example, `for _ i<|>` => true.
152pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool {
153 element
154 .into_token()
155 .and_then(|it| previous_non_trivia_token(it))
156 .and_then(|it| previous_non_trivia_token(it))
157 .filter(|it| it.kind() == FOR_KW)
158 .is_some()
159}
160#[test]
161fn test_for_is_prev2() {
162 check_pattern_is_applicable(r"for i i<|>", for_is_prev2);
163}
164
165#[test]
166fn test_if_is_prev() {
167 check_pattern_is_applicable(r"if l<|>", if_is_prev);
168}
169
170pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
171 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT).is_some()
172}
173#[test]
174fn test_has_trait_as_prev_sibling() {
175 check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling);
176}
177
178pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
179 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL).is_some()
180}
181#[test]
182fn test_has_impl_as_prev_sibling() {
183 check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling);
184}
185
186pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
187 let leaf = match element {
188 NodeOrToken::Node(node) => node,
189 NodeOrToken::Token(token) => token.parent(),
190 };
191 for node in leaf.ancestors() {
192 if node.kind() == FN || node.kind() == CLOSURE_EXPR {
193 break;
194 }
195 let loop_body = match_ast! {
196 match node {
197 ast::ForExpr(it) => it.loop_body(),
198 ast::WhileExpr(it) => it.loop_body(),
199 ast::LoopExpr(it) => it.loop_body(),
200 _ => None,
201 }
202 };
203 if let Some(body) = loop_body {
204 if body.syntax().text_range().contains_range(leaf.text_range()) {
205 return true;
206 }
207 }
208 }
209 false
210}
211
212fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
213 element
214 .ancestors()
215 .take_while(|it| it.text_range() == element.text_range())
216 .last()
217 .and_then(|it| it.parent())
218}
219
220fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
221 let mut token = token.prev_token();
222 while let Some(inner) = token.clone() {
223 if !inner.kind().is_trivia() {
224 return Some(inner);
225 } else {
226 token = inner.prev_token();
227 }
228 }
229 None
230}
231
232fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
233 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
234 if let Some(sibling) = token_sibling {
235 Some(sibling)
236 } else {
237 // if not trying to find first ancestor which has such a sibling
238 let node = match element {
239 NodeOrToken::Node(node) => node,
240 NodeOrToken::Token(token) => token.parent(),
241 };
242 let range = node.text_range();
243 let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?;
244 let prev_sibling_node = top_node.ancestors().find(|it| {
245 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
246 })?;
247 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
248 }
249}