syntree/
edit.rs

1//! Types associated with performing immutable editing of a tree.
2
3use core::cell::Cell;
4
5use alloc::vec::Vec;
6
7use std::collections::HashMap;
8
9use crate::error::Error;
10use crate::flavor::{Flavor, Storage};
11use crate::index::{Index, TreeIndex};
12use crate::links::Links;
13use crate::node::Node;
14use crate::pointer::Pointer;
15use crate::span::Span;
16use crate::tree::Tree;
17
18#[derive(Debug)]
19pub(crate) enum Change {
20    /// Delete the given node.
21    Delete,
22}
23
24/// A recorded set of tree modifications.
25///
26/// You can use [`ChangeSet::modify`] to construct a new modified tree from an
27/// existing one.
28///
29/// # Examples
30///
31/// ```
32/// use syntree::edit::ChangeSet;
33///
34/// let tree = syntree::tree! {
35///     "root" => {
36///         "child" => {
37///             ("lit", 1),
38///             ("lit", 2),
39///         },
40///         ("whitespace", 3),
41///     }
42/// };
43///
44/// let child = tree.first().and_then(|n| n.first()).ok_or("missing child")?;
45///
46/// let mut change_set = ChangeSet::new();
47/// change_set.remove(child.id());
48///
49/// assert_eq!(
50///     change_set.modify(&tree)?,
51///     syntree::tree! {
52///         "root" => {
53///             ("whitespace", 3)
54///         }
55///     }
56/// );
57///
58/// let lit = child.first().ok_or("missing lit")?;
59///
60/// let mut change_set = ChangeSet::new();
61/// change_set.remove(lit.id());
62///
63/// assert_eq!(
64///     change_set.modify(&tree)?,
65///     syntree::tree! {
66///         "root" => {
67///             "child" => {
68///                 ("lit", 2),
69///             },
70///             ("whitespace", 3)
71///         }
72///     }
73/// );
74/// # Ok::<_, Box<dyn core::error::Error>>(())
75/// ```
76pub struct ChangeSet<T, F>
77where
78    T: Copy,
79    F: Flavor,
80{
81    changes: HashMap<F::Pointer, Change>,
82    #[allow(unused)]
83    trees: Vec<Tree<T, F>>,
84}
85
86impl<T, F> ChangeSet<T, F>
87where
88    T: Copy,
89    F: Flavor,
90{
91    /// Construct a new empty [`ChangeSet`].
92    #[must_use]
93    pub fn new() -> Self {
94        Self::default()
95    }
96}
97
98impl<T, F> ChangeSet<T, F>
99where
100    T: Copy,
101    F: Flavor,
102{
103    /// Register a node removal in the changeset. Only one kind of modification
104    /// for a given node will be preserved.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use syntree::edit::ChangeSet;
110    ///
111    /// let tree = syntree::tree! {
112    ///     "root" => {
113    ///         "child" => {
114    ///             ("lit", 1),
115    ///             ("lit", 2),
116    ///         },
117    ///         ("whitespace", 3),
118    ///     }
119    /// };
120    ///
121    /// let child = tree.first().and_then(|n| n.first()).ok_or("missing child")?;
122    ///
123    /// let mut change_set = ChangeSet::new();
124    /// change_set.remove(child.id());
125    ///
126    /// assert_eq!(
127    ///     change_set.modify(&tree)?,
128    ///     syntree::tree! {
129    ///         "root" => {
130    ///             ("whitespace", 3)
131    ///         }
132    ///     }
133    /// );
134    /// # Ok::<_, Box<dyn core::error::Error>>(())
135    /// ```
136    pub fn remove(&mut self, id: F::Pointer) {
137        self.changes.insert(id, Change::Delete);
138    }
139
140    /// Construct a modified tree where the recorded modifications have been
141    /// applied.
142    ///
143    /// # Errors
144    ///
145    /// Errors with [`Error::Overflow`] in case we run out of node
146    /// identifiers.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// use syntree::edit::ChangeSet;
152    ///
153    /// let tree = syntree::tree! {
154    ///     "root" => {
155    ///         "child" => {
156    ///             ("lit", 1),
157    ///             ("lit", 2),
158    ///         },
159    ///         ("whitespace", 3),
160    ///     }
161    /// };
162    ///
163    /// let child = tree.first().and_then(|n| n.first()).ok_or("missing child")?;
164    /// let mut change_set = ChangeSet::new();
165    /// change_set.remove(child.id());
166    ///
167    /// assert_eq!(
168    ///     change_set.modify(&tree)?,
169    ///     syntree::tree! {
170    ///         "root" => {
171    ///             ("whitespace", 3)
172    ///         }
173    ///     }
174    /// );
175    /// # Ok::<_, Box<dyn core::error::Error>>(())
176    /// ```
177    pub fn modify(&mut self, tree: &Tree<T, F>) -> Result<Tree<T, F>, Error<F::Error>> {
178        let mut output = Tree::<T, F>::with_capacity(tree.capacity())?;
179
180        let mut refactor = RefactorWalk {
181            parents: Vec::new(),
182            prev: None,
183        };
184
185        let mut cursor = F::Index::EMPTY;
186
187        // The specified sub-tree depth is being deleted.
188        let mut current = tree.first().map(|node| (node, false));
189
190        while let Some((mut node, mut first)) = current.take() {
191            let node_id = F::Pointer::new(output.len()).ok_or(Error::Overflow)?;
192
193            if let Some(change) = self.changes.get(&node_id) {
194                match change {
195                    Change::Delete => {
196                        let Some(skipped) = refactor.skip_subtree(node, first) else {
197                            continue;
198                        };
199
200                        node = skipped.node;
201                        first = skipped.first;
202                    }
203                }
204            }
205
206            if refactor.parents.is_empty() {
207                let (first, last) = output.links_mut();
208
209                if first.is_none() {
210                    *first = Some(node_id);
211                }
212
213                *last = Some(node_id);
214            }
215
216            // Since we are the first node in the sequence we're obligated to
217            // set the first child of the parent.
218            let prev = if first {
219                None
220            } else {
221                let prev = refactor.prev.take();
222
223                if let Some(prev) = prev.and_then(|id| output.get_mut(id)) {
224                    prev.next = Some(node_id);
225                }
226
227                prev
228            };
229
230            let span = if !node.has_children() && !node.span().is_empty() {
231                output.indexes_mut().push(TreeIndex {
232                    index: cursor,
233                    id: node_id,
234                })?;
235                let start = cursor;
236                cursor = cursor
237                    .checked_add_len(node.span().len())
238                    .ok_or(Error::Overflow)?;
239                Span::new(start, cursor)
240            } else {
241                Span::point(cursor)
242            };
243
244            let parent = refactor.parents.last().map(|n| n.1);
245
246            if let Some(parent) = parent.and_then(|id| output.get_mut(id)) {
247                if parent.first.is_none() {
248                    parent.first = Some(node_id);
249                }
250
251                parent.last = Some(node_id);
252                parent.span.end = span.end;
253            }
254
255            output.push(Links {
256                data: Cell::new(node.value()),
257                span,
258                parent,
259                prev,
260                next: None,
261                first: None,
262                last: None,
263            })?;
264
265            current = refactor.step(node, node_id);
266        }
267
268        output.span_mut().end = cursor;
269        Ok(output)
270    }
271}
272
273impl<T, F> Default for ChangeSet<T, F>
274where
275    T: Copy,
276    F: Flavor,
277{
278    #[inline]
279    fn default() -> Self {
280        Self {
281            changes: HashMap::new(),
282            trees: Vec::new(),
283        }
284    }
285}
286
287/// The state of the skipped subtree.
288struct Skipped<'a, T, F>
289where
290    T: Copy,
291    F: Flavor,
292{
293    node: Node<'a, T, F>,
294    first: bool,
295}
296
297struct RefactorWalk<'a, T, F>
298where
299    T: Copy,
300    F: Flavor,
301{
302    parents: Vec<(Node<'a, T, F>, F::Pointer)>,
303    prev: Option<F::Pointer>,
304}
305
306impl<'a, T, F> RefactorWalk<'a, T, F>
307where
308    T: Copy,
309    F: Flavor,
310{
311    fn skip_subtree(&mut self, node: Node<'a, T, F>, first: bool) -> Option<Skipped<'a, T, F>> {
312        if let Some(next) = node.next() {
313            return Some(Skipped { node: next, first });
314        }
315
316        let (node, parent_id) = self.parents.pop()?;
317        self.prev = Some(parent_id);
318        Some(Skipped { node, first: false })
319    }
320
321    /// Advance the iteration.
322    fn step(
323        &mut self,
324        node: Node<'a, T, F>,
325        node_id: F::Pointer,
326    ) -> Option<(Node<'a, T, F>, bool)> {
327        if let Some(next) = node.first() {
328            self.parents.push((node, node_id));
329            return Some((next, true));
330        }
331
332        if let Some(next) = node.next() {
333            self.prev = Some(node_id);
334            return Some((next, false));
335        }
336
337        while let Some((parent, prev_id)) = self.parents.pop() {
338            if let Some(next) = parent.next() {
339                self.prev = Some(prev_id);
340                return Some((next, false));
341            }
342        }
343
344        None
345    }
346}