Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/inline_macro.rs')
| -rw-r--r-- | crates/ide-assists/src/handlers/inline_macro.rs | 67 |
1 files changed, 59 insertions, 8 deletions
diff --git a/crates/ide-assists/src/handlers/inline_macro.rs b/crates/ide-assists/src/handlers/inline_macro.rs index 002791b88d..f4ac75d229 100644 --- a/crates/ide-assists/src/handlers/inline_macro.rs +++ b/crates/ide-assists/src/handlers/inline_macro.rs @@ -1,8 +1,11 @@ use hir::db::ExpandDatabase; use ide_db::syntax_helpers::prettify_macro_expansion; -use syntax::ast::{self, AstNode, edit::AstNodeEdit}; +use syntax::ast::{self, AstNode, edit::IndentLevel}; -use crate::{AssistContext, AssistId, Assists}; +use crate::{ + AssistContext, AssistId, Assists, + utils::{cover_edit_range, original_range_in}, +}; // Assist: inline_macro // @@ -36,24 +39,40 @@ use crate::{AssistContext, AssistId, Assists}; // } // ``` pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { - let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?; - let macro_call = ctx.sema.to_def(&unexpanded)?; + let source = ctx.source_file().syntax(); + let sel = ctx.selection_trimmed(); + let (macro_call, text_range) = ctx + .sema + .find_nodes_at_offset_with_descend::<ast::MacroCall>(source, ctx.offset()) + .find_map(|macro_call_node| { + let macro_call = ctx.sema.to_def(¯o_call_node)?; + let original_range = + original_range_in(ctx.file_id(), &ctx.sema, macro_call_node.syntax())?; + original_range.contains_range(sel).then_some((macro_call, original_range)) + })?; let target_crate_id = ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into(); - let text_range = unexpanded.syntax().text_range(); acc.add( AssistId::refactor_inline("inline_macro"), "Inline macro".to_owned(), text_range, |builder| { - let editor = builder.make_editor(unexpanded.syntax()); let expanded = ctx.sema.parse_or_expand(macro_call.into()); let span_map = ctx.sema.db.expansion_span_map(macro_call); // Don't call `prettify_macro_expansion()` outside the actual assist action; it does some heavy rowan tree manipulation, // which can be very costly for big macros when it is done *even without the assist being invoked*. let expanded = prettify_macro_expansion(ctx.db(), expanded, span_map, target_crate_id); - let expanded = ast::edit::indent(&expanded, unexpanded.indent_level()); - editor.replace(unexpanded.syntax(), expanded); + + // macro_call is from an expansion, use source position for indent + let indent = source + .token_at_offset(text_range.start()) + .right_biased() + .map_or_else(IndentLevel::zero, |t| IndentLevel::from_token(&t)); + let expanded = ast::edit::indent(&expanded, indent); + + let editor = builder.make_editor(source); + let place = cover_edit_range(source, text_range); + editor.replace_all(place, vec![expanded.into()]); builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -103,6 +122,7 @@ macro_rules! num { "# }; } + #[test] fn inline_macro_target() { check_assist_target( @@ -379,4 +399,35 @@ fn bar() { "#, ); } + + #[test] + fn inline_macro_in_macro() { + check_assist( + inline_macro, + r#" +macro_rules! foo { () => { 2 }; } +macro_rules! m { ($($tt:tt)*) => { $($tt)* }; } +fn f() { m! { $0foo!(); } } +"#, + r#" +macro_rules! foo { () => { 2 }; } +macro_rules! m { ($($tt:tt)*) => { $($tt)* }; } +fn f() { m! { 2; } } +"#, + ); + check_assist( + inline_macro, + r#" +//- proc_macros: identity +macro_rules! foo { () => { 2 }; } +#[proc_macros::identity] +fn f() { $0foo!(); } +"#, + r#" +macro_rules! foo { () => { 2 }; } +#[proc_macros::identity] +fn f() { 2; } +"#, + ); + } } |