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.rs67
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(&macro_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; }
+"#,
+ );
+ }
}