2
2
3
3
mod format_like;
4
4
5
- use hir :: ItemInNs ;
6
- use ide_db :: text_edit :: TextEdit ;
5
+ use base_db :: SourceDatabase ;
6
+ use hir :: { ItemInNs , Semantics } ;
7
7
use ide_db:: {
8
8
documentation:: { Documentation , HasDocs } ,
9
9
imports:: insert_use:: ImportScope ,
10
+ text_edit:: TextEdit ,
10
11
ty_filter:: TryEnum ,
11
- SnippetCap ,
12
+ RootDatabase , SnippetCap ,
12
13
} ;
13
14
use stdx:: never;
14
15
use syntax:: {
15
- ast:: { self , make , AstNode , AstToken } ,
16
+ ast:: { self , AstNode , AstToken } ,
16
17
SyntaxKind :: { BLOCK_EXPR , EXPR_STMT , FOR_EXPR , IF_EXPR , LOOP_EXPR , STMT_LIST , WHILE_EXPR } ,
17
18
TextRange , TextSize ,
18
19
} ;
@@ -48,7 +49,8 @@ pub(crate) fn complete_postfix(
48
49
} ;
49
50
let expr_ctx = & dot_access. ctx ;
50
51
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) ;
52
54
53
55
let cap = match ctx. config . snippet_cap {
54
56
Some ( it) => it,
@@ -172,13 +174,15 @@ pub(crate) fn complete_postfix(
172
174
// The rest of the postfix completions create an expression that moves an argument,
173
175
// so it's better to consider references now to avoid breaking the compilation
174
176
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
+ } ;
182
186
183
187
if !ctx. config . snippets . is_empty ( ) {
184
188
add_custom_postfix_completions ( acc, ctx, & postfix_snippet, & receiver_text) ;
@@ -222,7 +226,7 @@ pub(crate) fn complete_postfix(
222
226
postfix_snippet ( "call" , "function(expr)" , & format ! ( "${{1}}({receiver_text})" ) )
223
227
. add_to ( acc, ctx. db ) ;
224
228
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 ( ) ) {
226
230
if matches ! ( parent. kind( ) , STMT_LIST | EXPR_STMT ) {
227
231
postfix_snippet ( "let" , "let" , & format ! ( "let $0 = {receiver_text};" ) )
228
232
. add_to ( acc, ctx. db ) ;
@@ -231,9 +235,9 @@ pub(crate) fn complete_postfix(
231
235
}
232
236
}
233
237
234
- if let ast:: Expr :: Literal ( literal) = dot_receiver . clone ( ) {
238
+ if let ast:: Expr :: Literal ( literal) = dot_receiver_including_refs . clone ( ) {
235
239
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) ;
237
241
}
238
242
}
239
243
@@ -260,14 +264,20 @@ pub(crate) fn complete_postfix(
260
264
}
261
265
}
262
266
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 ( ) ;
270
275
} ;
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 ( ) ;
271
281
272
282
// The receiver texts should be interpreted as-is, as they are expected to be
273
283
// normal Rust expressions.
@@ -284,15 +294,15 @@ fn escape_snippet_bits(text: &mut String) {
284
294
stdx:: replace ( text, '$' , "\\ $" ) ;
285
295
}
286
296
287
- fn include_references ( initial_element : & ast:: Expr ) -> ( ast:: Expr , ast :: Expr ) {
297
+ fn include_references ( initial_element : & ast:: Expr ) -> ( ast:: Expr , String ) {
288
298
let mut resulting_element = initial_element. clone ( ) ;
289
299
290
300
while let Some ( field_expr) = resulting_element. syntax ( ) . parent ( ) . and_then ( ast:: FieldExpr :: cast)
291
301
{
292
302
resulting_element = ast:: Expr :: from ( field_expr) ;
293
303
}
294
304
295
- let mut new_element_opt = initial_element . clone ( ) ;
305
+ let mut prefix = String :: new ( ) ;
296
306
297
307
while let Some ( parent_deref_element) =
298
308
resulting_element. syntax ( ) . parent ( ) . and_then ( ast:: PrefixExpr :: cast)
@@ -303,7 +313,7 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) {
303
313
304
314
resulting_element = ast:: Expr :: from ( parent_deref_element) ;
305
315
306
- new_element_opt = make :: expr_prefix ( syntax :: T ! [ * ] , new_element_opt ) . into ( ) ;
316
+ prefix . insert ( 0 , '*' ) ;
307
317
}
308
318
309
319
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) {
317
327
let exclusive = parent_ref_element. mut_token ( ) . is_some ( ) ;
318
328
resulting_element = ast:: Expr :: from ( parent_ref_element) ;
319
329
320
- new_element_opt = make :: expr_ref ( new_element_opt , exclusive) ;
330
+ prefix . insert_str ( 0 , if exclusive { "&mut " } else { "&" } ) ;
321
331
}
322
332
} else {
323
333
// If we do not find any ref expressions, restore
324
334
// all the progress of tree climbing
325
335
resulting_element = initial_element. clone ( ) ;
326
336
}
327
337
328
- ( resulting_element, new_element_opt )
338
+ ( resulting_element, prefix )
329
339
}
330
340
331
341
fn build_postfix_snippet_builder < ' ctx > (
@@ -901,4 +911,31 @@ fn main() {
901
911
"# ,
902
912
) ;
903
913
}
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
+ }
904
941
}
0 commit comments