1use std::cmp;
2
3use {
4 TomlDoc, Symbol, TextUnit, TextRange, ChunkedText,
5 tree::{NodeId, TreeData},
6};
7
8#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
9pub struct CstNode(pub(crate) NodeId);
10
11pub enum CstNodeKind<'a> {
12 Leaf(&'a str),
13 Internal(CstChildren<'a>),
14}
15
16impl CstNode {
17 pub fn symbol(self, doc: &TomlDoc) -> Symbol {
18 *match self.0.data(&doc.tree) {
19 TreeData::Internal(s) => s,
20 TreeData::Leaf((s, _)) => s,
21 }
22 }
23
24 pub fn range(self, doc: &TomlDoc) -> TextRange {
25 assert!(
26 !doc.edit_in_progress,
27 "range info is unavailable during edit"
28 );
29 doc.data[self.0.to_idx()].range
30 }
31
32 pub fn kind(self, doc: &TomlDoc) -> CstNodeKind {
33 match self.0.data(&doc.tree) {
34 TreeData::Leaf((_, idx)) => CstNodeKind::Leaf(doc.intern.resolve(*idx)),
35 TreeData::Internal(_) => CstNodeKind::Internal(self.children(doc)),
36 }
37 }
38
39 pub fn is_leaf(self, doc: &TomlDoc) -> bool {
40 match self.kind(doc) {
41 CstNodeKind::Leaf(_) => true,
42 CstNodeKind::Internal(_) => false,
43 }
44 }
45
46 pub fn parent(self, doc: &TomlDoc) -> Option<CstNode> {
47 self.0.parent(&doc.tree).map(CstNode)
48 }
49
50 pub fn children(self, doc: &TomlDoc) -> CstChildren {
51 CstChildren { doc, node: self }
52 }
53
54 pub fn next_sibling(self, doc: &TomlDoc) -> Option<CstNode> {
55 self.0.next_sibling(&doc.tree).map(CstNode)
56 }
57
58 pub fn prev_sibling(self, doc: &TomlDoc) -> Option<CstNode> {
59 self.0.prev_sibling(&doc.tree).map(CstNode)
60 }
61
62 pub fn get_text(self, doc: &TomlDoc) -> String {
63 self.chunked_text(doc).to_string()
64 }
65
66 pub(crate) fn chunked_text<'a>(self, doc: &'a TomlDoc) -> impl ChunkedText + 'a {
67 struct Chunks<'a> {
68 root: CstNode,
69 doc: &'a TomlDoc,
70 }
71
72 impl<'a> Chunks<'a> {
73 fn go<F: FnMut(&str) -> Result<(), T>, T>(
74 &self,
75 node: CstNode,
76 f: &mut F,
77 ) -> Result<(), T> {
78 match node.kind(self.doc) {
79 CstNodeKind::Leaf(text) => f(text)?,
80 CstNodeKind::Internal(children) => {
81 for child in children {
82 self.go(child, f)?;
83 }
84 }
85 }
86 Ok(())
87 }
88 }
89
90 impl<'a> ChunkedText for Chunks<'a> {
91 fn for_each_chunk<F: FnMut(&str) -> Result<(), T>, T>(
92 &self,
93 mut f: F,
94 ) -> Result<(), T> {
95 self.go(self.root, &mut f)
96 }
97 }
98
99 Chunks { root: self, doc }
100 }
101
102 pub(crate) fn chunked_substring<'a>(
103 self,
104 doc: &'a TomlDoc,
105 range: TextRange,
106 ) -> impl ChunkedText + 'a {
107 assert!(
108 !doc.edit_in_progress,
109 "range info is unavailable during edit"
110 );
111
112 struct Chunks<'a> {
113 root: CstNode,
114 doc: &'a TomlDoc,
115 range: TextRange,
116 }
117
118 impl<'a> Chunks<'a> {
119 fn go<F: FnMut(&str) -> Result<(), T>, T>(
120 &self,
121 node: CstNode,
122 f: &mut F,
123 ) -> Result<(), T> {
124 let node_range = node.range(self.doc);
125 let rel_range = match intersect(self.range, node_range) {
126 None => return Ok(()),
127 Some(range) => relative_range(node_range.start(), range),
128 };
129 match node.kind(self.doc) {
130 CstNodeKind::Leaf(text) => f(&text[rel_range])?,
131 CstNodeKind::Internal(children) => {
132 for child in children {
133 self.go(child, f)?;
134 }
135 }
136 }
137 Ok(())
138 }
139 }
140
141 impl<'a> ChunkedText for Chunks<'a> {
142 fn for_each_chunk<F: FnMut(&str) -> Result<(), T>, T>(
143 &self,
144 mut f: F,
145 ) -> Result<(), T> {
146 self.go(self.root, &mut f)
147 }
148 }
149 Chunks {
150 root: self,
151 doc,
152 range,
153 }
154 }
155
156 pub fn debug(self, doc: &TomlDoc) -> String {
157 if doc.edit_in_progress {
158 format!("{}@[??:??)", self.symbol(doc).name())
159 } else {
160 format!("{}@{:?}", self.symbol(doc).name(), self.range(doc))
161 }
162 }
163}
164
165#[derive(Clone, Copy)]
166pub struct CstChildren<'a> {
167 doc: &'a TomlDoc,
168 node: CstNode,
169}
170
171impl<'a> CstChildren<'a> {
172 pub fn first(self) -> Option<CstNode> {
173 self.node.0.first_child(&self.doc.tree).map(CstNode)
174 }
175 pub fn last(self) -> Option<CstNode> {
176 self.node.0.last_child(&self.doc.tree).map(CstNode)
177 }
178 pub fn iter(self) -> CstChildrenIter<'a> {
179 CstChildrenIter {
180 doc: self.doc,
181 curr: self.first(),
182 }
183 }
184 pub fn rev(self) -> RevCstChildrenIter<'a> {
185 RevCstChildrenIter {
186 doc: self.doc,
187 curr: self.last(),
188 }
189 }
190}
191
192impl<'a> IntoIterator for CstChildren<'a> {
193 type Item = CstNode;
194 type IntoIter = CstChildrenIter<'a>;
195 fn into_iter(self) -> Self::IntoIter {
196 self.iter()
197 }
198}
199
200#[derive(Clone)]
201pub struct CstChildrenIter<'a> {
202 pub(crate) doc: &'a TomlDoc,
203 curr: Option<CstNode>,
204}
205
206impl<'a> Iterator for CstChildrenIter<'a> {
207 type Item = CstNode;
208 fn next(&mut self) -> Option<CstNode> {
209 self.curr.map(|node| {
210 self.curr = node.next_sibling(self.doc);
211 node
212 })
213 }
214}
215
216#[derive(Clone)]
217pub struct RevCstChildrenIter<'a> {
218 doc: &'a TomlDoc,
219 curr: Option<CstNode>,
220}
221
222impl<'a> Iterator for RevCstChildrenIter<'a> {
223 type Item = CstNode;
224 fn next(&mut self) -> Option<CstNode> {
225 self.curr.map(|node| {
226 self.curr = node.prev_sibling(self.doc);
227 node
228 })
229 }
230}
231
232fn intersect(r1: TextRange, r2: TextRange) -> Option<TextRange> {
233 let start = cmp::max(r1.start(), r2.start());
234 let end = cmp::min(r1.end(), r2.end());
235 if end > start {
236 Some(TextRange::from_to(start, end))
237 } else {
238 None
239 }
240}
241
242fn relative_range(offset: TextUnit, range: TextRange) -> TextRange {
243 TextRange::from_to(range.start() - offset, range.end() - offset)
244}