@@ -5,7 +5,7 @@ use ra_db::{SourceDatabase, SourceDatabaseExt};
5
5
use ra_ide_db:: symbol_index:: SymbolsDatabase ;
6
6
use ra_ide_db:: RootDatabase ;
7
7
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 } ;
9
9
use ra_syntax:: { AstNode , SyntaxElement , SyntaxNode } ;
10
10
use ra_text_edit:: { TextEdit , TextEditBuilder } ;
11
11
use 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
188
188
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
+
189
240
fn check (
190
241
pattern : & SyntaxElement ,
191
242
code : & SyntaxElement ,
192
243
placeholders : & [ Var ] ,
193
244
mut match_ : Match ,
194
245
) -> 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) ) => {
197
248
if pattern. text ( ) == code. text ( ) {
198
249
Some ( match_)
199
250
} else {
200
251
None
201
252
}
202
253
}
203
- ( SyntaxElement :: Node ( ref pattern) , SyntaxElement :: Node ( ref code) ) => {
254
+ ( SyntaxElement :: Node ( pattern) , SyntaxElement :: Node ( code) ) => {
204
255
if placeholders. iter ( ) . any ( |n| n. 0 . as_str ( ) == pattern. text ( ) ) {
205
256
match_. binding . insert ( Var ( pattern. text ( ) . to_string ( ) ) , code. clone ( ) ) ;
206
257
Some ( match_)
207
258
} 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
+ }
230
285
}
231
286
}
232
287
_ => None ,
@@ -434,4 +489,13 @@ mod tests {
434
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
}
0 commit comments