1pub mod language;
10pub mod matcher;
11pub mod meta_var;
12pub mod ops;
13pub mod replacer;
14pub mod source;
15pub mod traversal;
16
17#[doc(hidden)]
18pub mod pinned;
19
20mod match_tree;
21mod node;
22
23pub use language::Language;
24pub use match_tree::MatchStrictness;
25pub use matcher::{Matcher, NodeMatch, Pattern, PatternError};
26pub use node::{Node, Position};
27pub use source::{Doc, StrDoc};
28
29#[doc(hidden)]
30pub use node::DisplayContext;
31
32use replacer::Replacer;
33
34use node::Root;
35use source::{Edit, TSParseError};
36
37#[derive(Clone)]
38pub struct AstGrep<D: Doc> {
39 #[doc(hidden)]
40 pub inner: Root<D>,
41}
42impl<D: Doc> AstGrep<D> {
43 pub fn root(&self) -> Node<D> {
44 self.inner.root()
45 }
46
47 pub fn edit(&mut self, edit: Edit<D::Source>) -> Result<&mut Self, TSParseError> {
48 self.inner.do_edit(edit)?;
49 Ok(self)
50 }
51
52 pub fn replace<M: Matcher<D::Lang>, R: Replacer<D>>(
53 &mut self,
54 pattern: M,
55 replacer: R,
56 ) -> Result<bool, TSParseError> {
57 if let Some(edit) = self.root().replace(pattern, replacer) {
58 self.edit(edit)?;
59 Ok(true)
60 } else {
61 Ok(false)
62 }
63 }
64
65 pub fn lang(&self) -> &D::Lang {
66 self.inner.lang()
67 }
68
69 pub fn doc(d: D) -> Self {
72 Self {
73 inner: Root::doc(d),
74 }
75 }
76}
77
78impl<L: Language> AstGrep<StrDoc<L>> {
79 pub fn new<S: AsRef<str>>(src: S, lang: L) -> Self {
80 Self {
81 inner: Root::new(src.as_ref(), lang),
82 }
83 }
84
85 pub fn source(&self) -> &str {
93 self.inner.doc.get_source().as_str()
94 }
95
96 pub fn generate(self) -> String {
97 self.inner.doc.src
98 }
99}
100
101#[cfg(test)]
102mod test {
103 use super::*;
104 use language::Tsx;
105 use ops::Op;
106
107 pub type Result = std::result::Result<(), TSParseError>;
108
109 #[test]
110 fn test_replace() -> Result {
111 let mut ast_grep = Tsx.ast_grep("var a = 1; let b = 2;");
112 ast_grep.replace("var $A = $B", "let $A = $B")?;
113 let source = ast_grep.generate();
114 assert_eq!(source, "let a = 1; let b = 2;"); Ok(())
116 }
117
118 #[test]
119 fn test_replace_by_rule() -> Result {
120 let rule = Op::either("let a = 123").or("let b = 456");
121 let mut ast_grep = Tsx.ast_grep("let a = 123");
122 let replaced = ast_grep.replace(rule, "console.log('it works!')")?;
123 assert!(replaced);
124 let source = ast_grep.generate();
125 assert_eq!(source, "console.log('it works!')");
126 Ok(())
127 }
128
129 #[test]
130 fn test_replace_unnamed_node() -> Result {
131 let mut ast_grep = Tsx.ast_grep("c++");
133 ast_grep.replace("$A++", "$A--")?;
134 let source = ast_grep.generate();
135 assert_eq!(source, "c--");
136 Ok(())
137 }
138
139 #[test]
140 fn test_replace_trivia() -> Result {
141 let mut ast_grep = Tsx.ast_grep("var a = 1 /*haha*/;");
142 ast_grep.replace("var $A = $B", "let $A = $B")?;
143 let source = ast_grep.generate();
144 assert_eq!(source, "let a = 1 /*haha*/;"); let mut ast_grep = Tsx.ast_grep("var a = 1; /*haha*/");
147 ast_grep.replace("var $A = $B", "let $A = $B")?;
148 let source = ast_grep.generate();
149 assert_eq!(source, "let a = 1; /*haha*/");
150 Ok(())
151 }
152
153 #[test]
154 fn test_replace_trivia_with_skipped() -> Result {
155 let mut ast_grep = Tsx.ast_grep("return foo(1, 2,) /*haha*/;");
156 ast_grep.replace("return foo($A, $B)", "return bar($A, $B)")?;
157 let source = ast_grep.generate();
158 assert_eq!(source, "return bar(1, 2) /*haha*/;"); Ok(())
160 }
161}