Skip to main content

dmc_transform/
visit.rs

1use dmc_parser::ast::*;
2
3/// Result of visiting one node. Walker consumes this when iterating the
4/// parent's children Vec.
5pub enum NodeAction {
6  /// Walker recurses into this node's children.
7  Keep,
8  /// Node stays in place but its children are not visited.
9  KeepSkipChildren,
10  /// Splice these nodes into the parent's `children` at the current index.
11  /// Replacements are NOT re-visited (prevents infinite loops on
12  /// transformers that produce nodes matching their own pattern).
13  Replace(Vec<Node>),
14  /// Drop this node from the parent's children.
15  Remove,
16}
17
18/// Implemented by transform passes to react to each node. The default impl
19/// keeps every node and recurses, so override only the variants you care about.
20pub trait Visitor {
21  fn visit_node(&mut self, _node: &mut Node) -> NodeAction {
22    NodeAction::Keep
23  }
24}
25
26/// Recurse into the per-variant inner children of `parent`. Leaf variants
27/// (Text, InlineCode, CodeBlock, Image, JsxExpression, ...) are no-ops.
28pub fn walk_children_mut<V: Visitor>(parent: &mut Node, v: &mut V) {
29  if let Node::Table(t) = parent {
30    for row in &mut t.children {
31      for cell in &mut row.cells {
32        walk_root(&mut cell.children, v);
33      }
34    }
35    return;
36  }
37
38  if let Some(kids) = Node::children_of_mut(parent) {
39    walk_root(kids, v);
40  }
41}
42
43/// Drive the visitor over a `Vec<Node>`, honoring every `NodeAction` variant.
44/// Replacements aren't re-visited but are descended into on a later pass if
45/// the visitor returns `Keep`.
46pub fn walk_root<V: Visitor>(children: &mut Vec<Node>, v: &mut V) {
47  let mut i = 0;
48  while i < children.len() {
49    match v.visit_node(&mut children[i]) {
50      NodeAction::Keep => {
51        walk_children_mut(&mut children[i], v);
52        i += 1;
53      },
54      NodeAction::KeepSkipChildren => {
55        i += 1;
56      },
57      NodeAction::Replace(new) => {
58        let n = new.len();
59        children.splice(i..=i, new);
60        i += n;
61      },
62      NodeAction::Remove => {
63        children.remove(i);
64      },
65    }
66  }
67}