diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-16 09:48:09 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-16 09:48:09 +0000 |
commit | a99cac671c3e6105a0192acbb1a91cb83e453018 (patch) | |
tree | a6eb701b7fc1a7e83b686a04e498253ee1b34cb0 | |
parent | 6616f336b28bcab7d693473c6d9263dff15639ae (diff) | |
parent | b150965ed7994c679711bc807de301a12f5c7944 (diff) |
Merge #3540
3540: Swtches to rust SSR query check r=matklad a=mikhail-m1
related to #3186
Co-authored-by: Mikhail Modin <[email protected]>
-rw-r--r-- | crates/ra_ide/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide/src/ssr.rs | 36 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 16 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 5 | ||||
-rw-r--r-- | crates/rust-analyzer/src/req.rs | 4 | ||||
-rw-r--r-- | editors/code/src/commands/ssr.ts | 16 | ||||
-rw-r--r-- | editors/code/src/rust-analyzer-api.ts | 3 |
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 | ||
3 | use crate::source_change::SourceFileEdit; | 3 | use crate::source_change::SourceFileEdit; |
4 | use ra_db::{SourceDatabase, SourceDatabaseExt}; | ||
5 | use ra_ide_db::symbol_index::SymbolsDatabase; | ||
4 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
5 | use ra_syntax::ast::make::expr_from_text; | 7 | use ra_syntax::ast::make::try_expr_from_text; |
6 | use ra_syntax::ast::{AstToken, Comment}; | 8 | use ra_syntax::ast::{AstToken, Comment}; |
7 | use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; | 9 | use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; |
8 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 10 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
@@ -10,9 +12,6 @@ use rustc_hash::FxHashMap; | |||
10 | use std::collections::HashMap; | 12 | use std::collections::HashMap; |
11 | use std::str::FromStr; | 13 | use std::str::FromStr; |
12 | 14 | ||
13 | pub use ra_db::{SourceDatabase, SourceDatabaseExt}; | ||
14 | use ra_ide_db::symbol_index::SymbolsDatabase; | ||
15 | |||
16 | #[derive(Debug, PartialEq)] | 15 | #[derive(Debug, PartialEq)] |
17 | pub struct SsrError(String); | 16 | pub struct SsrError(String); |
18 | 17 | ||
@@ -26,14 +25,17 @@ impl std::error::Error for SsrError {} | |||
26 | 25 | ||
27 | pub fn parse_search_replace( | 26 | pub 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 | } |
115 | pub fn expr_from_text(text: &str) -> ast::Expr { | 115 | fn 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 | ||
119 | pub fn try_expr_from_text(text: &str) -> Option<ast::Expr> { | ||
120 | try_ast_from_text(&format!("const C: () = {};", text)) | ||
121 | } | ||
122 | |||
119 | pub fn bind_pat(name: ast::Name) -> ast::BindPat { | 123 | pub 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 | ||
246 | fn 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 | |||
242 | fn unroot(n: SyntaxNode) -> SyntaxNode { | 256 | fn 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 | ||
933 | pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> { | 933 | pub 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(¶ms.arg)??.try_conv_with(&world) | 935 | world |
936 | .analysis() | ||
937 | .structural_search_replace(¶ms.query, params.parse_only)?? | ||
938 | .try_conv_with(&world) | ||
936 | } | 939 | } |
937 | 940 | ||
938 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { | 941 | pub 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")] | ||
221 | pub struct SsrParams { | 222 | pub 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 | ||
109 | export interface SsrParams { | 109 | export interface SsrParams { |
110 | arg: string; | 110 | query: string; |
111 | parseOnly: boolean; | ||
111 | } | 112 | } |
112 | export const ssr = request<SsrParams, SourceChange>("ssr"); | 113 | export const ssr = request<SsrParams, SourceChange>("ssr"); |
113 | 114 | ||