Render documentation for provider `init` callbacks (#224)
By default, we want the following behavior:
* Custom init callback specified
* The set of parameters for the init callback equals the set of
fields for the provider; and the docs for the init callback's
parameters are either empty or equal to corresponding field docs
* Some init parameters have a default value:
-> Render a single "Fields" table with 3 columns (name, doc,
default value)
* ... otherwise
-> Render a single "Fields" table with 2 columns
* ... otherwise
-> Render two tables - "Constructor parameters" and "Fields" - with
the links from the summary blurb (interfixed with "_init")
leading to the parameters table (not the fields table)
* ... otherwise
-> Trivial case - single "Fields" table (as before).
Fixes #182
diff --git a/src/main/java/com/google/devtools/build/stardoc/renderer/RendererMain.java b/src/main/java/com/google/devtools/build/stardoc/renderer/RendererMain.java
index 6503e6f..fc8fca0 100644
--- a/src/main/java/com/google/devtools/build/stardoc/renderer/RendererMain.java
+++ b/src/main/java/com/google/devtools/build/stardoc/renderer/RendererMain.java
@@ -46,6 +46,7 @@
*/
public final class RendererMain {
+ @SuppressWarnings("ProtoParseWithRegistry") // See https://github.com/bazelbuild/stardoc/pull/221
public static void main(String[] args) throws IOException {
RendererOptions rendererOptions = new RendererOptions();
diff --git a/src/main/java/com/google/devtools/build/stardoc/rendering/MarkdownRenderer.java b/src/main/java/com/google/devtools/build/stardoc/rendering/MarkdownRenderer.java
index 991378c..c2eb876 100644
--- a/src/main/java/com/google/devtools/build/stardoc/rendering/MarkdownRenderer.java
+++ b/src/main/java/com/google/devtools/build/stardoc/rendering/MarkdownRenderer.java
@@ -14,12 +14,17 @@
package com.google.devtools.build.stardoc.rendering;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.starlarkdocextract.StardocOutputProtos.AspectInfo;
+import com.google.devtools.build.lib.starlarkdocextract.StardocOutputProtos.FunctionParamInfo;
import com.google.devtools.build.lib.starlarkdocextract.StardocOutputProtos.ModuleExtensionInfo;
import com.google.devtools.build.lib.starlarkdocextract.StardocOutputProtos.ModuleInfo;
+import com.google.devtools.build.lib.starlarkdocextract.StardocOutputProtos.ProviderFieldInfo;
import com.google.devtools.build.lib.starlarkdocextract.StardocOutputProtos.ProviderInfo;
import com.google.devtools.build.lib.starlarkdocextract.StardocOutputProtos.RepositoryRuleInfo;
import com.google.devtools.build.lib.starlarkdocextract.StardocOutputProtos.RuleInfo;
@@ -157,8 +162,65 @@
/**
* Returns a markdown rendering of provider documentation for the given provider information
* object with the given name.
+ *
+ * <p>For evaluating the provider template, populates the the following constants:
+ *
+ * <ul>
+ * <li>util - a {@link MarkdownUtil} object
+ * <li>providerName - the provider's name
+ * <li>providerInfo - the {@link ProviderInfo} proto
+ * <li>initParamsWithInferredDocs - the list of the init callback's {@link FunctionParamInfo}
+ * protos, with any undocumented parameters inheriting the doc string of the provider's
+ * field with the same name; or an empty list of the provider doesn't have an init callback
+ * <li>initParamNamesEqualFieldNames - true iff the provider has an init callback and the set of
+ * names of the init callback's parameters equals the set of names of the provider's fields
+ * <li>initParamsHaveDefaultValues - true iff the provider has an init callback and at least one
+ * of the init callback's parameters has a default value specified
+ * <li>initParamsHaveDistinctDocs - true iff the provider has an init callback and at least one
+ * of the init callback's parameters has a docstring which is non-empty and not equal to the
+ * corresponding field's docstring.
+ * </ul>
*/
public String render(ProviderInfo providerInfo) throws IOException {
+ ImmutableMap.Builder<String, String> fieldDocsBuilder = ImmutableMap.builder();
+ for (ProviderFieldInfo fieldInfo : providerInfo.getFieldInfoList()) {
+ fieldDocsBuilder.put(fieldInfo.getName(), fieldInfo.getDocString());
+ }
+ ImmutableMap<String, String> fieldDocs = fieldDocsBuilder.buildOrThrow();
+
+ ImmutableList<FunctionParamInfo> initParamsWithInferredDocs;
+ if (providerInfo.hasInit()) {
+ initParamsWithInferredDocs =
+ providerInfo.getInit().getParameterList().stream()
+ .map(param -> withInferredDoc(param, fieldDocs))
+ .collect(toImmutableList());
+ } else {
+ initParamsWithInferredDocs = ImmutableList.of();
+ }
+ boolean initParamNamesEqualFieldNames =
+ providerInfo.hasInit()
+ && providerInfo.getInit().getParameterList().stream()
+ .map(FunctionParamInfo::getName)
+ .collect(toImmutableSet())
+ .equals(
+ providerInfo.getFieldInfoList().stream()
+ .map(ProviderFieldInfo::getName)
+ .collect(toImmutableSet()));
+ boolean initParamsHaveDefaultValues =
+ providerInfo.hasInit()
+ && providerInfo.getInit().getParameterList().stream()
+ .filter(param -> !param.getDefaultValue().isEmpty())
+ .findFirst()
+ .isPresent();
+ boolean initParamsHaveDistinctDocs =
+ providerInfo.hasInit()
+ && providerInfo.getInit().getParameterList().stream()
+ .filter(
+ param ->
+ !param.getDocString().isEmpty()
+ && !param.getDocString().equals(fieldDocs.get(param.getName())))
+ .findFirst()
+ .isPresent();
ImmutableMap<String, Object> vars =
ImmutableMap.of(
"util",
@@ -166,7 +228,15 @@
"providerName",
providerInfo.getProviderName(),
"providerInfo",
- providerInfo);
+ providerInfo,
+ "initParamsWithInferredDocs",
+ initParamsWithInferredDocs,
+ "initParamNamesEqualFieldNames",
+ initParamNamesEqualFieldNames,
+ "initParamsHaveDefaultValues",
+ initParamsHaveDefaultValues,
+ "initParamsHaveDistinctDocs",
+ initParamsHaveDistinctDocs);
Reader reader = readerFromPath(providerTemplateFilename);
try {
return Template.parseFrom(reader).evaluate(vars);
@@ -175,6 +245,18 @@
}
}
+ private static FunctionParamInfo withInferredDoc(
+ FunctionParamInfo paramInfo, ImmutableMap<String, String> fallbackDocs) {
+ if (paramInfo.getDocString().isEmpty() && fallbackDocs.containsKey(paramInfo.getName())) {
+ return paramInfo.toBuilder()
+ .clearDocString()
+ .setDocString(fallbackDocs.get(paramInfo.getName()))
+ .build();
+ } else {
+ return paramInfo;
+ }
+ }
+
/**
* Returns a markdown rendering of a user-defined function's documentation for the function info
* object.
diff --git a/src/main/java/com/google/devtools/build/stardoc/rendering/MarkdownUtil.java b/src/main/java/com/google/devtools/build/stardoc/rendering/MarkdownUtil.java
index acde9ae..1909964 100644
--- a/src/main/java/com/google/devtools/build/stardoc/rendering/MarkdownUtil.java
+++ b/src/main/java/com/google/devtools/build/stardoc/rendering/MarkdownUtil.java
@@ -37,6 +37,7 @@
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.UnaryOperator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -223,15 +224,36 @@
/**
* Return a string representing the summary for the given provider with the given name.
*
- * <p>For example: 'MyInfo(foo, bar)'. The summary will contain hyperlinks for each field.
+ * <p>For example: 'MyInfo(foo, bar)'.
+ *
+ * <p>If the provider has an init callback, the summary will contain hyperlinks for each of the
+ * init callback's parameters; if the provider doesn't have an init callback, the summary will
+ * contain hyperlinks for each field.
*/
@SuppressWarnings("unused") // Used by markdown template.
public String providerSummary(String providerName, ProviderInfo providerInfo) {
- ImmutableList<String> fieldNames =
- providerInfo.getFieldInfoList().stream()
- .map(field -> field.getName())
- .collect(toImmutableList());
- return summary(providerName, fieldNames);
+ return providerSummaryImpl(
+ providerName, providerInfo, param -> String.format("%s-%s", providerName, param));
+ }
+
+ /** Like {@link providerSummary}, but using "$providerName-_init" in HTML anchors. */
+ @SuppressWarnings("unused") // Used by markdown template.
+ public String providerSummaryWithInitAnchor(String providerName, ProviderInfo providerInfo) {
+ return providerSummaryImpl(
+ providerName, providerInfo, param -> String.format("%s-_init-%s", providerName, param));
+ }
+
+ private String providerSummaryImpl(
+ String providerName, ProviderInfo providerInfo, UnaryOperator<String> paramAnchorNamer) {
+ ImmutableList<String> paramNames =
+ providerInfo.hasInit()
+ ? providerInfo.getInit().getParameterList().stream()
+ .map(FunctionParamInfo::getName)
+ .collect(toImmutableList())
+ : providerInfo.getFieldInfoList().stream()
+ .map(field -> field.getName())
+ .collect(toImmutableList());
+ return summary(providerName, paramNames, paramAnchorNamer);
}
/**
@@ -310,14 +332,24 @@
return summary(funcInfo.getFunctionName(), paramNames);
}
- private static String summary(String functionName, ImmutableList<String> paramNames) {
+ /**
+ * Returns a string representing the summary for a function or other callable.
+ *
+ * @param paramAnchorNamer translates a paremeter's name into the name of its HTML anchor
+ */
+ private static String summary(
+ String functionName,
+ ImmutableList<String> paramNames,
+ UnaryOperator<String> paramAnchorNamer) {
ImmutableList<ImmutableList<String>> paramLines =
wrap(functionName, paramNames, MAX_LINE_LENGTH);
List<String> paramLinksLines = new ArrayList<>();
for (List<String> params : paramLines) {
String paramLinksLine =
params.stream()
- .map(param -> String.format("<a href=\"#%s-%s\">%s</a>", functionName, param, param))
+ .map(
+ param ->
+ String.format("<a href=\"#%s\">%s</a>", paramAnchorNamer.apply(param), param))
.collect(joining(", "));
paramLinksLines.add(paramLinksLine);
}
@@ -326,6 +358,10 @@
return String.format("%s(%s)", functionName, paramList);
}
+ private static String summary(String functionName, ImmutableList<String> paramNames) {
+ return summary(functionName, paramNames, param -> String.format("%s-%s", functionName, param));
+ }
+
/**
* Wraps the given function parameter names to be able to construct a function summary that stays
* within the provided line length limit.
diff --git a/stardoc/templates/html_tables/provider.vm b/stardoc/templates/html_tables/provider.vm
index b1820d5..a2919b0 100644
--- a/stardoc/templates/html_tables/provider.vm
+++ b/stardoc/templates/html_tables/provider.vm
@@ -1,12 +1,62 @@
<a id="${providerName}"></a>
+#if ($providerInfo.hasInit() && $initParamNamesEqualFieldNames && !$initParamsHaveDistinctDocs && !$initParamsWithInferredDocs.isEmpty())
+#set ($mergeParamsAndFields = true)
+#else
+#set ($mergeParamsAndFields = false)
+#end
#[[##]]# ${providerName}
<pre>
+#if ($providerInfo.hasInit() && !$mergeParamsAndFields)
+${util.providerSummaryWithInitAnchor($providerName, $providerInfo)}
+#else
${util.providerSummary($providerName, $providerInfo)}
+#end
</pre>
+#if (!$providerInfo.docString.isEmpty())
-${util.htmlEscape($providerInfo.docString)}
+${providerInfo.docString}
+#end
+#if ($providerInfo.hasInit() && !$providerInfo.init.deprecated.docString.isEmpty())
+
+#[[###]]# Deprecated
+
+${providerInfo.init.deprecated.docString}
+#end
+#if ($providerInfo.hasInit() && !$providerInfo.init.parameterList.isEmpty() && !$mergeParamsAndFields)
+
+#[[###]]# Constructor parameters
+
+<table class="params-table">
+<colgroup>
+<col class="col-param" />
+<col class="col-description" />
+</colgroup>
+<tbody>
+#foreach ($param in $initParamsWithInferredDocs)
+<tr id="${providerName}-_init-${param.name}">
+<td><code>${param.name}</code></td>
+<td>
+
+${util.mandatoryString($param)}.
+#if(!$param.getDefaultValue().isEmpty())
+default is <code>$param.getDefaultValue()</code>
+#end
+
+#if (!$param.docString.isEmpty())
+<p>
+
+${param.docString.trim()}
+
+</p>
+#end
+</td>
+</tr>
+#end
+</tbody>
+</table>
+#end
#if (!$providerInfo.fieldInfoList.isEmpty())
#[[###]]# Fields
@@ -17,6 +67,28 @@
<col class="col-description" />
</colgroup>
<tbody>
+#if ($mergeParamsAndFields)
+#foreach ($param in $initParamsWithInferredDocs)
+<tr id="${providerName}-_init-${param.name}">
+<td><code>${param.name}</code></td>
+<td>
+
+${util.mandatoryString($param)}.
+#if(!$param.getDefaultValue().isEmpty())
+default is <code>$param.getDefaultValue()</code>
+#end
+
+#if (!$param.docString.isEmpty())
+<p>
+
+${param.docString.trim()}
+
+</p>
+#end
+</td>
+</tr>
+#end
+#else
#foreach ($field in $providerInfo.fieldInfoList)
<tr id="${providerName}-${field.name}">
<td><code>${field.name}</code></td>
@@ -29,6 +101,7 @@
</td>
</tr>
#end
+#end
</tbody>
</table>
#end
diff --git a/stardoc/templates/markdown_tables/provider.vm b/stardoc/templates/markdown_tables/provider.vm
index 0a866db..f623e0d 100644
--- a/stardoc/templates/markdown_tables/provider.vm
+++ b/stardoc/templates/markdown_tables/provider.vm
@@ -1,20 +1,80 @@
<a id="${providerName}"></a>
+#if ($providerInfo.hasInit() && $initParamNamesEqualFieldNames && !$initParamsHaveDistinctDocs && !$initParamsWithInferredDocs.isEmpty())
+#set ($mergeParamsAndFields = true)
+#else
+#set ($mergeParamsAndFields = false)
+#end
#[[##]]# ${providerName}
<pre>
+#if ($providerInfo.hasInit() && !$mergeParamsAndFields)
+${util.providerSummaryWithInitAnchor($providerName, $providerInfo)}
+#else
${util.providerSummary($providerName, $providerInfo)}
+#end
</pre>
+#if (!$providerInfo.docString.isEmpty())
${providerInfo.docString}
+#end
+#if ($providerInfo.hasInit() && !$providerInfo.init.deprecated.docString.isEmpty())
+
+**DEPRECATED**
+
+${providerInfo.init.deprecated.docString}
+#end
+#if ($providerInfo.hasInit() && !$providerInfo.init.parameterList.isEmpty() && !$mergeParamsAndFields)
+
+**CONSTRUCTOR PARAMETERS**
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+#foreach ($param in $initParamsWithInferredDocs)
+| <a id="${providerName}-_init-${param.name}"></a>$param.name | ##
+#if (!$param.docString.isEmpty())
+${util.markdownCellFormat($param.docString)} ##
+#else
+<p align="center">-</p> ##
+#end
+| ##
+#if (!$param.getDefaultValue().isEmpty())
+${util.markdownCodeSpan($param.defaultValue)} ##
+#else
+none ##
+#end
+|
+#end
+#end
+#if (!$providerInfo.fieldInfoList.isEmpty())
**FIELDS**
-#if (!$providerInfo.fieldInfoList.isEmpty())
-
+#if ($mergeParamsAndFields)
+| Name | Description #if ($initParamsHaveDefaultValues)| Default Value #end|
+| :------------- | :------------- #if ($initParamsHaveDefaultValues)| :------------- #end|
+#foreach ($param in $initParamsWithInferredDocs)
+| <a id="${providerName}-${param.name}"></a>$param.name | ##
+#if (!$param.docString.isEmpty())
+${util.markdownCellFormat($param.docString)} ##
+#else
+<p align="center"> - </p> ##
+#end
+#if($initParamsHaveDefaultValues)
+| ##
+#if (!$param.getDefaultValue().isEmpty())
+${util.markdownCodeSpan($param.defaultValue)} ##
+#else
+none ##
+#end
+#end
+|
+#end
+#else
| Name | Description |
| :------------- | :------------- |
#foreach ($field in $providerInfo.fieldInfoList)
| <a id="${providerName}-${field.name}"></a>$field.name | #if(!$field.docString.isEmpty()) ${util.markdownCellFormat($field.docString)} #else - #end |
#end
#end
+#end
diff --git a/test/testdata/angle_bracket_test/golden.md b/test/testdata/angle_bracket_test/golden.md
index 06496b2..3e26bea 100644
--- a/test/testdata/angle_bracket_test/golden.md
+++ b/test/testdata/angle_bracket_test/golden.md
@@ -44,7 +44,6 @@
**FIELDS**
-
| Name | Description |
| :------------- | :------------- |
| <a id="bracketuse-foo"></a>foo | A string representing \<foo> |
diff --git a/test/testdata/misc_apis_test/golden.md b/test/testdata/misc_apis_test/golden.md
index 956bf70..4368d43 100644
--- a/test/testdata/misc_apis_test/golden.md
+++ b/test/testdata/misc_apis_test/golden.md
@@ -33,11 +33,8 @@
MyInfo(<a href="#MyInfo-foo">foo</a>, <a href="#MyInfo-bar">bar</a>)
</pre>
-
-
**FIELDS**
-
| Name | Description |
| :------------- | :------------- |
| <a id="MyInfo-foo"></a>foo | Something foo-related. |
diff --git a/test/testdata/provider_basic_test/golden.md b/test/testdata/provider_basic_test/golden.md
index 7029581..ec94c4a 100644
--- a/test/testdata/provider_basic_test/golden.md
+++ b/test/testdata/provider_basic_test/golden.md
@@ -2,6 +2,128 @@
+<a id="MyCustomInitInfo"></a>
+
+## MyCustomInitInfo
+
+<pre>
+MyCustomInitInfo(<a href="#MyCustomInitInfo-foo">foo</a>, <a href="#MyCustomInitInfo-bar">bar</a>)
+</pre>
+
+A provider with a custom constructor.
+
+Since the custom constructor parameters match the provider's fields,
+we don't need to render a separate table of constructor parameters.
+
+**FIELDS**
+
+| Name | Description |
+| :------------- | :------------- |
+| <a id="MyCustomInitInfo-foo"></a>foo | Foo data |
+| <a id="MyCustomInitInfo-bar"></a>bar | Bar data. |
+
+
+<a id="MyCustomInitWithDefaultParamValueInfo"></a>
+
+## MyCustomInitWithDefaultParamValueInfo
+
+<pre>
+MyCustomInitWithDefaultParamValueInfo(<a href="#MyCustomInitWithDefaultParamValueInfo-foo">foo</a>, <a href="#MyCustomInitWithDefaultParamValueInfo-bar">bar</a>)
+</pre>
+
+A provider with a custom constructor with a parameter with a default value.
+
+Since the custom constructor parameters match the provider's fields,
+we don't need to render a separate table of constructor parameters - but
+we do need to render the default value.
+
+**FIELDS**
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="MyCustomInitWithDefaultParamValueInfo-foo"></a>foo | Foo data | none |
+| <a id="MyCustomInitWithDefaultParamValueInfo-bar"></a>bar | Bar data. | `42` |
+
+
+<a id="MyCustomInitWithDocumentedParamInfo"></a>
+
+## MyCustomInitWithDocumentedParamInfo
+
+<pre>
+MyCustomInitWithDocumentedParamInfo(<a href="#MyCustomInitWithDocumentedParamInfo-_init-foo">foo</a>, <a href="#MyCustomInitWithDocumentedParamInfo-_init-bar">bar</a>)
+</pre>
+
+A provider with a custom constructor with documented constructor parameters.
+
+Docs for constructor parameters differ from docs for fields, so we need to render
+constructor parameters as a separate table.
+
+**CONSTRUCTOR PARAMETERS**
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="MyCustomInitWithDocumentedParamInfo-_init-foo"></a>foo | Foo data; must be non-negative | none |
+| <a id="MyCustomInitWithDocumentedParamInfo-_init-bar"></a>bar | Bar data. Note that we didn't document `bar` parameter for the init callback - we want this docstring to be propagated to the constructor param table. | `42` |
+
+**FIELDS**
+
+| Name | Description |
+| :------------- | :------------- |
+| <a id="MyCustomInitWithDocumentedParamInfo-foo"></a>foo | Foo data |
+| <a id="MyCustomInitWithDocumentedParamInfo-bar"></a>bar | Bar data. Note that we didn't document `bar` parameter for the init callback - we want this docstring to be propagated to the constructor param table. |
+
+
+<a id="MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo"></a>
+
+## MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo
+
+<pre>
+MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo(<a href="#MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo-_init-foo">foo</a>, <a href="#MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo-_init-bar">bar</a>)
+</pre>
+
+A provider with a custom constructor whose set of constructor parameters does not equal the provider's set of fields.
+
+We have no choice - we need to render constructor parameters as a separate table.
+
+**CONSTRUCTOR PARAMETERS**
+
+| Name | Description | Default Value |
+| :------------- | :------------- | :------------- |
+| <a id="MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo-_init-foo"></a>foo | Foo data | none |
+| <a id="MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo-_init-bar"></a>bar | Bar data. | none |
+
+**FIELDS**
+
+| Name | Description |
+| :------------- | :------------- |
+| <a id="MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo-foo"></a>foo | Foo data |
+| <a id="MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo-bar"></a>bar | Bar data. |
+| <a id="MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo-validated"></a>validated | True, hopefully |
+
+
+<a id="MyDeprecatedInfo"></a>
+
+## MyDeprecatedInfo
+
+<pre>
+MyDeprecatedInfo()
+</pre>
+
+You can read this info.
+
+But should you really construct it?
+
+**DEPRECATED**
+
+Do not construct!
+
+**FIELDS**
+
+| Name | Description |
+| :------------- | :------------- |
+| <a id="MyDeprecatedInfo-foo"></a>foo | Foo |
+
+
<a id="MyFooInfo"></a>
## MyFooInfo
@@ -14,7 +136,6 @@
**FIELDS**
-
| Name | Description |
| :------------- | :------------- |
| <a id="MyFooInfo-bar"></a>bar | - |
@@ -30,11 +151,6 @@
</pre>
-
-**FIELDS**
-
-
-
<a id="MyVeryDocumentedInfo"></a>
## MyVeryDocumentedInfo
@@ -49,7 +165,6 @@
**FIELDS**
-
| Name | Description |
| :------------- | :------------- |
| <a id="MyVeryDocumentedInfo-favorite_food"></a>favorite_food | A string representing my favorite food<br><br>Expected to be delicious. |
diff --git a/test/testdata/provider_basic_test/input.bzl b/test/testdata/provider_basic_test/input.bzl
index cc3068d..d6b7f8b 100644
--- a/test/testdata/provider_basic_test/input.bzl
+++ b/test/testdata/provider_basic_test/input.bzl
@@ -24,6 +24,132 @@
},
)
+def _init_my_custom_init_info(foo, bar):
+ """
+ Validate stuff.
+
+ Technical details; the user probably doesn't want to see this part.
+ """
+ if foo < 0:
+ fail("foo must be non-negative")
+
+ return {"foo": foo, "bar": bar, "validated": True}
+
+MyCustomInitInfo, _new_my_custom_init_info = provider(
+ doc = """
+ A provider with a custom constructor.
+
+ Since the custom constructor parameters match the provider's fields,
+ we don't need to render a separate table of constructor parameters.
+ """,
+ init = _init_my_custom_init_info,
+ fields = {
+ "foo": "Foo data",
+ "bar": "Bar data.",
+ },
+)
+
+def _init_my_custom_init_with_default_param_value_info(foo, bar = 42):
+ """
+ Validate stuff.
+
+ Technical details; the user probably doesn't want to see this part.
+ """
+ if foo < 0:
+ fail("foo must be non-negative")
+
+ return {"foo": foo, "bar": bar, "validated": True}
+
+MyCustomInitWithDefaultParamValueInfo, _new_my_custom_init_with_default_param_value_info = provider(
+ doc = """
+ A provider with a custom constructor with a parameter with a default value.
+
+ Since the custom constructor parameters match the provider's fields,
+ we don't need to render a separate table of constructor parameters - but
+ we do need to render the default value.
+ """,
+ init = _init_my_custom_init_with_default_param_value_info,
+ fields = {
+ "foo": "Foo data",
+ "bar": "Bar data.",
+ },
+)
+
+def _init_my_custom_init_with_mismatching_constructor_params_and_fields_info(foo, bar):
+ """
+ Validate stuff.
+
+ Technical details; the user probably doesn't want to see this part.
+ """
+ if foo < 0:
+ fail("foo must be non-negative")
+
+ return {"foo": foo, "bar": bar, "validated": True}
+
+MyCustomInitWithMismatchingConstructorParamsAndFieldsInfo, _new_my_custom_init_with_mismatching_constructor_params_and_fields_info = provider(
+ doc = """
+ A provider with a custom constructor whose set of constructor parameters does not equal the provider's set of fields.
+
+ We have no choice - we need to render constructor parameters as a separate table.
+ """,
+ init = _init_my_custom_init_with_mismatching_constructor_params_and_fields_info,
+ fields = {
+ "foo": "Foo data",
+ "bar": "Bar data.",
+ "validated": "True, hopefully",
+ },
+)
+
+# buildifier: disable=function-docstring-args
+def _init_my_custom_init_with_documented_param_info(foo, bar = 42):
+ """
+ Validate stuff.
+
+ Technical details; the user probably doesn't want to see this part.
+
+ Args:
+ foo: Foo data; must be non-negative
+ """
+ if foo < 0:
+ fail("foo must be non-negative")
+
+ return {"foo": foo, "bar": bar}
+
+MyCustomInitWithDocumentedParamInfo, _new_my_custom_init_with_documented_param_info = provider(
+ doc = """
+ A provider with a custom constructor with documented constructor parameters.
+
+ Docs for constructor parameters differ from docs for fields, so we need to render
+ constructor parameters as a separate table.
+ """,
+ init = _init_my_custom_init_with_documented_param_info,
+ fields = {
+ "foo": "Foo data",
+ "bar": "Bar data. Note that we didn't document `bar` parameter for the init callback - we want this docstring to be propagated to the constructor param table.",
+ },
+)
+
+def _init_my_deprecated_info():
+ """
+ MyDeprecatedInfo constructor.
+
+ Deprecated:
+ Do not construct!
+ """
+ return {}
+
+MyDeprecatedInfo, _new_my_deprecated_info = provider(
+ doc = """
+ You can read this info.
+
+ But should you really construct it?
+ """,
+ init = _init_my_deprecated_info,
+ fields = {
+ "foo": "Foo",
+ },
+)
+
named_providers_are_hashable = {
MyFooInfo: "MyFooInfo is hashable",
MyVeryDocumentedInfo: "So is MyVeryDocumentedInfo",
diff --git a/test/testdata/providers_for_attributes_test/golden.md b/test/testdata/providers_for_attributes_test/golden.md
index a427cf2..b259996 100644
--- a/test/testdata/providers_for_attributes_test/golden.md
+++ b/test/testdata/providers_for_attributes_test/golden.md
@@ -34,11 +34,8 @@
MyProviderInfo(<a href="#MyProviderInfo-foo">foo</a>, <a href="#MyProviderInfo-bar">bar</a>)
</pre>
-
-
**FIELDS**
-
| Name | Description |
| :------------- | :------------- |
| <a id="MyProviderInfo-foo"></a>foo | Something foo-related. |
@@ -54,11 +51,6 @@
</pre>
-
-**FIELDS**
-
-
-
<a id="my_rule_impl"></a>
## my_rule_impl
diff --git a/test/testdata/pure_markdown_template_test/golden.md b/test/testdata/pure_markdown_template_test/golden.md
index 62a9327..d39dbb5 100644
--- a/test/testdata/pure_markdown_template_test/golden.md
+++ b/test/testdata/pure_markdown_template_test/golden.md
@@ -34,7 +34,6 @@
**FIELDS**
-
| Name | Description |
| :------------- | :------------- |
| <a id="ExampleProviderInfo-foo"></a>foo | A string representing foo |
diff --git a/test/testdata/table_of_contents_test/golden.md b/test/testdata/table_of_contents_test/golden.md
index 52ca9aa..a49b49c 100644
--- a/test/testdata/table_of_contents_test/golden.md
+++ b/test/testdata/table_of_contents_test/golden.md
@@ -65,7 +65,6 @@
**FIELDS**
-
| Name | Description |
| :------------- | :------------- |
| <a id="MyFooInfo-bar"></a>bar | - |
@@ -86,7 +85,6 @@
**FIELDS**
-
| Name | Description |
| :------------- | :------------- |
| <a id="MyVeryDocumentedInfo-favorite_food"></a>favorite_food | A string representing my favorite food<br><br>Expected to be delicious. |