ide_assists/handlers/
toggle_ignore.rs

1use syntax::{
2    AstNode, AstToken,
3    ast::{self, HasAttrs, edit::AstNodeEdit},
4};
5
6use crate::{AssistContext, AssistId, Assists, utils::test_related_attribute_syn};
7
8// Assist: toggle_ignore
9//
10// Adds `#[ignore]` attribute to the test.
11//
12// ```
13// $0#[test]
14// fn arithmetics {
15//     assert_eq!(2 + 2, 5);
16// }
17// ```
18// ->
19// ```
20// #[test]
21// #[ignore]
22// fn arithmetics {
23//     assert_eq!(2 + 2, 5);
24// }
25// ```
26pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
27    let attr: ast::Attr = ctx.find_node_at_offset()?;
28    let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
29    let attr = test_related_attribute_syn(&func)?;
30    let indent = attr.indent_level();
31
32    match has_ignore_attribute(&func) {
33        None => acc.add(
34            AssistId::refactor("toggle_ignore"),
35            "Ignore this test",
36            attr.syntax().text_range(),
37            |builder| {
38                builder.insert(attr.syntax().text_range().end(), format!("\n{indent}#[ignore]"))
39            },
40        ),
41        Some(ignore_attr) => acc.add(
42            AssistId::refactor("toggle_ignore"),
43            "Re-enable this test",
44            ignore_attr.syntax().text_range(),
45            |builder| {
46                builder.delete(ignore_attr.syntax().text_range());
47                let whitespace = ignore_attr
48                    .syntax()
49                    .next_sibling_or_token()
50                    .and_then(|x| x.into_token())
51                    .and_then(ast::Whitespace::cast);
52                if let Some(whitespace) = whitespace {
53                    builder.delete(whitespace.syntax().text_range());
54                }
55            },
56        ),
57    }
58}
59
60fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
61    fn_def.attrs().find(|attr| attr.path().is_some_and(|it| it.syntax().text() == "ignore"))
62}
63
64#[cfg(test)]
65mod tests {
66    use crate::tests::check_assist;
67
68    use super::*;
69
70    #[test]
71    fn test_base_case() {
72        check_assist(
73            toggle_ignore,
74            r#"
75            mod indent {
76                #[test$0]
77                fn test() {}
78            }
79            "#,
80            r#"
81            mod indent {
82                #[test]
83                #[ignore]
84                fn test() {}
85            }
86            "#,
87        )
88    }
89
90    #[test]
91    fn test_unignore() {
92        check_assist(
93            toggle_ignore,
94            r#"
95            mod indent {
96                #[test$0]
97                #[ignore]
98                fn test() {}
99            }
100            "#,
101            r#"
102            mod indent {
103                #[test]
104                fn test() {}
105            }
106            "#,
107        )
108    }
109}