Skip to content

Commit e3a1f1c

Browse files
Merge #3765
3765: Adds sort for RecordLit comparison in SSR r=edwin0cheng a=mikhail-m1 an item from #3186 Co-authored-by: Mikhail Modin <[email protected]>
2 parents 8cce752 + 47e8f3c commit e3a1f1c

File tree

1 file changed

+90
-26
lines changed

1 file changed

+90
-26
lines changed

crates/ra_ide/src/ssr.rs

Lines changed: 90 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
55
use ra_ide_db::symbol_index::SymbolsDatabase;
66
use ra_ide_db::RootDatabase;
77
use ra_syntax::ast::make::try_expr_from_text;
8-
use ra_syntax::ast::{AstToken, Comment};
8+
use ra_syntax::ast::{AstToken, Comment, RecordField, RecordLit};
99
use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
1010
use ra_text_edit::{TextEdit, TextEditBuilder};
1111
use rustc_hash::FxHashMap;
@@ -186,47 +186,102 @@ fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrErr
186186
}
187187

188188
fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
189+
fn check_record_lit(
190+
pattern: RecordLit,
191+
code: RecordLit,
192+
placeholders: &[Var],
193+
match_: Match,
194+
) -> Option<Match> {
195+
let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?;
196+
197+
let mut pattern_fields =
198+
pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
199+
let mut code_fields =
200+
code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
201+
202+
if pattern_fields.len() != code_fields.len() {
203+
return None;
204+
}
205+
206+
let by_name = |a: &RecordField, b: &RecordField| {
207+
a.name_ref()
208+
.map(|x| x.syntax().text().to_string())
209+
.cmp(&b.name_ref().map(|x| x.syntax().text().to_string()))
210+
};
211+
pattern_fields.sort_by(by_name);
212+
code_fields.sort_by(by_name);
213+
214+
pattern_fields.into_iter().zip(code_fields.into_iter()).fold(
215+
Some(match_),
216+
|accum, (a, b)| {
217+
accum.and_then(|match_| check_opt_nodes(Some(a), Some(b), placeholders, match_))
218+
},
219+
)
220+
}
221+
222+
fn check_opt_nodes(
223+
pattern: Option<impl AstNode>,
224+
code: Option<impl AstNode>,
225+
placeholders: &[Var],
226+
match_: Match,
227+
) -> Option<Match> {
228+
match (pattern, code) {
229+
(Some(pattern), Some(code)) => check(
230+
&SyntaxElement::from(pattern.syntax().clone()),
231+
&SyntaxElement::from(code.syntax().clone()),
232+
placeholders,
233+
match_,
234+
),
235+
(None, None) => Some(match_),
236+
_ => None,
237+
}
238+
}
239+
189240
fn check(
190241
pattern: &SyntaxElement,
191242
code: &SyntaxElement,
192243
placeholders: &[Var],
193244
mut match_: Match,
194245
) -> Option<Match> {
195-
match (pattern, code) {
196-
(SyntaxElement::Token(ref pattern), SyntaxElement::Token(ref code)) => {
246+
match (&pattern, &code) {
247+
(SyntaxElement::Token(pattern), SyntaxElement::Token(code)) => {
197248
if pattern.text() == code.text() {
198249
Some(match_)
199250
} else {
200251
None
201252
}
202253
}
203-
(SyntaxElement::Node(ref pattern), SyntaxElement::Node(ref code)) => {
254+
(SyntaxElement::Node(pattern), SyntaxElement::Node(code)) => {
204255
if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) {
205256
match_.binding.insert(Var(pattern.text().to_string()), code.clone());
206257
Some(match_)
207258
} else {
208-
let mut pattern_children = pattern
209-
.children_with_tokens()
210-
.filter(|element| !element.kind().is_trivia());
211-
let mut code_children =
212-
code.children_with_tokens().filter(|element| !element.kind().is_trivia());
213-
let new_ignored_comments = code.children_with_tokens().filter_map(|element| {
214-
element.as_token().and_then(|token| Comment::cast(token.clone()))
215-
});
216-
match_.ignored_comments.extend(new_ignored_comments);
217-
let match_from_children = pattern_children
218-
.by_ref()
219-
.zip(code_children.by_ref())
220-
.fold(Some(match_), |accum, (a, b)| {
221-
accum.and_then(|match_| check(&a, &b, placeholders, match_))
222-
});
223-
match_from_children.and_then(|match_| {
224-
if pattern_children.count() == 0 && code_children.count() == 0 {
225-
Some(match_)
226-
} else {
227-
None
228-
}
229-
})
259+
if let (Some(pattern), Some(code)) =
260+
(RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone()))
261+
{
262+
check_record_lit(pattern, code, placeholders, match_)
263+
} else {
264+
let mut pattern_children = pattern
265+
.children_with_tokens()
266+
.filter(|element| !element.kind().is_trivia());
267+
let mut code_children = code
268+
.children_with_tokens()
269+
.filter(|element| !element.kind().is_trivia());
270+
let new_ignored_comments =
271+
code.children_with_tokens().filter_map(|element| {
272+
element.as_token().and_then(|token| Comment::cast(token.clone()))
273+
});
274+
match_.ignored_comments.extend(new_ignored_comments);
275+
pattern_children
276+
.by_ref()
277+
.zip(code_children.by_ref())
278+
.fold(Some(match_), |accum, (a, b)| {
279+
accum.and_then(|match_| check(&a, &b, placeholders, match_))
280+
})
281+
.filter(|_| {
282+
pattern_children.next().is_none() && code_children.next().is_none()
283+
})
284+
}
230285
}
231286
}
232287
_ => None,
@@ -434,4 +489,13 @@ mod tests {
434489
"fn main() { bar(5)/* using 5 */ }",
435490
)
436491
}
492+
493+
#[test]
494+
fn ssr_struct_lit() {
495+
assert_ssr_transform(
496+
"foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)",
497+
"fn main() { foo{b:2, a:1} }",
498+
"fn main() { foo::new(1, 2) }",
499+
)
500+
}
437501
}

0 commit comments

Comments
 (0)