From a172c2317c621ba15b7fc4c11b10040dac346ec8 Mon Sep 17 00:00:00 2001
From: Jakob Hellermann <jakob.hellermann@protonmail.com>
Date: Mon, 23 Nov 2020 20:22:13 +0100
Subject: [PATCH] add 'Re-enable this test' assist

---
 crates/assists/src/handlers/ignore_test.rs | 83 ++++++++++++++++++++--
 1 file changed, 76 insertions(+), 7 deletions(-)

diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/ignore_test.rs
index d2339184f1a..2b9dff081fd 100644
--- a/crates/assists/src/handlers/ignore_test.rs
+++ b/crates/assists/src/handlers/ignore_test.rs
@@ -1,4 +1,7 @@
-use syntax::{ast, AstNode};
+use syntax::{
+    ast::{self, AttrsOwner},
+    AstNode, AstToken,
+};
 
 use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
 
@@ -25,10 +28,76 @@ pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
     let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
     let attr = test_related_attribute(&func)?;
 
-    acc.add(
-        AssistId("ignore_test", AssistKind::None),
-        "Ignore this test",
-        attr.syntax().text_range(),
-        |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
-    )
+    match has_ignore_attribute(&func) {
+        None => acc.add(
+            AssistId("ignore_test", AssistKind::None),
+            "Ignore this test",
+            attr.syntax().text_range(),
+            |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
+        ),
+        Some(ignore_attr) => acc.add(
+            AssistId("unignore_test", AssistKind::None),
+            "Re-enable this test",
+            ignore_attr.syntax().text_range(),
+            |builder| {
+                builder.delete(ignore_attr.syntax().text_range());
+                let whitespace = ignore_attr
+                    .syntax()
+                    .next_sibling_or_token()
+                    .and_then(|x| x.into_token())
+                    .and_then(ast::Whitespace::cast);
+                if let Some(whitespace) = whitespace {
+                    builder.delete(whitespace.syntax().text_range());
+                }
+            },
+        ),
+    }
+}
+
+fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
+    fn_def.attrs().find_map(|attr| {
+        if attr.path()?.syntax().text().to_string() == "ignore" {
+            Some(attr)
+        } else {
+            None
+        }
+    })
+}
+
+#[cfg(test)]
+mod tests {
+    use super::ignore_test;
+    use crate::tests::check_assist;
+
+    #[test]
+    fn test_base_case() {
+        check_assist(
+            ignore_test,
+            r#"
+            #[test<|>]
+            fn test() {}
+            "#,
+            r#"
+            #[test]
+            #[ignore]
+            fn test() {}
+            "#,
+        )
+    }
+
+    #[test]
+    fn test_unignore() {
+        check_assist(
+            ignore_test,
+            r#"
+            #[test<|>]
+            #[ignore]
+            fn test() {}
+            "#,
+            r#"
+            #[test]
+            fn test() {}
+            "#,
+        )
+    }
 }