ide_assists/handlers/
toggle_ignore.rs1use syntax::{
2 AstNode, AstToken,
3 ast::{self, HasAttrs, edit::AstNodeEdit},
4};
5
6use crate::{AssistContext, AssistId, Assists, utils::test_related_attribute_syn};
7
8pub(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}