ide_assists/handlers/
remove_underscore.rs

1use ide_db::{
2    assists::AssistId,
3    defs::{Definition, NameClass, NameRefClass},
4    rename::RenameDefinition,
5};
6use syntax::{AstNode, ast};
7
8use crate::{AssistContext, Assists};
9
10// Assist: remove_underscore_from_used_variables
11//
12// Removes underscore from used variables.
13//
14// ```
15// fn main() {
16//     let mut _$0foo = 1;
17//     _foo = 2;
18// }
19// ```
20// ->
21// ```
22// fn main() {
23//     let mut foo = 1;
24//     foo = 2;
25// }
26// ```
27pub(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}