@@ -5,7 +5,7 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
55use ra_ide_db:: symbol_index:: SymbolsDatabase ;
66use ra_ide_db:: RootDatabase ;
77use ra_syntax:: ast:: make:: try_expr_from_text;
8- use ra_syntax:: ast:: { AstToken , Comment } ;
8+ use ra_syntax:: ast:: { AstToken , Comment , RecordField , RecordLit } ;
99use ra_syntax:: { AstNode , SyntaxElement , SyntaxNode } ;
1010use ra_text_edit:: { TextEdit , TextEditBuilder } ;
1111use rustc_hash:: FxHashMap ;
@@ -186,47 +186,102 @@ fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrErr
186186}
187187
188188fn 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