ast_grep_core/
replacer.rs

1use crate::matcher::Matcher;
2use crate::meta_var::{is_valid_meta_var_char, MetaVariableID, Underlying};
3use crate::{Doc, Node, NodeMatch, Root};
4use std::ops::Range;
5
6pub(crate) use indent::formatted_slice;
7
8use crate::source::Edit as E;
9type Edit<D> = E<<D as Doc>::Source>;
10
11mod indent;
12mod structural;
13mod template;
14
15pub use crate::source::Content;
16pub use template::{TemplateFix, TemplateFixError};
17
18/// Replace meta variable in the replacer string
19pub trait Replacer<D: Doc> {
20  fn generate_replacement(&self, nm: &NodeMatch<'_, D>) -> Underlying<D>;
21  fn get_replaced_range(&self, nm: &NodeMatch<'_, D>, matcher: impl Matcher) -> Range<usize> {
22    let range = nm.range();
23    if let Some(len) = matcher.get_match_len(nm.get_node().clone()) {
24      range.start..range.start + len
25    } else {
26      range
27    }
28  }
29}
30
31impl<D: Doc> Replacer<D> for str {
32  fn generate_replacement(&self, nm: &NodeMatch<'_, D>) -> Underlying<D> {
33    template::gen_replacement(self, nm)
34  }
35}
36
37impl<D: Doc> Replacer<D> for Root<D> {
38  fn generate_replacement(&self, nm: &NodeMatch<'_, D>) -> Underlying<D> {
39    structural::gen_replacement(self, nm)
40  }
41}
42
43impl<D, T> Replacer<D> for &T
44where
45  D: Doc,
46  T: Replacer<D> + ?Sized,
47{
48  fn generate_replacement(&self, nm: &NodeMatch<D>) -> Underlying<D> {
49    (**self).generate_replacement(nm)
50  }
51}
52
53impl<D: Doc> Replacer<D> for Node<'_, D> {
54  fn generate_replacement(&self, _nm: &NodeMatch<'_, D>) -> Underlying<D> {
55    let range = self.range();
56    self.root.doc.get_source().get_range(range).to_vec()
57  }
58}
59
60enum MetaVarExtract {
61  /// $A for captured meta var
62  Single(MetaVariableID),
63  /// $$$A for captured ellipsis
64  Multiple(MetaVariableID),
65  Transformed(MetaVariableID),
66}
67
68impl MetaVarExtract {
69  fn used_var(&self) -> &str {
70    match self {
71      MetaVarExtract::Single(s) => s,
72      MetaVarExtract::Multiple(s) => s,
73      MetaVarExtract::Transformed(s) => s,
74    }
75  }
76}
77
78fn split_first_meta_var(
79  src: &str,
80  meta_char: char,
81  transform: &[MetaVariableID],
82) -> Option<(MetaVarExtract, usize)> {
83  debug_assert!(src.starts_with(meta_char));
84  let mut i = 0;
85  let mut skipped = 0;
86  let is_multi = loop {
87    i += 1;
88    skipped += meta_char.len_utf8();
89    if i == 3 {
90      break true;
91    }
92    if !src[skipped..].starts_with(meta_char) {
93      break false;
94    }
95  };
96  // no Anonymous meta var allowed, so _ is not allowed
97  let i = src[skipped..]
98    .find(|c: char| !is_valid_meta_var_char(c))
99    .unwrap_or(src.len() - skipped);
100  // no name found
101  if i == 0 {
102    return None;
103  }
104  let name = src[skipped..skipped + i].to_string();
105  let var = if is_multi {
106    MetaVarExtract::Multiple(name)
107  } else if transform.contains(&name) {
108    MetaVarExtract::Transformed(name)
109  } else {
110    MetaVarExtract::Single(name)
111  };
112  Some((var, skipped + i))
113}