Repository: tweag/nickel
Pull request #1210 (2023-03-29 11:08:16)

Format type and contract information in documentation output
Type and contract information is rendered as a list of the form
```
# ident
- `ident : forall a. a`
- `ident | Contract`
```
before the documentation for `ident`.


Patch:
diff --git a/src/program.rs b/src/program.rs
index e4a04e33..c3e58701 100644
--- a/src/program.rs
+++ b/src/program.rs
@@ -416,7 +416,9 @@ mod doc {
     use crate::error::{Error, ExportError, IOError};
     use crate::term::{RichTerm, Term};
     use comrak::arena_tree::NodeEdge;
-    use comrak::nodes::{Ast, AstNode, NodeCode, NodeHeading, NodeValue};
+    use comrak::nodes::{
+        Ast, AstNode, ListDelimType, ListType, NodeCode, NodeHeading, NodeList, NodeValue,
+    };
     use comrak::{format_commonmark, parse_document, Arena, ComrakOptions};
     use serde::{Deserialize, Serialize};
     use std::collections::HashMap;
@@ -521,6 +523,15 @@ mod doc {
                 let header = mk_header(ident, header_level + 1, arena);
                 document.append(header);
 
+                if field.types.is_some() || !field.contracts.is_empty() {
+                    document.append(mk_types_and_contracts(
+                        ident,
+                        arena,
+                        field.types.as_deref(),
+                        field.contracts.as_ref(),
+                    ))
+                }
+
                 if let Some(ref doc) = field.documentation {
                     document.append(parse_markdown_string(header_level + 1, arena, doc, options));
                 }
@@ -582,6 +593,57 @@ mod doc {
 
         res
     }
+
+    fn mk_types_and_contracts<'a>(
+        ident: &str,
+        arena: &'a Arena<AstNode<'a>>,
+        types: Option<&'a str>,
+        contracts: &'a [String],
+    ) -> &'a AstNode<'a> {
+        let list = arena.alloc(AstNode::from(NodeValue::List(NodeList {
+            list_type: ListType::Bullet,
+            marker_offset: 1,
+            padding: 0,
+            start: 0,
+            delimiter: ListDelimType::Period,
+            bullet_char: b'*',
+            tight: true,
+        })));
+
+        if let Some(t) = types {
+            list.append(mk_type(ident, ':', t, arena));
+        }
+
+        for contract in contracts {
+            list.append(mk_type(ident, '|', contract, arena));
+        }
+
+        list
+    }
+
+    fn mk_type<'a>(
+        ident: &str,
+        separator: char,
+        types: &str,
+        arena: &'a Arena<AstNode<'a>>,
+    ) -> &'a AstNode<'a> {
+        let list_item = arena.alloc(AstNode::from(NodeValue::Item(NodeList {
+            list_type: ListType::Bullet,
+            marker_offset: 1,
+            padding: 0,
+            start: 0,
+            delimiter: ListDelimType::Period,
+            bullet_char: b'*',
+            tight: true,
+        })));
+
+        list_item.append(arena.alloc(AstNode::from(NodeValue::Code(NodeCode {
+            literal: format!("{ident} {separator} {types}").bytes().collect(),
+            num_backticks: 1,
+        }))));
+
+        list_item
+    }
 }
 
 #[cfg(test)]


Test patch:
diff --git a/tests/snapshot/inputs/docs/types.ncl b/tests/snapshot/inputs/docs/types.ncl
new file mode 100644
index 00000000..369343ce
--- /dev/null
+++ b/tests/snapshot/inputs/docs/types.ncl
@@ -0,0 +1,6 @@
+{
+  Hello
+    : Number
+    | string.Stringable
+    | doc "World!"
+}
diff --git a/tests/snapshot/snapshots/snapshot__doc_stderr_types.ncl.snap b/tests/snapshot/snapshots/snapshot__doc_stderr_types.ncl.snap
new file mode 100644
index 00000000..b5a2b9aa
--- /dev/null
+++ b/tests/snapshot/snapshots/snapshot__doc_stderr_types.ncl.snap
@@ -0,0 +1,5 @@
+---
+source: tests/snapshot/main.rs
+expression: snapshot
+---
+
diff --git a/tests/snapshot/snapshots/snapshot__doc_stdout_types.ncl.snap b/tests/snapshot/snapshots/snapshot__doc_stdout_types.ncl.snap
new file mode 100644
index 00000000..0f4db2a9
--- /dev/null
+++ b/tests/snapshot/snapshots/snapshot__doc_stdout_types.ncl.snap
@@ -0,0 +1,10 @@
+---
+source: tests/snapshot/main.rs
+expression: snapshot
+---
+# `Hello`
+
+- `Hello : Number`
+- `Hello | string.Stringable`
+World\!
+