diff --git a/crates/ide-assists/src/handlers/remove_underscore.rs b/crates/ide-assists/src/handlers/remove_underscore.rs new file mode 100644 index 000000000000..912e1936b593 --- /dev/null +++ b/crates/ide-assists/src/handlers/remove_underscore.rs @@ -0,0 +1,191 @@ +use ide_db::{ + assists::AssistId, + defs::{Definition, NameClass, NameRefClass}, +}; +use syntax::{AstNode, ast}; + +use crate::{AssistContext, Assists}; + +// Assist: remove_underscore_from_used_variables +// +// Removes underscore from used variables. +// +// ``` +// fn main() { +// let mut _$0foo = 1; +// _foo = 2; +// } +// ``` +// -> +// ``` +// fn main() { +// let mut foo = 1; +// foo = 2; +// } +// ``` +pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let (text, text_range, def) = if let Some(name_ref) = ctx.find_node_at_offset::() { + let text = name_ref.text(); + if !text.starts_with('_') { + return None; + } + + let def = match NameClass::classify(&ctx.sema, &name_ref)? { + NameClass::Definition(def @ Definition::Local(_)) => def, + NameClass::PatFieldShorthand { local_def, .. } => Definition::Local(local_def), + _ => return None, + }; + (text.to_owned(), name_ref.syntax().text_range(), def) + } else if let Some(name_ref) = ctx.find_node_at_offset::() { + let text = name_ref.text(); + if !text.starts_with('_') { + return None; + } + let def = match NameRefClass::classify(&ctx.sema, &name_ref)? { + NameRefClass::Definition(def @ Definition::Local(_), _) => def, + NameRefClass::FieldShorthand { local_ref, .. } => Definition::Local(local_ref), + _ => return None, + }; + (text.to_owned(), name_ref.syntax().text_range(), def) + } else { + return None; + }; + + if !def.usages(&ctx.sema).at_least_one() { + return None; + } + + let new_name = text.trim_start_matches('_'); + acc.add( + AssistId::refactor("remove_underscore_from_used_variables"), + "Remove underscore from a used variable", + text_range, + |builder| { + let changes = def.rename(&ctx.sema, new_name).unwrap(); + builder.source_change = changes; + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn remove_underscore_from_used_variable() { + check_assist( + remove_underscore, + r#" +fn main() { + let mut _$0foo = 1; + _foo = 2; +} +"#, + r#" +fn main() { + let mut foo = 1; + foo = 2; +} +"#, + ); + } + + #[test] + fn not_applicable_for_unused() { + check_assist_not_applicable( + remove_underscore, + r#" +fn main() { + let _$0unused = 1; +} +"#, + ); + } + + #[test] + fn not_applicable_for_no_underscore() { + check_assist_not_applicable( + remove_underscore, + r#" +fn main() { + let f$0oo = 1; + foo = 2; +} +"#, + ); + } + + #[test] + fn remove_multiple_underscores() { + check_assist( + remove_underscore, + r#" +fn main() { + let mut _$0_foo = 1; + __foo = 2; +} +"#, + r#" +fn main() { + let mut foo = 1; + foo = 2; +} +"#, + ); + } + + #[test] + fn remove_underscore_on_usage() { + check_assist( + remove_underscore, + r#" +fn main() { + let mut _foo = 1; + _$0foo = 2; +} +"#, + r#" +fn main() { + let mut foo = 1; + foo = 2; +} +"#, + ); + } + + #[test] + fn remove_underscore_in_function_parameter_usage() { + check_assist( + remove_underscore, + r#" +fn foo(_foo: i32) { + let bar = _$0foo + 1; +} +"#, + r#" +fn foo(foo: i32) { + let bar = foo + 1; +} +"#, + ) + } + + #[test] + fn remove_underscore_in_function_parameter() { + check_assist( + remove_underscore, + r#" +fn foo(_$0foo: i32) { + let bar = _foo + 1; +} +"#, + r#" +fn foo(foo: i32) { + let bar = foo + 1; +} +"#, + ) + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index fee5a9dd73f0..a157483a449c 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -200,6 +200,7 @@ mod handlers { mod remove_dbg; mod remove_mut; mod remove_parentheses; + mod remove_underscore; mod remove_unused_imports; mod remove_unused_param; mod reorder_fields; @@ -335,6 +336,7 @@ mod handlers { remove_dbg::remove_dbg, remove_mut::remove_mut, remove_parentheses::remove_parentheses, + remove_underscore::remove_underscore, remove_unused_imports::remove_unused_imports, remove_unused_param::remove_unused_param, reorder_fields::reorder_fields, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 359de438ed6d..00a9d35c3107 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -2748,6 +2748,25 @@ fn main() { ) } +#[test] +fn doctest_remove_underscore_from_used_variables() { + check_doc_test( + "remove_underscore_from_used_variables", + r#####" +fn main() { + let mut _$0foo = 1; + _foo = 2; +} +"#####, + r#####" +fn main() { + let mut foo = 1; + foo = 2; +} +"#####, + ) +} + #[test] fn doctest_remove_unused_imports() { check_doc_test(