11use hir:: Semantics ;
2- use ide_db:: { RootDatabase , assists:: AssistId , defs:: Definition } ;
2+ use ide_db:: { RootDatabase , assists:: AssistId , defs:: Definition , famous_defs :: FamousDefs } ;
33use syntax:: {
44 AstNode ,
5- ast:: { self , Expr , HasArgList , make , syntax_factory:: SyntaxFactory } ,
5+ ast:: { self , Expr , HasArgList , syntax_factory:: SyntaxFactory } ,
66} ;
77
88use crate :: { AssistContext , Assists , utils:: wrap_paren_in_call} ;
@@ -64,9 +64,26 @@ pub(crate) fn replace_with_lazy_method(
6464 format ! ( "Replace {method_name} with {method_name_lazy}" ) ,
6565 call. syntax ( ) . text_range ( ) ,
6666 |builder| {
67- let closured = into_closure ( & last_arg, & method_name_lazy) ;
68- builder. replace ( method_name. syntax ( ) . text_range ( ) , method_name_lazy) ;
69- builder. replace_ast ( last_arg, closured) ;
67+ let editor = builder. make_editor ( call. syntax ( ) ) ;
68+ let add_param = match & * method_name_lazy {
69+ "and_then" => true ,
70+ "or_else" | "unwrap_or_else" => {
71+ FamousDefs ( & ctx. sema , scope. krate ( ) ) . core_result_Result ( ) . is_some_and (
72+ |result| result. ty ( ctx. db ( ) ) . could_unify_with ( ctx. db ( ) , & receiver_ty) ,
73+ )
74+ }
75+ _ => false ,
76+ } ;
77+ let closured = into_closure ( & last_arg, add_param, editor. make ( ) ) ;
78+ editor. replace ( method_name. syntax ( ) , editor. make ( ) . name ( & method_name_lazy) . syntax ( ) ) ;
79+ editor. replace ( last_arg. syntax ( ) , closured. syntax ( ) ) ;
80+ if let Some ( cap) = ctx. config . snippet_cap
81+ && let ast:: Expr :: ClosureExpr ( closured) = closured
82+ && let Some ( param) = closured. param_list ( ) . and_then ( |it| it. params ( ) . next ( ) )
83+ {
84+ editor. add_annotation ( param. syntax ( ) , builder. make_placeholder_snippet ( cap) ) ;
85+ }
86+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
7087 } ,
7188 )
7289}
@@ -83,7 +100,7 @@ fn lazy_method_name(name: &str) -> String {
83100 }
84101}
85102
86- fn into_closure ( param : & Expr , name_lazy : & str ) -> Expr {
103+ fn into_closure ( param : & Expr , add_param : bool , make : & SyntaxFactory ) -> Expr {
87104 ( || {
88105 if let ast:: Expr :: CallExpr ( call) = param {
89106 if call. arg_list ( ) ?. args ( ) . count ( ) == 0 { Some ( call. expr ( ) ?) } else { None }
@@ -92,9 +109,8 @@ fn into_closure(param: &Expr, name_lazy: &str) -> Expr {
92109 }
93110 } ) ( )
94111 . unwrap_or_else ( || {
95- let pats = ( name_lazy == "and_then" )
96- . then ( || make:: untyped_param ( make:: ext:: simple_ident_pat ( make:: name ( "it" ) ) . into ( ) ) ) ;
97- make:: expr_closure ( pats, param. clone ( ) ) . into ( )
112+ let pats = add_param. then ( || make. untyped_param ( make. wildcard_pat ( ) . into ( ) ) ) ;
113+ make. expr_closure ( pats, param. clone ( ) ) . into ( )
98114 } )
99115}
100116
@@ -156,14 +172,16 @@ pub(crate) fn replace_with_eager_method(
156172 format ! ( "Replace {method_name} with {method_name_eager}" ) ,
157173 call. syntax ( ) . text_range ( ) ,
158174 |builder| {
159- builder. replace ( method_name. syntax ( ) . text_range ( ) , method_name_eager) ;
160- let called = into_call ( & last_arg, & ctx. sema ) ;
161- builder. replace_ast ( last_arg, called) ;
175+ let editor = builder. make_editor ( call. syntax ( ) ) ;
176+ let called = into_call ( & last_arg, & ctx. sema , editor. make ( ) ) ;
177+ editor. replace ( method_name. syntax ( ) , editor. make ( ) . name ( method_name_eager) . syntax ( ) ) ;
178+ editor. replace ( last_arg. syntax ( ) , called. syntax ( ) ) ;
179+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
162180 } ,
163181 )
164182}
165183
166- fn into_call ( param : & Expr , sema : & Semantics < ' _ , RootDatabase > ) -> Expr {
184+ fn into_call ( param : & Expr , sema : & Semantics < ' _ , RootDatabase > , make : & SyntaxFactory ) -> Expr {
167185 ( || {
168186 if let ast:: Expr :: ClosureExpr ( closure) = param {
169187 let mut params = closure. param_list ( ) ?. params ( ) ;
@@ -183,8 +201,8 @@ fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr {
183201 }
184202 } ) ( )
185203 . unwrap_or_else ( || {
186- let callable = wrap_paren_in_call ( param. clone ( ) , & SyntaxFactory :: without_mappings ( ) ) ;
187- make:: expr_call ( callable, make:: arg_list ( Vec :: new ( ) ) ) . into ( )
204+ let callable = wrap_paren_in_call ( param. clone ( ) , make ) ;
205+ make. expr_call ( callable, make. arg_list ( Vec :: new ( ) ) ) . into ( )
188206 } )
189207}
190208
@@ -213,7 +231,7 @@ mod tests {
213231 check_assist (
214232 replace_with_lazy_method,
215233 r#"
216- //- minicore: option, fn
234+ //- minicore: option, result, fn
217235fn foo() {
218236 let foo = Some(1);
219237 return foo.unwrap_$0or(2);
@@ -228,6 +246,26 @@ fn foo() {
228246 )
229247 }
230248
249+ #[ test]
250+ fn replace_or_with_or_else_with_parameter ( ) {
251+ check_assist (
252+ replace_with_lazy_method,
253+ r#"
254+ //- minicore: option, result, fn
255+ fn foo() {
256+ let foo = Ok(1);
257+ return foo.unwrap_$0or(2);
258+ }
259+ "# ,
260+ r#"
261+ fn foo() {
262+ let foo = Ok(1);
263+ return foo.unwrap_or_else(|${0:_}| 2);
264+ }
265+ "# ,
266+ )
267+ }
268+
231269 #[ test]
232270 fn replace_or_with_or_else_call ( ) {
233271 check_assist (
@@ -358,7 +396,7 @@ fn foo() {
358396 r#"
359397fn foo() {
360398 let foo = Some("foo");
361- return foo.and_then(|it | Some("bar"));
399+ return foo.and_then(|${0:_} | Some("bar"));
362400}
363401"# ,
364402 )
0 commit comments