aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/lib.rs3
-rw-r--r--crates/ra_ide/src/ssr.rs36
-rw-r--r--crates/ra_syntax/src/ast/make.rs16
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs5
-rw-r--r--crates/rust-analyzer/src/req.rs4
5 files changed, 52 insertions, 12 deletions
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index c60e86aea..40276d4fe 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -478,9 +478,10 @@ impl Analysis {
478 pub fn structural_search_replace( 478 pub fn structural_search_replace(
479 &self, 479 &self,
480 query: &str, 480 query: &str,
481 parse_only: bool,
481 ) -> Cancelable<Result<SourceChange, SsrError>> { 482 ) -> Cancelable<Result<SourceChange, SsrError>> {
482 self.with_db(|db| { 483 self.with_db(|db| {
483 let edits = ssr::parse_search_replace(query, db)?; 484 let edits = ssr::parse_search_replace(query, parse_only, db)?;
484 Ok(SourceChange::source_file_edits("ssr", edits)) 485 Ok(SourceChange::source_file_edits("ssr", edits))
485 }) 486 })
486 } 487 }
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index c011a2e74..1c9710a5d 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -1,8 +1,10 @@
1//! structural search replace 1//! structural search replace
2 2
3use crate::source_change::SourceFileEdit; 3use crate::source_change::SourceFileEdit;
4use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_ide_db::symbol_index::SymbolsDatabase;
4use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
5use ra_syntax::ast::make::expr_from_text; 7use ra_syntax::ast::make::try_expr_from_text;
6use ra_syntax::ast::{AstToken, Comment}; 8use ra_syntax::ast::{AstToken, Comment};
7use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; 9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
8use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
@@ -10,9 +12,6 @@ use rustc_hash::FxHashMap;
10use std::collections::HashMap; 12use std::collections::HashMap;
11use std::str::FromStr; 13use std::str::FromStr;
12 14
13pub use ra_db::{SourceDatabase, SourceDatabaseExt};
14use ra_ide_db::symbol_index::SymbolsDatabase;
15
16#[derive(Debug, PartialEq)] 15#[derive(Debug, PartialEq)]
17pub struct SsrError(String); 16pub struct SsrError(String);
18 17
@@ -26,14 +25,17 @@ impl std::error::Error for SsrError {}
26 25
27pub fn parse_search_replace( 26pub fn parse_search_replace(
28 query: &str, 27 query: &str,
28 parse_only: bool,
29 db: &RootDatabase, 29 db: &RootDatabase,
30) -> Result<Vec<SourceFileEdit>, SsrError> { 30) -> Result<Vec<SourceFileEdit>, SsrError> {
31 let mut edits = vec![]; 31 let mut edits = vec![];
32 let query: SsrQuery = query.parse()?; 32 let query: SsrQuery = query.parse()?;
33 if parse_only {
34 return Ok(edits);
35 }
33 for &root in db.local_roots().iter() { 36 for &root in db.local_roots().iter() {
34 let sr = db.source_root(root); 37 let sr = db.source_root(root);
35 for file_id in sr.walk() { 38 for file_id in sr.walk() {
36 dbg!(db.file_relative_path(file_id));
37 let matches = find(&query.pattern, db.parse(file_id).tree().syntax()); 39 let matches = find(&query.pattern, db.parse(file_id).tree().syntax());
38 if !matches.matches.is_empty() { 40 if !matches.matches.is_empty() {
39 edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) }); 41 edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) });
@@ -106,7 +108,10 @@ impl FromStr for SsrQuery {
106 template = replace_in_template(template, var, new_var); 108 template = replace_in_template(template, var, new_var);
107 } 109 }
108 110
109 let template = expr_from_text(&template).syntax().clone(); 111 let template = try_expr_from_text(&template)
112 .ok_or(SsrError("Template is not an expression".into()))?
113 .syntax()
114 .clone();
110 let mut placeholders = FxHashMap::default(); 115 let mut placeholders = FxHashMap::default();
111 116
112 traverse(&template, &mut |n| { 117 traverse(&template, &mut |n| {
@@ -118,7 +123,13 @@ impl FromStr for SsrQuery {
118 } 123 }
119 }); 124 });
120 125
121 let pattern = SsrPattern { pattern: expr_from_text(&pattern).syntax().clone(), vars }; 126 let pattern = SsrPattern {
127 pattern: try_expr_from_text(&pattern)
128 .ok_or(SsrError("Pattern is not an expression".into()))?
129 .syntax()
130 .clone(),
131 vars,
132 };
122 let template = SsrTemplate { template, placeholders }; 133 let template = SsrTemplate { template, placeholders };
123 Ok(SsrQuery { pattern, template }) 134 Ok(SsrQuery { pattern, template })
124 } 135 }
@@ -284,7 +295,6 @@ mod tests {
284 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a"); 295 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a");
285 assert_eq!(result.pattern.vars[1].0, "__search_pattern_b"); 296 assert_eq!(result.pattern.vars[1].0, "__search_pattern_b");
286 assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)"); 297 assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)");
287 dbg!(result.template.placeholders);
288 } 298 }
289 299
290 #[test] 300 #[test]
@@ -335,6 +345,16 @@ mod tests {
335 } 345 }
336 346
337 #[test] 347 #[test]
348 fn parser_invlid_pattern() {
349 assert_eq!(parse_error_text(" ==>> ()"), "Parse error: Pattern is not an expression");
350 }
351
352 #[test]
353 fn parser_invlid_template() {
354 assert_eq!(parse_error_text("() ==>> )"), "Parse error: Template is not an expression");
355 }
356
357 #[test]
338 fn parse_match_replace() { 358 fn parse_match_replace() {
339 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap(); 359 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap();
340 let input = "fn main() { foo(1+2); }"; 360 let input = "fn main() { foo(1+2); }";
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index ae8829807..9f6f1cc53 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -112,10 +112,14 @@ pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
112 let token = token(op); 112 let token = token(op);
113 expr_from_text(&format!("{}{}", token, expr.syntax())) 113 expr_from_text(&format!("{}{}", token, expr.syntax()))
114} 114}
115pub fn expr_from_text(text: &str) -> ast::Expr { 115fn expr_from_text(text: &str) -> ast::Expr {
116 ast_from_text(&format!("const C: () = {};", text)) 116 ast_from_text(&format!("const C: () = {};", text))
117} 117}
118 118
119pub fn try_expr_from_text(text: &str) -> Option<ast::Expr> {
120 try_ast_from_text(&format!("const C: () = {};", text))
121}
122
119pub fn bind_pat(name: ast::Name) -> ast::BindPat { 123pub fn bind_pat(name: ast::Name) -> ast::BindPat {
120 return from_text(name.text()); 124 return from_text(name.text());
121 125
@@ -239,6 +243,16 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
239 node 243 node
240} 244}
241 245
246fn try_ast_from_text<N: AstNode>(text: &str) -> Option<N> {
247 let parse = SourceFile::parse(text);
248 let node = parse.tree().syntax().descendants().find_map(N::cast)?;
249 let node = node.syntax().clone();
250 let node = unroot(node);
251 let node = N::cast(node).unwrap();
252 assert_eq!(node.syntax().text_range().start(), 0.into());
253 Some(node)
254}
255
242fn unroot(n: SyntaxNode) -> SyntaxNode { 256fn unroot(n: SyntaxNode) -> SyntaxNode {
243 SyntaxNode::new_root(n.green().clone()) 257 SyntaxNode::new_root(n.green().clone())
244} 258}
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 8dc6e8dc0..3a31a6dd7 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -906,7 +906,10 @@ pub fn handle_document_highlight(
906 906
907pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { 907pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> {
908 let _p = profile("handle_ssr"); 908 let _p = profile("handle_ssr");
909 world.analysis().structural_search_replace(&params.arg)??.try_conv_with(&world) 909 world
910 .analysis()
911 .structural_search_replace(&params.query, params.parse_only)??
912 .try_conv_with(&world)
910} 913}
911 914
912pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 915pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index a3efe3b9f..435f717ae 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -217,6 +217,8 @@ impl Request for Ssr {
217} 217}
218 218
219#[derive(Debug, Deserialize, Serialize)] 219#[derive(Debug, Deserialize, Serialize)]
220#[serde(rename_all = "camelCase")]
220pub struct SsrParams { 221pub struct SsrParams {
221 pub arg: String, 222 pub query: String,
223 pub parse_only: bool,
222} 224}