Skip to content

Commit a99cac6

Browse files
Merge #3540
3540: Swtches to rust SSR query check r=matklad a=mikhail-m1 related to #3186 Co-authored-by: Mikhail Modin <[email protected]>
2 parents 6616f33 + b150965 commit a99cac6

File tree

7 files changed

+63
-20
lines changed

7 files changed

+63
-20
lines changed

crates/ra_ide/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,9 +473,10 @@ impl Analysis {
473473
pub fn structural_search_replace(
474474
&self,
475475
query: &str,
476+
parse_only: bool,
476477
) -> Cancelable<Result<SourceChange, SsrError>> {
477478
self.with_db(|db| {
478-
let edits = ssr::parse_search_replace(query, db)?;
479+
let edits = ssr::parse_search_replace(query, parse_only, db)?;
479480
Ok(SourceChange::source_file_edits("ssr", edits))
480481
})
481482
}

crates/ra_ide/src/ssr.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
//! structural search replace
22
33
use crate::source_change::SourceFileEdit;
4+
use ra_db::{SourceDatabase, SourceDatabaseExt};
5+
use ra_ide_db::symbol_index::SymbolsDatabase;
46
use ra_ide_db::RootDatabase;
5-
use ra_syntax::ast::make::expr_from_text;
7+
use ra_syntax::ast::make::try_expr_from_text;
68
use ra_syntax::ast::{AstToken, Comment};
79
use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
810
use ra_text_edit::{TextEdit, TextEditBuilder};
911
use rustc_hash::FxHashMap;
1012
use std::collections::HashMap;
1113
use std::str::FromStr;
1214

13-
pub use ra_db::{SourceDatabase, SourceDatabaseExt};
14-
use ra_ide_db::symbol_index::SymbolsDatabase;
15-
1615
#[derive(Debug, PartialEq)]
1716
pub struct SsrError(String);
1817

@@ -26,14 +25,17 @@ impl std::error::Error for SsrError {}
2625

2726
pub fn parse_search_replace(
2827
query: &str,
28+
parse_only: bool,
2929
db: &RootDatabase,
3030
) -> Result<Vec<SourceFileEdit>, SsrError> {
3131
let mut edits = vec![];
3232
let query: SsrQuery = query.parse()?;
33+
if parse_only {
34+
return Ok(edits);
35+
}
3336
for &root in db.local_roots().iter() {
3437
let sr = db.source_root(root);
3538
for file_id in sr.walk() {
36-
dbg!(db.file_relative_path(file_id));
3739
let matches = find(&query.pattern, db.parse(file_id).tree().syntax());
3840
if !matches.matches.is_empty() {
3941
edits.push(SourceFileEdit { file_id, edit: replace(&matches, &query.template) });
@@ -106,7 +108,10 @@ impl FromStr for SsrQuery {
106108
template = replace_in_template(template, var, new_var);
107109
}
108110

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();
110115
let mut placeholders = FxHashMap::default();
111116

112117
traverse(&template, &mut |n| {
@@ -118,7 +123,13 @@ impl FromStr for SsrQuery {
118123
}
119124
});
120125

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+
};
122133
let template = SsrTemplate { template, placeholders };
123134
Ok(SsrQuery { pattern, template })
124135
}
@@ -284,7 +295,6 @@ mod tests {
284295
assert_eq!(result.pattern.vars[0].0, "__search_pattern_a");
285296
assert_eq!(result.pattern.vars[1].0, "__search_pattern_b");
286297
assert_eq!(&result.template.template.text(), "bar(__search_pattern_b, __search_pattern_a)");
287-
dbg!(result.template.placeholders);
288298
}
289299

290300
#[test]
@@ -334,6 +344,16 @@ mod tests {
334344
);
335345
}
336346

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+
337357
#[test]
338358
fn parse_match_replace() {
339359
let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap();

crates/ra_syntax/src/ast/make.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,14 @@ pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
112112
let token = token(op);
113113
expr_from_text(&format!("{}{}", token, expr.syntax()))
114114
}
115-
pub fn expr_from_text(text: &str) -> ast::Expr {
115+
fn expr_from_text(text: &str) -> ast::Expr {
116116
ast_from_text(&format!("const C: () = {};", text))
117117
}
118118

119+
pub fn try_expr_from_text(text: &str) -> Option<ast::Expr> {
120+
try_ast_from_text(&format!("const C: () = {};", text))
121+
}
122+
119123
pub fn bind_pat(name: ast::Name) -> ast::BindPat {
120124
return from_text(name.text());
121125

@@ -239,6 +243,16 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
239243
node
240244
}
241245

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+
242256
fn unroot(n: SyntaxNode) -> SyntaxNode {
243257
SyntaxNode::new_root(n.green().clone())
244258
}

crates/rust-analyzer/src/main_loop/handlers.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,10 @@ pub fn handle_document_highlight(
932932

933933
pub fn handle_ssr(world: WorldSnapshot, params: req::SsrParams) -> Result<req::SourceChange> {
934934
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)
936939
}
937940

938941
pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> {

crates/rust-analyzer/src/req.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ impl Request for Ssr {
218218
}
219219

220220
#[derive(Debug, Deserialize, Serialize)]
221+
#[serde(rename_all = "camelCase")]
221222
pub struct SsrParams {
222-
pub arg: String,
223+
pub query: String,
224+
pub parse_only: bool,
223225
}

editors/code/src/commands/ssr.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,22 @@ export function ssr(ctx: Ctx): Cmd {
1010
if (!client) return;
1111

1212
const options: vscode.InputBoxOptions = {
13-
placeHolder: "foo($a:expr, $b:expr) ==>> bar($a, foo($b))",
14-
prompt: "Enter request",
15-
validateInput: (x: string) => {
16-
if (x.includes('==>>')) {
17-
return null;
13+
value: "() ==>> ()",
14+
prompt: "EnteR request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
15+
validateInput: async (x: string) => {
16+
try {
17+
await client.sendRequest(ra.ssr, { query: x, parseOnly: true });
18+
} catch (e) {
19+
return e.toString();
1820
}
19-
return "Enter request: pattern ==>> template";
21+
return null;
2022
}
2123
};
2224
const request = await vscode.window.showInputBox(options);
2325

2426
if (!request) return;
2527

26-
const change = await client.sendRequest(ra.ssr, { arg: request });
28+
const change = await client.sendRequest(ra.ssr, { query: request, parseOnly: false });
2729

2830
await applySourceChange(ctx, change);
2931
};

editors/code/src/rust-analyzer-api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ export const inlayHints = request<InlayHintsParams, Vec<InlayHint>>("inlayHints"
107107

108108

109109
export interface SsrParams {
110-
arg: string;
110+
query: string;
111+
parseOnly: boolean;
111112
}
112113
export const ssr = request<SsrParams, SourceChange>("ssr");
113114

0 commit comments

Comments
 (0)