ast_grep_core/matcher/
node_match.rs1use 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#[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 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 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
76impl<'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
83impl<'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
91impl<'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}