diff options
Diffstat (limited to 'crates/ra_syntax/src/algo/mod.rs')
-rw-r--r-- | crates/ra_syntax/src/algo/mod.rs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/algo/mod.rs b/crates/ra_syntax/src/algo/mod.rs new file mode 100644 index 000000000..7287f5bb2 --- /dev/null +++ b/crates/ra_syntax/src/algo/mod.rs | |||
@@ -0,0 +1,129 @@ | |||
1 | pub mod walk; | ||
2 | pub mod visit; | ||
3 | |||
4 | use { | ||
5 | SyntaxNodeRef, TextUnit, TextRange, | ||
6 | text_utils::{contains_offset_nonstrict, is_subrange}, | ||
7 | }; | ||
8 | |||
9 | pub fn find_leaf_at_offset(node: SyntaxNodeRef, offset: TextUnit) -> LeafAtOffset { | ||
10 | let range = node.range(); | ||
11 | assert!( | ||
12 | contains_offset_nonstrict(range, offset), | ||
13 | "Bad offset: range {:?} offset {:?}", range, offset | ||
14 | ); | ||
15 | if range.is_empty() { | ||
16 | return LeafAtOffset::None; | ||
17 | } | ||
18 | |||
19 | if node.is_leaf() { | ||
20 | return LeafAtOffset::Single(node); | ||
21 | } | ||
22 | |||
23 | let mut children = node.children() | ||
24 | .filter(|child| { | ||
25 | let child_range = child.range(); | ||
26 | !child_range.is_empty() && contains_offset_nonstrict(child_range, offset) | ||
27 | }); | ||
28 | |||
29 | let left = children.next().unwrap(); | ||
30 | let right = children.next(); | ||
31 | assert!(children.next().is_none()); | ||
32 | return if let Some(right) = right { | ||
33 | match (find_leaf_at_offset(left, offset), find_leaf_at_offset(right, offset)) { | ||
34 | (LeafAtOffset::Single(left), LeafAtOffset::Single(right)) => | ||
35 | LeafAtOffset::Between(left, right), | ||
36 | _ => unreachable!() | ||
37 | } | ||
38 | } else { | ||
39 | find_leaf_at_offset(left, offset) | ||
40 | }; | ||
41 | } | ||
42 | |||
43 | #[derive(Clone, Copy, Debug)] | ||
44 | pub enum LeafAtOffset<'a> { | ||
45 | None, | ||
46 | Single(SyntaxNodeRef<'a>), | ||
47 | Between(SyntaxNodeRef<'a>, SyntaxNodeRef<'a>) | ||
48 | } | ||
49 | |||
50 | impl<'a> LeafAtOffset<'a> { | ||
51 | pub fn right_biased(self) -> Option<SyntaxNodeRef<'a>> { | ||
52 | match self { | ||
53 | LeafAtOffset::None => None, | ||
54 | LeafAtOffset::Single(node) => Some(node), | ||
55 | LeafAtOffset::Between(_, right) => Some(right) | ||
56 | } | ||
57 | } | ||
58 | |||
59 | pub fn left_biased(self) -> Option<SyntaxNodeRef<'a>> { | ||
60 | match self { | ||
61 | LeafAtOffset::None => None, | ||
62 | LeafAtOffset::Single(node) => Some(node), | ||
63 | LeafAtOffset::Between(left, _) => Some(left) | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl<'f> Iterator for LeafAtOffset<'f> { | ||
69 | type Item = SyntaxNodeRef<'f>; | ||
70 | |||
71 | fn next(&mut self) -> Option<SyntaxNodeRef<'f>> { | ||
72 | match *self { | ||
73 | LeafAtOffset::None => None, | ||
74 | LeafAtOffset::Single(node) => { *self = LeafAtOffset::None; Some(node) } | ||
75 | LeafAtOffset::Between(left, right) => { *self = LeafAtOffset::Single(right); Some(left) } | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | pub fn find_covering_node(root: SyntaxNodeRef, range: TextRange) -> SyntaxNodeRef { | ||
81 | assert!(is_subrange(root.range(), range)); | ||
82 | let (left, right) = match ( | ||
83 | find_leaf_at_offset(root, range.start()).right_biased(), | ||
84 | find_leaf_at_offset(root, range.end()).left_biased() | ||
85 | ) { | ||
86 | (Some(l), Some(r)) => (l, r), | ||
87 | _ => return root | ||
88 | }; | ||
89 | |||
90 | common_ancestor(left, right) | ||
91 | } | ||
92 | |||
93 | pub fn ancestors<'a>(node: SyntaxNodeRef<'a>) -> impl Iterator<Item=SyntaxNodeRef<'a>> { | ||
94 | generate(Some(node), |&node| node.parent()) | ||
95 | } | ||
96 | |||
97 | #[derive(Debug)] | ||
98 | pub enum Direction { | ||
99 | Forward, | ||
100 | Backward, | ||
101 | } | ||
102 | |||
103 | pub fn siblings<'a>( | ||
104 | node: SyntaxNodeRef<'a>, | ||
105 | direction: Direction | ||
106 | ) -> impl Iterator<Item=SyntaxNodeRef<'a>> { | ||
107 | generate(Some(node), move |&node| match direction { | ||
108 | Direction::Forward => node.next_sibling(), | ||
109 | Direction::Backward => node.prev_sibling(), | ||
110 | }) | ||
111 | } | ||
112 | |||
113 | fn common_ancestor<'a>(n1: SyntaxNodeRef<'a>, n2: SyntaxNodeRef<'a>) -> SyntaxNodeRef<'a> { | ||
114 | for p in ancestors(n1) { | ||
115 | if ancestors(n2).any(|a| a == p) { | ||
116 | return p; | ||
117 | } | ||
118 | } | ||
119 | panic!("Can't find common ancestor of {:?} and {:?}", n1, n2) | ||
120 | } | ||
121 | |||
122 | pub fn generate<T>(seed: Option<T>, step: impl Fn(&T) -> Option<T>) -> impl Iterator<Item=T> { | ||
123 | ::itertools::unfold(seed, move |slot| { | ||
124 | slot.take().map(|curr| { | ||
125 | *slot = step(&curr); | ||
126 | curr | ||
127 | }) | ||
128 | }) | ||
129 | } | ||