ast_grep_core/matcher/
node_match.rs

1use super::Matcher;
2use crate::meta_var::MetaVarEnv;
3use crate::replacer::Replacer;
4use crate::source::Edit as E;
5use crate::{Doc, Node};
6
7use std::borrow::Borrow;
8use std::ops::Deref;
9
10type Edit<D> = E<<D as Doc>::Source>;
11
12/// Represents the matched node with populated MetaVarEnv.
13/// It derefs to the Node so you can use it as a Node.
14/// To access the underlying MetaVarEnv, call `get_env` method.
15#[derive(Clone)]
16pub struct NodeMatch<'t, D: Doc>(Node<'t, D>, MetaVarEnv<'t, D>);
17
18impl<'tree, D: Doc> NodeMatch<'tree, D> {
19  pub fn new(node: Node<'tree, D>, env: MetaVarEnv<'tree, D>) -> Self {
20    Self(node, env)
21  }
22
23  pub fn get_node(&self) -> &Node<'tree, D> {
24    &self.0
25  }
26
27  /// Returns the populated MetaVarEnv for this match.
28  pub fn get_env(&self) -> &MetaVarEnv<'tree, D> {
29    &self.1
30  }
31  pub fn get_env_mut(&mut self) -> &mut MetaVarEnv<'tree, D> {
32    &mut self.1
33  }
34  /// # Safety
35  /// should only called for readopting nodes
36  pub(crate) unsafe fn get_node_mut(&mut self) -> &mut Node<'tree, D> {
37    &mut self.0
38  }
39}
40
41impl<D: Doc> NodeMatch<'_, D> {
42  pub fn replace_by<R: Replacer<D>>(&self, replacer: R) -> Edit<D> {
43    let range = self.range();
44    let position = range.start;
45    let deleted_length = range.len();
46    let inserted_text = replacer.generate_replacement(self);
47    Edit::<D> {
48      position,
49      deleted_length,
50      inserted_text,
51    }
52  }
53
54  #[doc(hidden)]
55  pub fn make_edit<M, R>(&self, matcher: &M, replacer: &R) -> Edit<D>
56  where
57    M: Matcher,
58    R: Replacer<D>,
59  {
60    let range = replacer.get_replaced_range(self, matcher);
61    let inserted_text = replacer.generate_replacement(self);
62    Edit::<D> {
63      position: range.start,
64      deleted_length: range.len(),
65      inserted_text,
66    }
67  }
68}
69
70impl<'tree, D: Doc> From<Node<'tree, D>> for NodeMatch<'tree, D> {
71  fn from(node: Node<'tree, D>) -> Self {
72    Self(node, MetaVarEnv::new())
73  }
74}
75
76/// NodeMatch is an immutable view to Node
77impl<'tree, D: Doc> From<NodeMatch<'tree, D>> for Node<'tree, D> {
78  fn from(node_match: NodeMatch<'tree, D>) -> Self {
79    node_match.0
80  }
81}
82
83/// NodeMatch is an immutable view to Node
84impl<'tree, D: Doc> Deref for NodeMatch<'tree, D> {
85  type Target = Node<'tree, D>;
86  fn deref(&self) -> &Self::Target {
87    &self.0
88  }
89}
90
91/// NodeMatch is an immutable view to Node
92impl<'tree, D: Doc> Borrow<Node<'tree, D>> for NodeMatch<'tree, D> {
93  fn borrow(&self) -> &Node<'tree, D> {
94    &self.0
95  }
96}
97
98#[cfg(test)]
99mod test {
100  use super::*;
101  use crate::language::Tsx;
102  use crate::tree_sitter::{LanguageExt, StrDoc};
103
104  fn use_node<L: LanguageExt>(n: &Node<StrDoc<L>>) -> String {
105    n.text().to_string()
106  }
107
108  fn borrow_node<'a, D, B>(b: B) -> String
109  where
110    D: Doc + 'static,
111    B: Borrow<Node<'a, D>>,
112  {
113    b.borrow().text().to_string()
114  }
115
116  #[test]
117  fn test_node_match_as_node() {
118    let root = Tsx.ast_grep("var a = 1");
119    let node = root.root();
120    let src = node.text().to_string();
121    let nm = NodeMatch::from(node);
122    let ret = use_node(&*nm);
123    assert_eq!(ret, src);
124    assert_eq!(use_node(&*nm), borrow_node(nm));
125  }
126
127  #[test]
128  fn test_node_env() {
129    let root = Tsx.ast_grep("var a = 1");
130    let find = root.root().find("var $A = 1").expect("should find");
131    let env = find.get_env();
132    let node = env.get_match("A").expect("should find");
133    assert_eq!(node.text(), "a");
134  }
135
136  #[test]
137  fn test_replace_by() {
138    let root = Tsx.ast_grep("var a = 1");
139    let find = root.root().find("var $A = 1").expect("should find");
140    let fixed = find.replace_by("var b = $A");
141    assert_eq!(fixed.position, 0);
142    assert_eq!(fixed.deleted_length, 9);
143    assert_eq!(fixed.inserted_text, "var b = a".as_bytes());
144  }
145}