Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 28 additions & 38 deletions crates/ide-assists/src/handlers/unqualify_method_call.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use hir::AsAssocItem;
use syntax::{
TextRange,
ast::{self, AstNode, HasArgList, prec::ExprPrecedence},
};
use syntax::ast::{self, AstNode, HasArgList, prec::ExprPrecedence, syntax_factory::SyntaxFactory};

use crate::{AssistContext, AssistId, Assists};

Expand Down Expand Up @@ -36,10 +33,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>)
}

let args = call.arg_list()?;
let l_paren = args.l_paren_token()?;
let mut args_iter = args.args();
let first_arg = args_iter.next()?;
let second_arg = args_iter.next();
let first_arg = args.args().next()?;

let qualifier = path.qualifier()?;
let method_name = path.segment()?.name_ref()?;
Expand All @@ -51,51 +45,42 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>)
return None;
}

// `core::ops::Add::add(` -> ``
let delete_path =
TextRange::new(path.syntax().text_range().start(), l_paren.text_range().end());

// Parens around `expr` if needed
let parens = first_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix).then(|| {
let range = first_arg.syntax().text_range();
(range.start(), range.end())
});

// `, ` -> `.add(`
let replace_comma = TextRange::new(
first_arg.syntax().text_range().end(),
second_arg
.map(|a| a.syntax().text_range().start())
.unwrap_or_else(|| first_arg.syntax().text_range().end()),
);

acc.add(
AssistId::refactor_rewrite("unqualify_method_call"),
"Unqualify method call",
call.syntax().text_range(),
|edit| {
edit.delete(delete_path);
if let Some((open, close)) = parens {
edit.insert(open, "(");
edit.insert(close, ")");
}
edit.replace(replace_comma, format!(".{method_name}("));
|builder| {
let make = SyntaxFactory::with_mappings();
let mut editor = builder.make_editor(call.syntax());

let new_arg_list = make.arg_list(args.args().skip(1));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will lose the trivia between args, such as a multi line ArgList, but it is also imperfect before migration, it is okay

let receiver = if first_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix) {
ast::Expr::from(make.expr_paren(first_arg.clone()))
} else {
first_arg.clone()
};
let method_call = make.expr_method_call(receiver, method_name, new_arg_list);

editor.replace(call.syntax(), method_call.syntax());

if let Some(fun) = fun.as_assoc_item(ctx.db())
&& let Some(trait_) = fun.container_or_implemented_trait(ctx.db())
&& !scope.can_use_trait_methods(trait_)
{
// Only add an import for trait methods that are not already imported.
add_import(qualifier, ctx, edit);
add_import(qualifier, ctx, &make, &mut editor);
}

editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}

fn add_import(
qualifier: ast::Path,
ctx: &AssistContext<'_>,
edit: &mut ide_db::source_change::SourceChangeBuilder,
make: &SyntaxFactory,
editor: &mut syntax::syntax_editor::SyntaxEditor,
) {
if let Some(path_segment) = qualifier.segment() {
// for `<i32 as std::ops::Add>`
Expand All @@ -122,8 +107,13 @@ fn add_import(
);

if let Some(scope) = scope {
let scope = edit.make_import_scope_mut(scope);
ide_db::imports::insert_use::insert_use(&scope, import, &ctx.config.insert_use);
ide_db::imports::insert_use::insert_use_with_editor(
&scope,
import,
&ctx.config.insert_use,
editor,
make,
);
}
}
}
Expand Down