Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stardoc: auto-dedent rule doc and attribute docs #13403

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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).
aiuto marked this conversation as resolved.
Show resolved Hide resolved
*
* @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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line.trim() removes tabs (as well as all other characters below 0x20, e.g. windows carriage returns), not just spaces. DocstringParser.getIndentation() counts only spaces.

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;
Comment on lines +116 to +118
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why never dedent the first line? Note that you do use the first line for calculating indentation if the first line's indentation level is non-zero.

} else if (bufLine.trim().isEmpty()) {
description.append("\n");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning doc unchanged on line 110 implies that all-whitespace lines become empty lines (even if they contain whitespace beyond indentation) if any dedentation is done, but remain unchanged when not dedenting - which is unintuitive behavior.

Would it make more sense to instead dedent all-whitespace lines by up to the same number of spaces as any other line?

} 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