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.rename(&ctx.sema, new_name, RenameDefinition::Yes).unwrap();
66 builder.source_change = changes;
67 },
68 )
69}
70
71#[cfg(test)]
72mod tests {
73 use crate::tests::{check_assist, check_assist_not_applicable};
74
75 use super::*;
76
77 #[test]
78 fn remove_underscore_from_used_variable() {
79 check_assist(
80 remove_underscore,
81 r#"
82fn main() {
83 let mut _$0foo = 1;
84 _foo = 2;
85}
86"#,
87 r#"
88fn main() {
89 let mut foo = 1;
90 foo = 2;
91}
92"#,
93 );
94 }
95
96 #[test]
97 fn not_applicable_for_unused() {
98 check_assist_not_applicable(
99 remove_underscore,
100 r#"
101fn main() {
102 let _$0unused = 1;
103}
104"#,
105 );
106 }
107
108 #[test]
109 fn not_applicable_for_no_underscore() {
110 check_assist_not_applicable(
111 remove_underscore,
112 r#"
113fn main() {
114 let f$0oo = 1;
115 foo = 2;
116}
117"#,
118 );
119 }
120
121 #[test]
122 fn remove_multiple_underscores() {
123 check_assist(
124 remove_underscore,
125 r#"
126fn main() {
127 let mut _$0_foo = 1;
128 __foo = 2;
129}
130"#,
131 r#"
132fn main() {
133 let mut foo = 1;
134 foo = 2;
135}
136"#,
137 );
138 }
139
140 #[test]
141 fn remove_underscore_on_usage() {
142 check_assist(
143 remove_underscore,
144 r#"
145fn main() {
146 let mut _foo = 1;
147 _$0foo = 2;
148}
149"#,
150 r#"
151fn main() {
152 let mut foo = 1;
153 foo = 2;
154}
155"#,
156 );
157 }
158
159 #[test]
160 fn remove_underscore_in_function_parameter_usage() {
161 check_assist(
162 remove_underscore,
163 r#"
164fn foo(_foo: i32) {
165 let bar = _$0foo + 1;
166}
167"#,
168 r#"
169fn foo(foo: i32) {
170 let bar = foo + 1;
171}
172"#,
173 )
174 }
175
176 #[test]
177 fn remove_underscore_in_function_parameter() {
178 check_assist(
179 remove_underscore,
180 r#"
181fn foo(_$0foo: i32) {
182 let bar = _foo + 1;
183}
184"#,
185 r#"
186fn foo(foo: i32) {
187 let bar = foo + 1;
188}
189"#,
190 )
191 }
192}