aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/completion/patterns.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/completion/patterns.rs')
-rw-r--r--crates/ra_ide/src/completion/patterns.rs206
1 files changed, 206 insertions, 0 deletions
diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs
new file mode 100644
index 000000000..464032cb4
--- /dev/null
+++ b/crates/ra_ide/src/completion/patterns.rs
@@ -0,0 +1,206 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2
3use ra_syntax::{
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken,
9};
10
11pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
12 not_same_range_ancestor(element)
13 .filter(|it| it.kind() == ITEM_LIST)
14 .and_then(|it| it.parent())
15 .filter(|it| it.kind() == TRAIT_DEF)
16 .is_some()
17}
18
19pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
20 not_same_range_ancestor(element)
21 .filter(|it| it.kind() == ITEM_LIST)
22 .and_then(|it| it.parent())
23 .filter(|it| it.kind() == IMPL_DEF)
24 .is_some()
25}
26
27pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
28 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
29}
30
31pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
32 element.ancestors().find(|it| it.kind() == BIND_PAT).is_some()
33}
34
35pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
36 not_same_range_ancestor(element)
37 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
38 .is_some()
39}
40
41pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
42 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
43 && previous_sibling_or_ancestor_sibling(element)
44 .and_then(|it| it.into_token())
45 .filter(|it| it.kind() == FAT_ARROW)
46 .is_some()
47}
48
49pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
50 element
51 .into_token()
52 .and_then(|it| previous_non_trivia_token(it))
53 .filter(|it| it.kind() == UNSAFE_KW)
54 .is_some()
55}
56
57pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
58 element
59 .into_token()
60 .and_then(|it| previous_non_trivia_token(it))
61 .filter(|it| it.kind() == IF_KW)
62 .is_some()
63}
64
65pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
66 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some()
67}
68
69pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
70 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL_DEF).is_some()
71}
72
73pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
74 let leaf = match element {
75 NodeOrToken::Node(node) => node,
76 NodeOrToken::Token(token) => token.parent(),
77 };
78 for node in leaf.ancestors() {
79 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
80 break;
81 }
82 let loop_body = match_ast! {
83 match node {
84 ast::ForExpr(it) => it.loop_body(),
85 ast::WhileExpr(it) => it.loop_body(),
86 ast::LoopExpr(it) => it.loop_body(),
87 _ => None,
88 }
89 };
90 if let Some(body) = loop_body {
91 if body.syntax().text_range().contains_range(leaf.text_range()) {
92 return true;
93 }
94 }
95 }
96 false
97}
98
99fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
100 element
101 .ancestors()
102 .take_while(|it| it.text_range() == element.text_range())
103 .last()
104 .and_then(|it| it.parent())
105}
106
107fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
108 let mut token = token.prev_token();
109 while let Some(inner) = token.clone() {
110 if !inner.kind().is_trivia() {
111 return Some(inner);
112 } else {
113 token = inner.prev_token();
114 }
115 }
116 None
117}
118
119fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
120 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
121 if let Some(sibling) = token_sibling {
122 Some(sibling)
123 } else {
124 // if not trying to find first ancestor which has such a sibling
125 let node = match element {
126 NodeOrToken::Node(node) => node,
127 NodeOrToken::Token(token) => token.parent(),
128 };
129 let range = node.text_range();
130 let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?;
131 let prev_sibling_node = top_node.ancestors().find(|it| {
132 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
133 })?;
134 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::{
141 has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent,
142 has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, is_match_arm,
143 unsafe_is_prev,
144 };
145 use crate::completion::test_utils::check_pattern_is_applicable;
146
147 #[test]
148 fn test_unsafe_is_prev() {
149 check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev);
150 }
151
152 #[test]
153 fn test_if_is_prev() {
154 check_pattern_is_applicable(r"if l<|>", if_is_prev);
155 }
156
157 #[test]
158 fn test_has_trait_parent() {
159 check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent);
160 }
161
162 #[test]
163 fn test_has_impl_parent() {
164 check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent);
165 }
166
167 #[test]
168 fn test_has_trait_as_prev_sibling() {
169 check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling);
170 }
171
172 #[test]
173 fn test_has_impl_as_prev_sibling() {
174 check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling);
175 }
176
177 #[test]
178 fn test_parent_block_expr() {
179 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent);
180 }
181
182 #[test]
183 fn test_has_ref_pat_parent_in_func_parameters() {
184 check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent);
185 }
186
187 #[test]
188 fn test_has_ref_pat_parent_in_let_statement() {
189 check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent);
190 }
191
192 #[test]
193 fn test_has_bind_pat_parent_in_func_parameters() {
194 check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent);
195 }
196
197 #[test]
198 fn test_has_bind_pat_parent_in_let_statement() {
199 check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent);
200 }
201
202 #[test]
203 fn test_is_match_arm() {
204 check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm);
205 }
206}