ide_assists/handlers/
remove_underscore.rs1use ide_db::{
2 assists::AssistId,
3 defs::{Definition, NameClass, NameRefClass},
4 rename::RenameDefinition,
5};
6use syntax::{AstNode, ast};
7
8use crate::{AssistContext, Assists};
9
10pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
28 let (text, text_range, def) = if let Some(name_ref) = ctx.find_node_at_offset::<ast::Name>() {
29 let text = name_ref.text();
30 if !text.starts_with('_') {
31 return None;
32 }
33
34 let def = match NameClass::classify(&ctx.sema, &name_ref)? {
35 NameClass::Definition(def @ Definition::Local(_)) => def,
36 NameClass::PatFieldShorthand { local_def, .. } => Definition::Local(local_def),
37 _ => return None,
38 };
39 (text.to_owned(), name_ref.syntax().text_range(), def)
40 } else if let Some(name_ref) = ctx.find_node_at_offset::<ast::NameRef>() {
41 let text = name_ref.text();
42 if !text.starts_with('_') {
43 return None;
44 }
45 let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
46 NameRefClass::Definition(def @ Definition::Local(_), _) => def,
47 NameRefClass::FieldShorthand { local_ref, .. } => Definition::Local(local_ref),
48 _ => return None,
49 };
50 (text.to_owned(), name_ref.syntax().text_range(), def)
51 } else {
52 return None;
53 };
54
55 if !def.usages(&ctx.sema).at_least_one() {
56 return None;
57 }
58
59 let new_name = text.trim_start_matches('_');
60 acc.add(
61 AssistId::refactor("remove_underscore_from_used_variables"),
62 "Remove underscore from a used variable",
63 text_range,
64 |builder| {
65 let changes = def
66 .rename(&ctx.sema, new_name, RenameDefinition::Yes, &ctx.config.rename_config())
67 .unwrap();
68 builder.source_change = changes;
69 },
70 )
71}
72
73#[cfg(test)]
74mod tests {
75 use crate::tests::{check_assist, check_assist_not_applicable};
76
77 use super::*;
78
79 #[test]
80 fn remove_underscore_from_used_variable() {
81 check_assist(
82 remove_underscore,
83 r#"
84fn main() {
85 let mut _$0foo = 1;
86 _foo = 2;
87}
88"#,
89 r#"
90fn main() {
91 let mut foo = 1;
92 foo = 2;
93}
94"#,
95 );
96 }
97
98 #[test]
99 fn not_applicable_for_unused() {
100 check_assist_not_applicable(
101 remove_underscore,
102 r#"
103fn main() {
104 let _$0unused = 1;
105}
106"#,
107 );
108 }
109
110 #[test]
111 fn not_applicable_for_no_underscore() {
112 check_assist_not_applicable(
113 remove_underscore,
114 r#"
115fn main() {
116 let f$0oo = 1;
117 foo = 2;
118}
119"#,
120 );
121 }
122
123 #[test]
124 fn remove_multiple_underscores() {
125 check_assist(
126 remove_underscore,
127 r#"
128fn main() {
129 let mut _$0_foo = 1;
130 __foo = 2;
131}
132"#,
133 r#"
134fn main() {
135 let mut foo = 1;
136 foo = 2;
137}
138"#,
139 );
140 }
141
142 #[test]
143 fn remove_underscore_on_usage() {
144 check_assist(
145 remove_underscore,
146 r#"
147fn main() {
148 let mut _foo = 1;
149 _$0foo = 2;
150}
151"#,
152 r#"
153fn main() {
154 let mut foo = 1;
155 foo = 2;
156}
157"#,
158 );
159 }
160
161 #[test]
162 fn remove_underscore_in_function_parameter_usage() {
163 check_assist(
164 remove_underscore,
165 r#"
166fn foo(_foo: i32) {
167 let bar = _$0foo + 1;
168}
169"#,
170 r#"
171fn foo(foo: i32) {
172 let bar = foo + 1;
173}
174"#,
175 )
176 }
177
178 #[test]
179 fn remove_underscore_in_function_parameter() {
180 check_assist(
181 remove_underscore,
182 r#"
183fn foo(_$0foo: i32) {
184 let bar = _foo + 1;
185}
186"#,
187 r#"
188fn foo(foo: i32) {
189 let bar = foo + 1;
190}
191"#,
192 )
193 }
194}