Repository: dashbitco/nimble_options
Pull request #57 (2020-11-03 16:03:00)

Support a nesting level in NimbleOptions.docs/2
The nesting level allows to nest all options in the generated Markdown by the given level. This allows to have, for example, a nested `nested_schema` schema and use `NimbleOptions.docs/2` inside the documentation for another option:

```elixir
main_schema = [
  my_key: [
    type: {:or, [:string, keyword_list: nested_schema]},
    doc: """
    Either a string or a keyword list with keys:
    
    #{NimbleOptions.docs(nested_schema, _nest_level = 1)}
    """
  ]
]
```

The options for the nested schema in `:my_key` will be nested by one level, producing the correct Markdown.

josevalim: Can we make it a keyword list? I am sure we will want other options in the future and the fact we are using a variable probably means we should use a keyword list anyway. :D
whatyouhide: @josevalim good call, didn't think about it. Updated!

Patch:
diff --git a/lib/nimble_options.ex b/lib/nimble_options.ex
index 20ee2ef..42adda0 100644
--- a/lib/nimble_options.ex
+++ b/lib/nimble_options.ex
@@ -69,7 +69,7 @@ defmodule NimbleOptions do
   These are the options supported in a *schema*. They are what
   defines the validation for the items in the given schema.
 
-  #{NimbleOptions.Docs.generate(@options_schema)}
+  #{NimbleOptions.Docs.generate(@options_schema, nest_level: 0)}
 
   ## Types
 
@@ -255,10 +255,33 @@ defmodule NimbleOptions do
 
       @doc "Supported options:\n#{NimbleOptions.docs(@options_schema)}"
 
+  ## Options
+
+    * `:nest_level` - an integer deciding the "nest level" of the generated
+      docs. This is useful when, for example, you use `docs/2` inside the `:doc`
+      option of another schema. For example, if you have the following nested schema:
+
+          nested_schema = [
+            allowed_messages: [type: :pos_integer, doc: "Allowed messages."],
+            interval: [type: :pos_integer, doc: "Interval."]
+          ]
+
+      then you can document it inside another schema with its nesting level increased:
+
+          schema = [
+            producer: [
+              type: {:or, [:string, keyword_list: nested_schema]},
+              doc:
+                "Either a string or a keyword list with the following keys:\n\n" <>
+                  NimbleOptions.docs(nested_schema, nest_level: 1)
+            ],
+            other_key: [type: :string]
+          ]
+
   """
-  @spec docs(schema()) :: String.t()
-  def docs(schema) when is_list(schema) do
-    NimbleOptions.Docs.generate(schema)
+  @spec docs(schema(), keyword()) :: String.t()
+  def docs(schema, options \\ []) when is_list(schema) and is_list(options) do
+    NimbleOptions.Docs.generate(schema, options)
   end
 
   @doc false
diff --git a/lib/nimble_options/docs.ex b/lib/nimble_options/docs.ex
index 79c821e..7a6400a 100644
--- a/lib/nimble_options/docs.ex
+++ b/lib/nimble_options/docs.ex
@@ -1,8 +1,9 @@
 defmodule NimbleOptions.Docs do
   @moduledoc false
 
-  def generate(schema) when is_list(schema) do
-    {docs, sections, _level} = build_docs(schema, {[], [], 0})
+  def generate(schema, options) when is_list(schema) and is_list(options) do
+    nest_level = Keyword.get(options, :nest_level, 0)
+    {docs, sections, _level} = build_docs(schema, {[], [], nest_level})
     to_string([Enum.reverse(docs), Enum.reverse(sections)])
   end
 


Test patch:
diff --git a/test/nimble_options_test.exs b/test/nimble_options_test.exs
index a014c06..6b63de5 100644
--- a/test/nimble_options_test.exs
+++ b/test/nimble_options_test.exs
@@ -1237,6 +1237,38 @@ defmodule NimbleOptionsTest do
       assert NimbleOptions.docs(schema) == docs
     end
 
+    test "passing specific indentation" do
+      nested_schema = [
+        allowed_messages: [type: :pos_integer, doc: "Allowed messages."],
+        interval: [type: :pos_integer, doc: "Interval."]
+      ]
+
+      schema = [
+        producer: [
+          type: {:or, [:string, keyword_list: nested_schema]},
+          doc: """
+          The producer. Either a string or a keyword list with the following keys:
+
+          #{NimbleOptions.docs(nested_schema, nest_level: 1)}
+          """
+        ],
+        other_key: [type: :string]
+      ]
+
+      docs = """
+        * `:producer` - The producer. Either a string or a keyword list with the following keys:
+
+          * `:allowed_messages` - Allowed messages.
+
+          * `:interval` - Interval.
+
+        * `:other_key`
+
+      """
+
+      assert NimbleOptions.docs(schema) == docs
+    end
+
     test "generate subsections for nested options" do
       schema = [
         name: [required: true, type: :atom, doc: "The name."],