Skip to content

Commit

Permalink
stardoc: auto-dedent rule doc and attribute docs
Browse files Browse the repository at this point in the history
Makes them suitable for markdown output.

Fixes #13402
  • Loading branch information
alexeagle committed Apr 28, 2021
1 parent 18073f2 commit 5312d38
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ java_library(
"//src/main/java/com/google/devtools/build/skydoc/rendering/proto:stardoc_output_java_proto",
"//src/main/java/net/starlark/java/eval",
"//src/main/java/net/starlark/java/syntax",
"//src/tools/starlark/java/com/google/devtools/starlark/common",
"//third_party:guava",
"//third_party:jsr305",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AttributeType;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ProviderNameGroup;
import com.google.devtools.starlark.common.DocstringUtils;
import java.util.List;
import net.starlark.java.eval.Printer;

Expand Down Expand Up @@ -51,7 +52,7 @@ public AttributeInfo asAttributeInfo(String attributeName) {
AttributeInfo.Builder attrInfo =
AttributeInfo.newBuilder()
.setName(attributeName)
.setDocString(docString)
.setDocString(DocstringUtils.dedentDocstring(docString))
.setType(type)
.setMandatory(mandatory)
.setDefaultValue(mandatory ? "" : defaultRepresentation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ProviderFieldInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ProviderInfo;
import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.RuleInfo;
import com.google.devtools.starlark.common.DocstringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
Expand Down Expand Up @@ -154,7 +155,7 @@ public StarlarkCallable rule(
RuleDefinitionIdentifier functionIdentifier = new RuleDefinitionIdentifier();

// Only the Builder is passed to RuleInfoWrapper as the rule name is not yet available.
RuleInfo.Builder ruleInfo = RuleInfo.newBuilder().setDocString(doc).addAllAttribute(attrInfos);
RuleInfo.Builder ruleInfo = RuleInfo.newBuilder().setDocString(DocstringUtils.dedentDocstring(doc)).addAllAttribute(attrInfos);

Location loc = thread.getCallerLocation();
ruleInfoList.add(new RuleInfoWrapper(functionIdentifier, loc, ruleInfo));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ example_rule(<a href="#example_rule-name">name</a>, <a href="#example_rule-first

Small example of rule using a markdown template.

This paragraph should not have extra indent, as that would make it a code block.

**ATTRIBUTES**


| Name | Description | Type | Mandatory | Default |
| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: |
| <a name="example_rule-name"></a>name | A unique name for this target. | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required | |
| <a name="example_rule-first"></a>first | This is the first attribute | String | optional | "" |
| <a name="example_rule-first"></a>first | This is the first attribute<br><br>This paragraph also should not have extra indent. | String | optional | "" |
| <a name="example_rule-second"></a>second | - | String | optional | "2" |


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,17 @@ def _rule_impl(ctx):

example_rule = rule(
implementation = _rule_impl,
doc = "Small example of rule using a markdown template.",
doc = """Small example of rule using a markdown template.
This paragraph should not have extra indent, as that would make it a code block.""",
attrs = {
"first": attr.string(doc = "This is the first attribute"),
"first": attr.string(
doc = """\
This is the first attribute
This paragraph also should not have extra indent.
""",
),
"second": attr.string(default = "2"),
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private DocstringUtils() {} // uninstantiable
* """
* }</pre>
*
* @param docstring a docstring of the format described above
* @param doc a docstring of the format described above
* @param parseErrors a list to which parsing error messages are written
* @return the parsed docstring information
*/
Expand All @@ -69,6 +69,63 @@ public static DocstringInfo parseDocstring(String doc, List<DocstringParseError>
return result;
}

/**
* Removes leading whitespace from docstring lines after the first.
*
* Allows docstrings to be indented to the context where they appear, without including that indent in the output.
* Some output formats have semantic meaning for leading whitespace (such as Markdown).
*
* @param doc A non-function docstring, without Args or Returns sections.
*
*/
public static String dedentDocstring(String doc) {
// Two iterations: first buffer lines and find the minimal indent, then trim to the min
List<String> buffer = new ArrayList<>();
int indentation = Integer.MAX_VALUE;
int lineOffset = 0;
int endOfLineOffset = 0;

do {
endOfLineOffset = doc.indexOf("\n", lineOffset);
String line = endOfLineOffset < 0 ? doc.substring(lineOffset) : doc.substring(lineOffset, endOfLineOffset + 1);
boolean allWhitespace = line.trim().isEmpty();
boolean isFirstLine = lineOffset == 0;
if (!allWhitespace) {
int lineIndent = DocstringParser.getIndentation(line);
if (isFirstLine && lineIndent == 0) {
// Doesn't count. First line is often not indented, e.g.
// doc = """First line
// second line"""
// should still result in both lines with no leading whitespace
} else {
indentation = Math.min(indentation, lineIndent);
}
}
buffer.add(line);
// next line
lineOffset = endOfLineOffset + 1;
} while (endOfLineOffset >= 0);

if (indentation == Integer.MAX_VALUE) {
return doc;
}

boolean firstLine = true;
StringBuilder description = new StringBuilder();
for (String bufLine : buffer) {
if (firstLine) {
description.append(bufLine);
firstLine = false;
} else if (bufLine.trim().isEmpty()) {
description.append("\n");
} else {
String trimmedLine = bufLine.substring(indentation);
description.append(trimmedLine);
}
}
return description.toString();
}

/** Encapsulates information about a Starlark function docstring. */
public static class DocstringInfo {

Expand Down

0 comments on commit 5312d38

Please sign in to comment.