22
33mod format_like;
44
5- use hir :: ItemInNs ;
6- use ide_db :: text_edit :: TextEdit ;
5+ use base_db :: SourceDatabase ;
6+ use hir :: { ItemInNs , Semantics } ;
77use ide_db:: {
88 documentation:: { Documentation , HasDocs } ,
99 imports:: insert_use:: ImportScope ,
10+ text_edit:: TextEdit ,
1011 ty_filter:: TryEnum ,
11- SnippetCap ,
12+ RootDatabase , SnippetCap ,
1213} ;
1314use stdx:: never;
1415use syntax:: {
15- ast:: { self , make , AstNode , AstToken } ,
16+ ast:: { self , AstNode , AstToken } ,
1617 SyntaxKind :: { BLOCK_EXPR , EXPR_STMT , FOR_EXPR , IF_EXPR , LOOP_EXPR , STMT_LIST , WHILE_EXPR } ,
1718 TextRange , TextSize ,
1819} ;
@@ -48,7 +49,8 @@ pub(crate) fn complete_postfix(
4849 } ;
4950 let expr_ctx = & dot_access. ctx ;
5051
51- let receiver_text = get_receiver_text ( dot_receiver, receiver_is_ambiguous_float_literal) ;
52+ let receiver_text =
53+ get_receiver_text ( & ctx. sema , dot_receiver, receiver_is_ambiguous_float_literal) ;
5254
5355 let cap = match ctx. config . snippet_cap {
5456 Some ( it) => it,
@@ -172,13 +174,15 @@ pub(crate) fn complete_postfix(
172174 // The rest of the postfix completions create an expression that moves an argument,
173175 // so it's better to consider references now to avoid breaking the compilation
174176
175- let ( dot_receiver, node_to_replace_with) = include_references ( dot_receiver) ;
176- let receiver_text =
177- get_receiver_text ( & node_to_replace_with, receiver_is_ambiguous_float_literal) ;
178- let postfix_snippet = match build_postfix_snippet_builder ( ctx, cap, & dot_receiver) {
179- Some ( it) => it,
180- None => return ,
181- } ;
177+ let ( dot_receiver_including_refs, prefix) = include_references ( dot_receiver) ;
178+ let mut receiver_text =
179+ get_receiver_text ( & ctx. sema , dot_receiver, receiver_is_ambiguous_float_literal) ;
180+ receiver_text. insert_str ( 0 , & prefix) ;
181+ let postfix_snippet =
182+ match build_postfix_snippet_builder ( ctx, cap, & dot_receiver_including_refs) {
183+ Some ( it) => it,
184+ None => return ,
185+ } ;
182186
183187 if !ctx. config . snippets . is_empty ( ) {
184188 add_custom_postfix_completions ( acc, ctx, & postfix_snippet, & receiver_text) ;
@@ -222,7 +226,7 @@ pub(crate) fn complete_postfix(
222226 postfix_snippet ( "call" , "function(expr)" , & format ! ( "${{1}}({receiver_text})" ) )
223227 . add_to ( acc, ctx. db ) ;
224228
225- if let Some ( parent) = dot_receiver . syntax ( ) . parent ( ) . and_then ( |p| p. parent ( ) ) {
229+ if let Some ( parent) = dot_receiver_including_refs . syntax ( ) . parent ( ) . and_then ( |p| p. parent ( ) ) {
226230 if matches ! ( parent. kind( ) , STMT_LIST | EXPR_STMT ) {
227231 postfix_snippet ( "let" , "let" , & format ! ( "let $0 = {receiver_text};" ) )
228232 . add_to ( acc, ctx. db ) ;
@@ -231,9 +235,9 @@ pub(crate) fn complete_postfix(
231235 }
232236 }
233237
234- if let ast:: Expr :: Literal ( literal) = dot_receiver . clone ( ) {
238+ if let ast:: Expr :: Literal ( literal) = dot_receiver_including_refs . clone ( ) {
235239 if let Some ( literal_text) = ast:: String :: cast ( literal. token ( ) ) {
236- add_format_like_completions ( acc, ctx, & dot_receiver , cap, & literal_text) ;
240+ add_format_like_completions ( acc, ctx, & dot_receiver_including_refs , cap, & literal_text) ;
237241 }
238242 }
239243
@@ -260,14 +264,20 @@ pub(crate) fn complete_postfix(
260264 }
261265}
262266
263- fn get_receiver_text ( receiver : & ast:: Expr , receiver_is_ambiguous_float_literal : bool ) -> String {
264- let mut text = if receiver_is_ambiguous_float_literal {
265- let text = receiver. syntax ( ) . text ( ) ;
266- let without_dot = ..text. len ( ) - TextSize :: of ( '.' ) ;
267- text. slice ( without_dot) . to_string ( )
268- } else {
269- receiver. to_string ( )
267+ fn get_receiver_text (
268+ sema : & Semantics < ' _ , RootDatabase > ,
269+ receiver : & ast:: Expr ,
270+ receiver_is_ambiguous_float_literal : bool ,
271+ ) -> String {
272+ // Do not just call `receiver.to_string()`, as that will mess up whitespaces inside macros.
273+ let Some ( mut range) = sema. original_range_opt ( receiver. syntax ( ) ) else {
274+ return receiver. to_string ( ) ;
270275 } ;
276+ if receiver_is_ambiguous_float_literal {
277+ range. range = TextRange :: at ( range. range . start ( ) , range. range . len ( ) - TextSize :: of ( '.' ) )
278+ }
279+ let file_text = sema. db . file_text ( range. file_id . file_id ( ) ) ;
280+ let mut text = file_text[ range. range ] . to_owned ( ) ;
271281
272282 // The receiver texts should be interpreted as-is, as they are expected to be
273283 // normal Rust expressions.
@@ -284,15 +294,15 @@ fn escape_snippet_bits(text: &mut String) {
284294 stdx:: replace ( text, '$' , "\\ $" ) ;
285295}
286296
287- fn include_references ( initial_element : & ast:: Expr ) -> ( ast:: Expr , ast :: Expr ) {
297+ fn include_references ( initial_element : & ast:: Expr ) -> ( ast:: Expr , String ) {
288298 let mut resulting_element = initial_element. clone ( ) ;
289299
290300 while let Some ( field_expr) = resulting_element. syntax ( ) . parent ( ) . and_then ( ast:: FieldExpr :: cast)
291301 {
292302 resulting_element = ast:: Expr :: from ( field_expr) ;
293303 }
294304
295- let mut new_element_opt = initial_element . clone ( ) ;
305+ let mut prefix = String :: new ( ) ;
296306
297307 while let Some ( parent_deref_element) =
298308 resulting_element. syntax ( ) . parent ( ) . and_then ( ast:: PrefixExpr :: cast)
@@ -303,7 +313,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
303313
304314 resulting_element = ast:: Expr :: from ( parent_deref_element) ;
305315
306- new_element_opt = make :: expr_prefix ( syntax :: T ! [ * ] , new_element_opt ) . into ( ) ;
316+ prefix . insert ( 0 , '*' ) ;
307317 }
308318
309319 if let Some ( first_ref_expr) = resulting_element. syntax ( ) . parent ( ) . and_then ( ast:: RefExpr :: cast) {
@@ -317,15 +327,15 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
317327 let exclusive = parent_ref_element. mut_token ( ) . is_some ( ) ;
318328 resulting_element = ast:: Expr :: from ( parent_ref_element) ;
319329
320- new_element_opt = make :: expr_ref ( new_element_opt , exclusive) ;
330+ prefix . insert_str ( 0 , if exclusive { "&mut " } else { "&" } ) ;
321331 }
322332 } else {
323333 // If we do not find any ref expressions, restore
324334 // all the progress of tree climbing
325335 resulting_element = initial_element. clone ( ) ;
326336 }
327337
328- ( resulting_element, new_element_opt )
338+ ( resulting_element, prefix )
329339}
330340
331341fn build_postfix_snippet_builder < ' ctx > (
@@ -901,4 +911,31 @@ fn main() {
901911"# ,
902912 ) ;
903913 }
914+
915+ #[ test]
916+ fn inside_macro ( ) {
917+ check_edit (
918+ "box" ,
919+ r#"
920+ macro_rules! assert {
921+ ( $it:expr $(,)? ) => { $it };
922+ }
923+
924+ fn foo() {
925+ let a = true;
926+ assert!(if a == false { true } else { false }.$0);
927+ }
928+ "# ,
929+ r#"
930+ macro_rules! assert {
931+ ( $it:expr $(,)? ) => { $it };
932+ }
933+
934+ fn foo() {
935+ let a = true;
936+ assert!(Box::new(if a == false { true } else { false }));
937+ }
938+ "# ,
939+ ) ;
940+ }
904941}
0 commit comments