diff options
Diffstat (limited to 'crates/ra_ide/src/completion/patterns.rs')
-rw-r--r-- | crates/ra_ide/src/completion/patterns.rs | 117 |
1 files changed, 117 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..b55f23fbe --- /dev/null +++ b/crates/ra_ide/src/completion/patterns.rs | |||
@@ -0,0 +1,117 @@ | |||
1 | use ra_syntax::{ | ||
2 | algo::non_trivia_sibling, | ||
3 | ast::{self, LoopBodyOwner}, | ||
4 | match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, | ||
5 | SyntaxKind::*, | ||
6 | SyntaxNode, | ||
7 | }; | ||
8 | |||
9 | pub(crate) fn inside_impl(element: SyntaxElement) -> bool { | ||
10 | let node = match element { | ||
11 | NodeOrToken::Node(node) => node, | ||
12 | NodeOrToken::Token(token) => token.parent(), | ||
13 | }; | ||
14 | node.ancestors().find(|it| it.kind() == IMPL_DEF).is_some() | ||
15 | } | ||
16 | |||
17 | pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { | ||
18 | let node = match element { | ||
19 | NodeOrToken::Node(node) => node, | ||
20 | NodeOrToken::Token(token) => token.parent(), | ||
21 | }; | ||
22 | node.ancestors().find(|it| it.kind() == BIND_PAT).is_some() | ||
23 | } | ||
24 | |||
25 | pub(crate) fn has_ref_pat_parent(element: SyntaxElement) -> bool { | ||
26 | let node = match element { | ||
27 | NodeOrToken::Node(node) => node, | ||
28 | NodeOrToken::Token(token) => token.parent(), | ||
29 | }; | ||
30 | node.ancestors().find(|it| it.kind() == REF_PAT).is_some() | ||
31 | } | ||
32 | |||
33 | pub(crate) fn goes_after_unsafe(element: SyntaxElement) -> bool { | ||
34 | if let Some(token) = previous_non_triva_element(element).and_then(|it| it.into_token()) { | ||
35 | if token.kind() == UNSAFE_KW { | ||
36 | return true; | ||
37 | } | ||
38 | } | ||
39 | false | ||
40 | } | ||
41 | |||
42 | pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { | ||
43 | not_same_range_parent(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() | ||
44 | } | ||
45 | |||
46 | pub(crate) fn has_item_list_parent(element: SyntaxElement) -> bool { | ||
47 | not_same_range_parent(element).filter(|it| it.kind() == ITEM_LIST).is_some() | ||
48 | } | ||
49 | |||
50 | pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { | ||
51 | let leaf = match element { | ||
52 | NodeOrToken::Node(node) => node, | ||
53 | NodeOrToken::Token(token) => token.parent(), | ||
54 | }; | ||
55 | for node in leaf.ancestors() { | ||
56 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { | ||
57 | break; | ||
58 | } | ||
59 | let loop_body = match_ast! { | ||
60 | match node { | ||
61 | ast::ForExpr(it) => it.loop_body(), | ||
62 | ast::WhileExpr(it) => it.loop_body(), | ||
63 | ast::LoopExpr(it) => it.loop_body(), | ||
64 | _ => None, | ||
65 | } | ||
66 | }; | ||
67 | if let Some(body) = loop_body { | ||
68 | if body.syntax().text_range().contains_range(leaf.text_range()) { | ||
69 | return true; | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | false | ||
74 | } | ||
75 | |||
76 | fn not_same_range_parent(element: SyntaxElement) -> Option<SyntaxNode> { | ||
77 | let node = match element { | ||
78 | NodeOrToken::Node(node) => node, | ||
79 | NodeOrToken::Token(token) => token.parent(), | ||
80 | }; | ||
81 | let range = node.text_range(); | ||
82 | node.ancestors().take_while(|it| it.text_range() == range).last().and_then(|it| it.parent()) | ||
83 | } | ||
84 | |||
85 | fn previous_non_triva_element(element: SyntaxElement) -> Option<SyntaxElement> { | ||
86 | // trying to get first non triva sibling if we have one | ||
87 | let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev); | ||
88 | let mut wrapped = if let Some(sibling) = token_sibling { | ||
89 | sibling | ||
90 | } else { | ||
91 | // if not trying to find first ancestor which has such a sibling | ||
92 | let node = match element { | ||
93 | NodeOrToken::Node(node) => node, | ||
94 | NodeOrToken::Token(token) => token.parent(), | ||
95 | }; | ||
96 | let range = node.text_range(); | ||
97 | let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?; | ||
98 | let prev_sibling_node = top_node.ancestors().find(|it| { | ||
99 | non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some() | ||
100 | })?; | ||
101 | non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)? | ||
102 | }; | ||
103 | //I think you can avoid this loop if you use SyntaxToken::prev_token -- unlike prev_sibling_or_token, it works across parents. | ||
104 | // traversing the tree down to get the last token or node, i.e. the closest one | ||
105 | loop { | ||
106 | if let Some(token) = wrapped.as_token() { | ||
107 | return Some(NodeOrToken::Token(token.clone())); | ||
108 | } else { | ||
109 | let new = wrapped.as_node().and_then(|n| n.last_child_or_token()); | ||
110 | if new.is_some() { | ||
111 | wrapped = new.unwrap().clone(); | ||
112 | } else { | ||
113 | return Some(wrapped); | ||
114 | } | ||
115 | } | ||
116 | } | ||
117 | } | ||