aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide/src/ssr.rs116
1 files changed, 90 insertions, 26 deletions
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 1c9710a5d..1abb891c1 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -5,7 +5,7 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
5use ra_ide_db::symbol_index::SymbolsDatabase; 5use ra_ide_db::symbol_index::SymbolsDatabase;
6use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
7use ra_syntax::ast::make::try_expr_from_text; 7use ra_syntax::ast::make::try_expr_from_text;
8use ra_syntax::ast::{AstToken, Comment}; 8use ra_syntax::ast::{AstToken, Comment, RecordField, RecordLit};
9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; 9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
10use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
11use rustc_hash::FxHashMap; 11use rustc_hash::FxHashMap;
@@ -186,47 +186,102 @@ fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrErr
186} 186}
187 187
188fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches { 188fn 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
189 fn check( 240 fn check(
190 pattern: &SyntaxElement, 241 pattern: &SyntaxElement,
191 code: &SyntaxElement, 242 code: &SyntaxElement,
192 placeholders: &[Var], 243 placeholders: &[Var],
193 mut match_: Match, 244 mut match_: Match,
194 ) -> Option<Match> { 245 ) -> Option<Match> {
195 match (pattern, code) { 246 match (&pattern, &code) {
196 (SyntaxElement::Token(ref pattern), SyntaxElement::Token(ref code)) => { 247 (SyntaxElement::Token(pattern), SyntaxElement::Token(code)) => {
197 if pattern.text() == code.text() { 248 if pattern.text() == code.text() {
198 Some(match_) 249 Some(match_)
199 } else { 250 } else {
200 None 251 None
201 } 252 }
202 } 253 }
203 (SyntaxElement::Node(ref pattern), SyntaxElement::Node(ref code)) => { 254 (SyntaxElement::Node(pattern), SyntaxElement::Node(code)) => {
204 if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) { 255 if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) {
205 match_.binding.insert(Var(pattern.text().to_string()), code.clone()); 256 match_.binding.insert(Var(pattern.text().to_string()), code.clone());
206 Some(match_) 257 Some(match_)
207 } else { 258 } else {
208 let mut pattern_children = pattern 259 if let (Some(pattern), Some(code)) =
209 .children_with_tokens() 260 (RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone()))
210 .filter(|element| !element.kind().is_trivia()); 261 {
211 let mut code_children = 262 check_record_lit(pattern, code, placeholders, match_)
212 code.children_with_tokens().filter(|element| !element.kind().is_trivia()); 263 } else {
213 let new_ignored_comments = code.children_with_tokens().filter_map(|element| { 264 let mut pattern_children = pattern
214 element.as_token().and_then(|token| Comment::cast(token.clone())) 265 .children_with_tokens()
215 }); 266 .filter(|element| !element.kind().is_trivia());
216 match_.ignored_comments.extend(new_ignored_comments); 267 let mut code_children = code
217 let match_from_children = pattern_children 268 .children_with_tokens()
218 .by_ref() 269 .filter(|element| !element.kind().is_trivia());
219 .zip(code_children.by_ref()) 270 let new_ignored_comments =
220 .fold(Some(match_), |accum, (a, b)| { 271 code.children_with_tokens().filter_map(|element| {
221 accum.and_then(|match_| check(&a, &b, placeholders, match_)) 272 element.as_token().and_then(|token| Comment::cast(token.clone()))
222 }); 273 });
223 match_from_children.and_then(|match_| { 274 match_.ignored_comments.extend(new_ignored_comments);
224 if pattern_children.count() == 0 && code_children.count() == 0 { 275 pattern_children
225 Some(match_) 276 .by_ref()
226 } else { 277 .zip(code_children.by_ref())
227 None 278 .fold(Some(match_), |accum, (a, b)| {
228 } 279 accum.and_then(|match_| check(&a, &b, placeholders, match_))
229 }) 280 })
281 .filter(|_| {
282 pattern_children.next().is_none() && code_children.next().is_none()
283 })
284 }
230 } 285 }
231 } 286 }
232 _ => None, 287 _ => None,
@@ -434,4 +489,13 @@ mod tests {
434 "fn main() { bar(5)/* using 5 */ }", 489 "fn main() { bar(5)/* using 5 */ }",
435 ) 490 )
436 } 491 }
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 }
437} 501}