aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ssr/src/replacing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ssr/src/replacing.rs')
-rw-r--r--crates/ra_ssr/src/replacing.rs128
1 files changed, 93 insertions, 35 deletions
diff --git a/crates/ra_ssr/src/replacing.rs b/crates/ra_ssr/src/replacing.rs
index e43cc5167..4b3f5509c 100644
--- a/crates/ra_ssr/src/replacing.rs
+++ b/crates/ra_ssr/src/replacing.rs
@@ -1,66 +1,124 @@
1//! Code for applying replacement templates for matches that have previously been found. 1//! Code for applying replacement templates for matches that have previously been found.
2 2
3use crate::matching::Var; 3use crate::matching::Var;
4use crate::parsing::PatternElement; 4use crate::{resolving::ResolvedRule, Match, SsrMatches};
5use crate::{Match, SsrMatches}; 5use ra_syntax::ast::{self, AstToken};
6use ra_syntax::ast::AstToken; 6use ra_syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextSize};
7use ra_syntax::TextSize;
8use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
9 8
10/// Returns a text edit that will replace each match in `matches` with its corresponding replacement 9/// Returns a text edit that will replace each match in `matches` with its corresponding replacement
11/// template. Placeholders in the template will have been substituted with whatever they matched to 10/// template. Placeholders in the template will have been substituted with whatever they matched to
12/// in the original code. 11/// in the original code.
13pub(crate) fn matches_to_edit(matches: &SsrMatches, file_src: &str) -> TextEdit { 12pub(crate) fn matches_to_edit(
14 matches_to_edit_at_offset(matches, file_src, 0.into()) 13 matches: &SsrMatches,
14 file_src: &str,
15 rules: &[ResolvedRule],
16) -> TextEdit {
17 matches_to_edit_at_offset(matches, file_src, 0.into(), rules)
15} 18}
16 19
17fn matches_to_edit_at_offset( 20fn matches_to_edit_at_offset(
18 matches: &SsrMatches, 21 matches: &SsrMatches,
19 file_src: &str, 22 file_src: &str,
20 relative_start: TextSize, 23 relative_start: TextSize,
24 rules: &[ResolvedRule],
21) -> TextEdit { 25) -> TextEdit {
22 let mut edit_builder = ra_text_edit::TextEditBuilder::default(); 26 let mut edit_builder = ra_text_edit::TextEditBuilder::default();
23 for m in &matches.matches { 27 for m in &matches.matches {
24 edit_builder.replace( 28 edit_builder.replace(
25 m.range.range.checked_sub(relative_start).unwrap(), 29 m.range.range.checked_sub(relative_start).unwrap(),
26 render_replace(m, file_src), 30 render_replace(m, file_src, rules),
27 ); 31 );
28 } 32 }
29 edit_builder.finish() 33 edit_builder.finish()
30} 34}
31 35
32fn render_replace(match_info: &Match, file_src: &str) -> String { 36struct ReplacementRenderer<'a> {
37 match_info: &'a Match,
38 file_src: &'a str,
39 rules: &'a [ResolvedRule],
40 rule: &'a ResolvedRule,
41}
42
43fn render_replace(match_info: &Match, file_src: &str, rules: &[ResolvedRule]) -> String {
33 let mut out = String::new(); 44 let mut out = String::new();
34 for r in &match_info.template.tokens { 45 let rule = &rules[match_info.rule_index];
35 match r { 46 let template = rule
36 PatternElement::Token(t) => out.push_str(t.text.as_str()), 47 .template
37 PatternElement::Placeholder(p) => { 48 .as_ref()
38 if let Some(placeholder_value) = 49 .expect("You called MatchFinder::edits after calling MatchFinder::add_search_pattern");
39 match_info.placeholder_values.get(&Var(p.ident.to_string())) 50 let renderer = ReplacementRenderer { match_info, file_src, rules, rule };
40 { 51 renderer.render_node(&template.node, &mut out);
41 let range = &placeholder_value.range.range; 52 for comment in &match_info.ignored_comments {
42 let mut matched_text = 53 out.push_str(&comment.syntax().to_string());
43 file_src[usize::from(range.start())..usize::from(range.end())].to_owned(); 54 }
44 let edit = matches_to_edit_at_offset( 55 out
45 &placeholder_value.inner_matches, 56}
46 file_src, 57
47 range.start(), 58impl ReplacementRenderer<'_> {
48 ); 59 fn render_node_children(&self, node: &SyntaxNode, out: &mut String) {
49 edit.apply(&mut matched_text); 60 for node_or_token in node.children_with_tokens() {
50 out.push_str(&matched_text); 61 self.render_node_or_token(&node_or_token, out);
51 } else { 62 }
52 // We validated that all placeholder references were valid before we 63 }
53 // started, so this shouldn't happen. 64
54 panic!( 65 fn render_node_or_token(&self, node_or_token: &SyntaxElement, out: &mut String) {
55 "Internal error: replacement referenced unknown placeholder {}", 66 match node_or_token {
56 p.ident 67 SyntaxElement::Token(token) => {
57 ); 68 self.render_token(&token, out);
69 }
70 SyntaxElement::Node(child_node) => {
71 self.render_node(&child_node, out);
72 }
73 }
74 }
75
76 fn render_node(&self, node: &SyntaxNode, out: &mut String) {
77 use ra_syntax::ast::AstNode;
78 if let Some(mod_path) = self.match_info.rendered_template_paths.get(&node) {
79 out.push_str(&mod_path.to_string());
80 // Emit everything except for the segment's name-ref, since we already effectively
81 // emitted that as part of `mod_path`.
82 if let Some(path) = ast::Path::cast(node.clone()) {
83 if let Some(segment) = path.segment() {
84 for node_or_token in segment.syntax().children_with_tokens() {
85 if node_or_token.kind() != SyntaxKind::NAME_REF {
86 self.render_node_or_token(&node_or_token, out);
87 }
88 }
58 } 89 }
59 } 90 }
91 } else {
92 self.render_node_children(&node, out);
60 } 93 }
61 } 94 }
62 for comment in &match_info.ignored_comments { 95
63 out.push_str(&comment.syntax().to_string()); 96 fn render_token(&self, token: &SyntaxToken, out: &mut String) {
97 if let Some(placeholder) = self.rule.get_placeholder(&token) {
98 if let Some(placeholder_value) =
99 self.match_info.placeholder_values.get(&Var(placeholder.ident.to_string()))
100 {
101 let range = &placeholder_value.range.range;
102 let mut matched_text =
103 self.file_src[usize::from(range.start())..usize::from(range.end())].to_owned();
104 let edit = matches_to_edit_at_offset(
105 &placeholder_value.inner_matches,
106 self.file_src,
107 range.start(),
108 self.rules,
109 );
110 edit.apply(&mut matched_text);
111 out.push_str(&matched_text);
112 } else {
113 // We validated that all placeholder references were valid before we
114 // started, so this shouldn't happen.
115 panic!(
116 "Internal error: replacement referenced unknown placeholder {}",
117 placeholder.ident
118 );
119 }
120 } else {
121 out.push_str(token.text().as_str());
122 }
64 } 123 }
65 out
66} 124}