tom/
cst.rs

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}