Repository: casey/just
Pull request #2470 (2024-11-17 01:56:28)

Make recipe doc attribute override comment


Patch:
diff --git a/src/node.rs b/src/node.rs
index f8788c4d..6bfb042a 100644
--- a/src/node.rs
+++ b/src/node.rs
@@ -197,7 +197,7 @@ impl<'src> Node<'src> for UnresolvedRecipe<'src> {
       t.push_mut("quiet");
     }
 
-    if let Some(doc) = self.doc {
+    if let Some(doc) = &self.doc {
       t.push_mut(Tree::string(doc));
     }
 
diff --git a/src/parser.rs b/src/parser.rs
index 00246bf3..229f5aea 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -936,6 +936,14 @@ impl<'run, 'src> Parser<'run, 'src> {
 
     let private = name.lexeme().starts_with('_') || attributes.contains(&Attribute::Private);
 
+    let mut doc = doc.map(ToOwned::to_owned);
+
+    for attribute in &attributes {
+      if let Attribute::Doc(attribute_doc) = attribute {
+        doc = attribute_doc.as_ref().map(|doc| doc.cooked.clone());
+      }
+    }
+
     Ok(Recipe {
       shebang: shebang || script,
       attributes,
diff --git a/src/recipe.rs b/src/recipe.rs
index 05516225..d57bc938 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -22,7 +22,7 @@ pub(crate) struct Recipe<'src, D = Dependency<'src>> {
   pub(crate) attributes: BTreeSet<Attribute<'src>>,
   pub(crate) body: Vec<Line<'src>>,
   pub(crate) dependencies: Vec<D>,
-  pub(crate) doc: Option<&'src str>,
+  pub(crate) doc: Option<String>,
   #[serde(skip)]
   pub(crate) file_depth: u32,
   #[serde(skip)]
@@ -465,7 +465,8 @@ impl<'src, D> Recipe<'src, D> {
         return doc.as_ref().map(|s| s.cooked.as_ref());
       }
     }
-    self.doc
+
+    self.doc.as_deref()
   }
 
   pub(crate) fn subsequents(&self) -> impl Iterator<Item = &D> {
@@ -475,8 +476,14 @@ impl<'src, D> Recipe<'src, D> {
 
 impl<'src, D: Display> ColorDisplay for Recipe<'src, D> {
   fn fmt(&self, f: &mut Formatter, color: Color) -> fmt::Result {
-    if let Some(doc) = self.doc {
-      writeln!(f, "# {doc}")?;
+    if !self
+      .attributes
+      .iter()
+      .any(|attribute| matches!(attribute, Attribute::Doc(_)))
+    {
+      if let Some(doc) = &self.doc {
+        writeln!(f, "# {doc}")?;
+      }
     }
 
     for attribute in &self.attributes {


Test patch:
diff --git a/tests/fmt.rs b/tests/fmt.rs
index 013c0a27..110b6e59 100644
--- a/tests/fmt.rs
+++ b/tests/fmt.rs
@@ -1096,3 +1096,27 @@ fn multi_argument_attribute() {
     )
     .run();
 }
+
+#[test]
+fn doc_attribute_suppresses_comment() {
+  Test::new()
+    .justfile(
+      "
+        set unstable
+
+        # COMMENT
+        [doc('ATTRIBUTE')]
+        foo:
+      ",
+    )
+    .arg("--dump")
+    .stdout(
+      "
+        set unstable := true
+
+        [doc('ATTRIBUTE')]
+        foo:
+      ",
+    )
+    .run();
+}
diff --git a/tests/json.rs b/tests/json.rs
index 3819c040..d3ea7d56 100644
--- a/tests/json.rs
+++ b/tests/json.rs
@@ -1429,3 +1429,58 @@ fn recipes_with_private_attribute_are_private() {
     }),
   );
 }
+
+#[test]
+fn doc_attribute_overrides_comment() {
+  case(
+    "
+      # COMMENT
+      [doc('ATTRIBUTE')]
+      foo:
+    ",
+    json!({
+      "aliases": {},
+      "assignments": {},
+      "first": "foo",
+      "doc": null,
+      "groups": [],
+      "modules": {},
+      "recipes": {
+        "foo": {
+          "attributes": [{"doc": "ATTRIBUTE"}],
+          "body": [],
+          "dependencies": [],
+          "doc": "ATTRIBUTE",
+          "name": "foo",
+          "namepath": "foo",
+          "parameters": [],
+          "priors": 0,
+          "private": false,
+          "quiet": false,
+          "shebang": false,
+        }
+      },
+      "settings": {
+        "allow_duplicate_recipes": false,
+        "allow_duplicate_variables": false,
+        "dotenv_filename": null,
+        "dotenv_load": false,
+        "dotenv_path": null,
+        "dotenv_required": false,
+        "export": false,
+        "fallback": false,
+        "ignore_comments": false,
+        "positional_arguments": false,
+        "quiet": false,
+        "shell": null,
+        "tempdir" : null,
+        "unstable": false,
+        "windows_powershell": false,
+        "windows_shell": null,
+        "working_directory" : null,
+      },
+      "unexports": [],
+      "warnings": [],
+    }),
+  );
+}