aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--editors/code/src/commands/ssr.ts16
-rw-r--r--editors/code/src/rust-analyzer-api.ts3
7 files changed, 63 insertions, 20 deletions
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index e9af80b6c..5ab06c6cf 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -473,9 +473,10 @@ impl Analysis {
473 pub fn structural_search_replace( 473 pub fn structural_search_replace(
474 &self, 474 &self,
475 query: &str, 475 query: &str,
476 parse_only: bool,
476 ) -> Cancelable<Result<SourceChange, SsrError>> { 477 ) -> Cancelable<Result<SourceChange, SsrError>> {
477 self.with_db(|db| { 478 self.with_db(|db| {
478 let edits = ssr::parse_search_replace(query, db)?; 479 let edits = ssr::parse_search_replace(query, parse_only, db)?;
479 Ok(SourceChange::source_file_edits("ssr", edits)) 480 Ok(SourceChange::source_file_edits("ssr", edits))
480 }) 481 })
481 } 482 }
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 df3622d61..1cc2f6571 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -932,7 +932,10 @@ pub fn handle_document_highlight(
932 932
933pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { 933pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> {
934 let _p = profile("handle_ssr"); 934 let _p = profile("handle_ssr");
935 world.analysis().structural_search_replace(&params.arg)??.try_conv_with(&world) 935 world
936 .analysis()
937 .structural_search_replace(&params.query, params.parse_only)??
938 .try_conv_with(&world)
936} 939}
937 940
938pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 941pub 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 156328df8..9e27d3f1c 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -218,6 +218,8 @@ impl Request for Ssr {
218} 218}
219 219
220#[derive(Debug, Deserialize, Serialize)] 220#[derive(Debug, Deserialize, Serialize)]
221#[serde(rename_all = "camelCase")]
221pub struct SsrParams { 222pub struct SsrParams {
222 pub arg: String, 223 pub query: String,
224 pub parse_only: bool,
223} 225}
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts
index eee48c693..6fee051fd 100644
--- a/editors/code/src/commands/ssr.ts
+++ b/editors/code/src/commands/ssr.ts
@@ -10,20 +10,22 @@ export function ssr(ctx: Ctx): Cmd {
10 if (!client) return; 10 if (!client) return;
11 11
12 const options: vscode.InputBoxOptions = { 12 const options: vscode.InputBoxOptions = {
13 placeHolder: "foo($a:expr, $b:expr) ==>> bar($a, foo($b))", 13 value: "() ==>> ()",
14 prompt: "Enter request", 14 prompt: "EnteR request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
15 validateInput: (x: string) => { 15 validateInput: async (x: string) => {
16 if (x.includes('==>>')) { 16 try {
17 return null; 17 await client.sendRequest(ra.ssr, { query: x, parseOnly: true });
18 } catch (e) {
19 return e.toString();
18 } 20 }
19 return "Enter request: pattern ==>> template"; 21 return null;
20 } 22 }
21 }; 23 };
22 const request = await vscode.window.showInputBox(options); 24 const request = await vscode.window.showInputBox(options);
23 25
24 if (!request) return; 26 if (!request) return;
25 27
26 const change = await client.sendRequest(ra.ssr, { arg: request }); 28 const change = await client.sendRequest(ra.ssr, { query: request, parseOnly: false });
27 29
28 await applySourceChange(ctx, change); 30 await applySourceChange(ctx, change);
29 }; 31 };
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
index e09a203c9..9846f7343 100644
--- a/editors/code/src/rust-analyzer-api.ts
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -107,7 +107,8 @@ export const inlayHints = request<InlayHintsParams, Vec<InlayHint>>("inlayHints"
107 107
108 108
109export interface SsrParams { 109export interface SsrParams {
110 arg: string; 110 query: string;
111 parseOnly: boolean;
111} 112}
112export const ssr = request<SsrParams, SourceChange>("ssr"); 113export const ssr = request<SsrParams, SourceChange>("ssr");
113 114