Skip to main content

tiptap_rusty_parser/
path.rs

1//! Positional addressing by index path.
2//!
3//! A *path* is a slice of child indices from a node, root = `&[]`. So in a
4//! `doc -> paragraph -> text` tree, the text node is at `&[0, 0]`.
5//!
6//! There are no stored parent pointers; parent/sibling navigation is expressed
7//! by slicing a path: the parent of `path` is `node.node_at(&path[..path.len()-1])`.
8
9use crate::node::Node;
10
11impl Node {
12    /// Node at `path` (relative to `self`), or `None` if any index is missing.
13    ///
14    /// ```
15    /// use tiptap_rusty_parser::Document;
16    /// let doc = Document::from_json_str(
17    ///     r#"{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hi"}]}]}"#,
18    /// ).unwrap();
19    /// assert_eq!(doc.node_at(&[0, 0]).unwrap().get_text(), Some("hi"));
20    /// assert!(doc.node_at(&[5]).is_none());
21    /// ```
22    pub fn node_at(&self, path: &[usize]) -> Option<&Node> {
23        let mut cur = self;
24        for &i in path {
25            cur = cur.content.as_ref()?.get(i)?;
26        }
27        Some(cur)
28    }
29
30    /// Mutable variant of [`Node::node_at`].
31    pub fn node_at_mut(&mut self, path: &[usize]) -> Option<&mut Node> {
32        let mut cur = self;
33        for &i in path {
34            cur = cur.content.as_mut()?.get_mut(i)?;
35        }
36        Some(cur)
37    }
38
39    /// Path to the first node (incl. `self`) matching `pred`, pre-order.
40    ///
41    /// Returns `Some(vec![])` when `self` matches.
42    ///
43    /// ```
44    /// use tiptap_rusty_parser::Document;
45    /// let doc = Document::from_json_str(
46    ///     r#"{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"hi"}]}]}"#,
47    /// ).unwrap();
48    /// let p = doc.path_to(|n| n.node_type.as_deref() == Some("text")).unwrap();
49    /// assert_eq!(p, vec![0, 0]);
50    /// assert_eq!(doc.node_at(&p).unwrap().get_text(), Some("hi"));
51    /// ```
52    pub fn path_to(&self, mut pred: impl FnMut(&Node) -> bool) -> Option<Vec<usize>> {
53        let mut path = Vec::new();
54        if path_to_rec(self, &mut pred, &mut path) {
55            Some(path)
56        } else {
57            None
58        }
59    }
60
61    /// Paths to every node (incl. `self`) matching `pred`, pre-order.
62    pub fn paths_to(&self, mut pred: impl FnMut(&Node) -> bool) -> Vec<Vec<usize>> {
63        let mut out = Vec::new();
64        let mut path = Vec::new();
65        paths_to_rec(self, &mut pred, &mut path, &mut out);
66        out
67    }
68}
69
70fn path_to_rec(node: &Node, pred: &mut impl FnMut(&Node) -> bool, path: &mut Vec<usize>) -> bool {
71    if pred(node) {
72        return true;
73    }
74    if let Some(children) = &node.content {
75        for (i, child) in children.iter().enumerate() {
76            path.push(i);
77            if path_to_rec(child, pred, path) {
78                return true;
79            }
80            path.pop();
81        }
82    }
83    false
84}
85
86fn paths_to_rec(
87    node: &Node,
88    pred: &mut impl FnMut(&Node) -> bool,
89    path: &mut Vec<usize>,
90    out: &mut Vec<Vec<usize>>,
91) {
92    if pred(node) {
93        out.push(path.clone());
94    }
95    if let Some(children) = &node.content {
96        for (i, child) in children.iter().enumerate() {
97            path.push(i);
98            paths_to_rec(child, pred, path, out);
99            path.pop();
100        }
101    }
102}