Skip to main content

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
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}