Skip to content

Commit

Permalink
Simple Markdown rendering for skydoc
Browse files Browse the repository at this point in the history
This uses apache velocity engine templates to create markdown-HTML. There are other alternatives, but there is already precedent for depending on this library from docgen.

RELNOTES: None.
PiperOrigin-RevId: 203795431
  • Loading branch information
c-parsons authored and George Gensure committed Aug 2, 2018
1 parent 0121518 commit bd42322
Show file tree
Hide file tree
Showing 22 changed files with 626 additions and 80 deletions.
19 changes: 12 additions & 7 deletions src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import com.google.devtools.build.skydoc.fakebuildapi.platform.FakePlatformCommon;
import com.google.devtools.build.skydoc.fakebuildapi.repository.FakeRepositoryModule;
import com.google.devtools.build.skydoc.fakebuildapi.test.FakeTestingModule;
import com.google.devtools.build.skydoc.rendering.MarkdownRenderer;
import com.google.devtools.build.skydoc.rendering.RuleInfo;
import java.io.IOException;
import java.io.PrintWriter;
Expand Down Expand Up @@ -116,29 +117,33 @@ public static void main(String[] args) throws IOException, InterruptedException

new SkydocMain(new FilesystemFileAccessor()).eval(path, ruleInfoMap, unexportedRuleInfos);

MarkdownRenderer renderer = new MarkdownRenderer();

try (PrintWriter printWriter = new PrintWriter(outputPath, "UTF-8")) {
printRuleInfos(printWriter, ruleInfoMap.build(), unexportedRuleInfos.build());
printRuleInfos(printWriter, renderer, ruleInfoMap.build(), unexportedRuleInfos.build());
}
}

// TODO(cparsons): Improve output (markdown or HTML).
private static void printRuleInfos(
PrintWriter printWriter,
MarkdownRenderer renderer,
Map<String, RuleInfo> ruleInfos,
List<RuleInfo> unexportedRuleInfos) throws IOException {
for (Entry<String, RuleInfo> ruleInfoEntry : ruleInfos.entrySet()) {
printRuleInfo(printWriter, ruleInfoEntry.getKey(), ruleInfoEntry.getValue());
printRuleInfo(printWriter, renderer, ruleInfoEntry.getKey(), ruleInfoEntry.getValue());
printWriter.println();
}
for (RuleInfo unexportedRuleInfo : unexportedRuleInfos) {
printRuleInfo(printWriter, "<unknown name>", unexportedRuleInfo);
printRuleInfo(printWriter, renderer, "<unknown name>", unexportedRuleInfo);
printWriter.println();
}
}

private static void printRuleInfo(
PrintWriter printWriter, String exportedName, RuleInfo ruleInfo) {
printWriter.println(exportedName);
printWriter.println(ruleInfo.getDescription());
printWriter.println();
PrintWriter printWriter, MarkdownRenderer renderer,
String exportedName, RuleInfo ruleInfo) throws IOException {
printWriter.println(renderer.render(exportedName, ruleInfo));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,19 @@
* Fake implementation of {@link Descriptor}.
*/
public class FakeDescriptor implements Descriptor {
private final String docString;

public FakeDescriptor(String docString) {
this.docString = docString;
}

public String getDocString() {
return docString;
}

@Override
public void repr(SkylarkPrinter printer) {}

// TODO(cparsons): This class should store information about the attribute definition, for
// example, the attribute type.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,90 +30,90 @@ public class FakeSkylarkAttrApi implements SkylarkAttrApi {
@Override
public Descriptor intAttribute(Integer defaultInt, String doc, Boolean mandatory,
SkylarkList<?> values, FuncallExpression ast, Environment env) throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor stringAttribute(String defaultString, String doc, Boolean mandatory,
SkylarkList<?> values, FuncallExpression ast, Environment env) throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor labelAttribute(Object defaultO, String doc, Boolean executable,
Object allowFiles, Object allowSingleFile, Boolean mandatory, SkylarkList<?> providers,
Object allowRules, Boolean singleFile, Object cfg, SkylarkList<?> aspects,
FuncallExpression ast, Environment env) throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor stringListAttribute(Boolean mandatory, Boolean nonEmpty, Boolean allowEmpty,
SkylarkList<?> defaultList, String doc, FuncallExpression ast, Environment env)
throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor intListAttribute(Boolean mandatory, Boolean nonEmpty, Boolean allowEmpty,
SkylarkList<?> defaultList, String doc, FuncallExpression ast, Environment env)
throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor labelListAttribute(Boolean allowEmpty, Object defaultList, String doc,
Object allowFiles, Object allowRules, SkylarkList<?> providers, SkylarkList<?> flags,
Boolean mandatory, Boolean nonEmpty, Object cfg, SkylarkList<?> aspects,
FuncallExpression ast, Environment env) throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor labelKeyedStringDictAttribute(Boolean allowEmpty, Object defaultList,
String doc, Object allowFiles, Object allowRules, SkylarkList<?> providers,
SkylarkList<?> flags, Boolean mandatory, Boolean nonEmpty, Object cfg, SkylarkList<?> aspects,
FuncallExpression ast, Environment env) throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor boolAttribute(Boolean defaultO, String doc, Boolean mandatory,
FuncallExpression ast, Environment env) throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor outputAttribute(Object defaultO, String doc, Boolean mandatory,
FuncallExpression ast, Environment env) throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor outputListAttribute(Boolean allowEmpty, SkylarkList<?> defaultList, String doc,
Boolean mandatory, Boolean nonEmpty, FuncallExpression ast, Environment env)
throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor stringDictAttribute(Boolean allowEmpty, SkylarkDict<?, ?> defaultO, String doc,
Boolean mandatory, Boolean nonEmpty, FuncallExpression ast, Environment env)
throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor stringListDictAttribute(Boolean allowEmpty, SkylarkDict<?, ?> defaultO,
String doc, Boolean mandatory, Boolean nonEmpty, FuncallExpression ast, Environment env)
throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
public Descriptor licenseAttribute(Object defaultO, String doc, Boolean mandatory,
FuncallExpression ast, Environment env) throws EvalException {
return new FakeDescriptor();
return new FakeDescriptor(doc);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@

package com.google.devtools.build.skydoc.fakebuildapi;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.skylarkbuildapi.FileApi;
import com.google.devtools.build.lib.skylarkbuildapi.FileTypeApi;
import com.google.devtools.build.lib.skylarkbuildapi.ProviderApi;
import com.google.devtools.build.lib.skylarkbuildapi.SkylarkAspectApi;
import com.google.devtools.build.lib.skylarkbuildapi.SkylarkAttrApi.Descriptor;
import com.google.devtools.build.lib.skylarkbuildapi.SkylarkRuleFunctionsApi;
import com.google.devtools.build.lib.syntax.BaseFunction;
import com.google.devtools.build.lib.syntax.Environment;
Expand All @@ -30,10 +29,11 @@
import com.google.devtools.build.lib.syntax.Runtime;
import com.google.devtools.build.lib.syntax.SkylarkDict;
import com.google.devtools.build.lib.syntax.SkylarkList;
import com.google.devtools.build.skydoc.rendering.AttributeInfo;
import com.google.devtools.build.skydoc.rendering.RuleInfo;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

/**
Expand Down Expand Up @@ -68,19 +68,24 @@ public BaseFunction rule(BaseFunction implementation, Boolean test, Object attrs
SkylarkList<?> toolchains, String doc, SkylarkList<?> providesArg,
Boolean executionPlatformConstraintsAllowed, SkylarkList<?> execCompatibleWith,
FuncallExpression ast, Environment funcallEnv) throws EvalException {
Set<String> attrNames;
List<AttributeInfo> attrInfos;
// TODO(cparsons): Include implicit "Name" attribute.
if (attrs != null && attrs != Runtime.NONE) {
SkylarkDict<?, ?> attrsDict = (SkylarkDict<?, ?>) attrs;
Map<String, Descriptor> attrsMap =
attrsDict.getContents(String.class, Descriptor.class, "attrs");
attrNames = attrsMap.keySet();
Map<String, FakeDescriptor> attrsMap =
attrsDict.getContents(String.class, FakeDescriptor.class, "attrs");
// TODO(cparsons): Include better attribute details. For example, attribute type.
attrInfos = attrsMap.entrySet().stream()
.map(entry -> new AttributeInfo(entry.getKey(), entry.getValue().getDocString()))
.sorted((o1, o2) -> o1.getName().compareTo(o2.getName()))
.collect(Collectors.toList());
} else {
attrNames = ImmutableSet.of();
attrInfos = ImmutableList.of();
}

RuleDefinitionIdentifier functionIdentifier = new RuleDefinitionIdentifier();
// TODO(cparsons): Improve details given to RuleInfo (for example, attribute types).
ruleInfoList.add(new RuleInfo(functionIdentifier, ast.getLocation(), doc, attrNames));

ruleInfoList.add(new RuleInfo(functionIdentifier, ast.getLocation(), doc, attrInfos));
return functionIdentifier;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.build.skydoc.rendering;

/**
* Stores information about a skylark attribute definition.
*/
public class AttributeInfo {

private final String name;
private final String docString;

public AttributeInfo(String name, String docString) {
this.name = name;
this.docString = docString;
}

public String getName() {
return name;
}

public String getDocString() {
return docString;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,20 @@ filegroup(
java_library(
name = "rendering",
srcs = glob(["*.java"]),
resources = [":template_files"],
deps = [
"//src/main/java/com/google/devtools/build/lib:events",
"//src/main/java/com/google/devtools/build/lib:skylarkinterface",
"//src/main/java/com/google/devtools/build/lib:syntax",
"//third_party:apache_velocity",
"//third_party:guava",
"//third_party:jsr305",
],
)

filegroup(
name = "template_files",
srcs = glob([
"templates/*.vm",
]),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2018 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.build.skydoc.rendering;

import com.google.common.base.Joiner;
import java.io.IOException;
import java.io.StringWriter;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.apache.velocity.runtime.resource.loader.JarResourceLoader;

/**
* Produces skydoc output in markdown form.
*/
public class MarkdownRenderer {

private static final String TEMPLATE_FILENAME =
"com/google/devtools/build/skydoc/rendering/templates/test.vm";

private final VelocityEngine velocityEngine;

public MarkdownRenderer() {
this.velocityEngine = new VelocityEngine();
velocityEngine.setProperty("resource.loader", "classpath, jar");
velocityEngine.setProperty("classpath.resource.loader.class",
ClasspathResourceLoader.class.getName());
velocityEngine.setProperty("jar.resource.loader.class", JarResourceLoader.class.getName());
velocityEngine.setProperty("input.encoding", "UTF-8");
velocityEngine.setProperty("output.encoding", "UTF-8");
velocityEngine.setProperty("runtime.references.strict", true);
}

/**
* Returns a markdown rendering of rule documentation for the given rule information object with
* the given rule name.
*/
public String render(String ruleName, RuleInfo ruleInfo) throws IOException {
VelocityContext context = new VelocityContext();
// TODO(cparsons): Attributes in summary form should have links.
context.put("summaryform", getSummaryForm(ruleName, ruleInfo));
context.put("ruleName", ruleName);
context.put("ruleInfo", ruleInfo);

StringWriter stringWriter = new StringWriter();
try {
velocityEngine.mergeTemplate(TEMPLATE_FILENAME, "UTF-8", context, stringWriter);
} catch (ResourceNotFoundException | ParseErrorException | MethodInvocationException e) {
throw new IOException(e);
}
return stringWriter.toString();
}

private static String getSummaryForm(String ruleName, RuleInfo ruleInfo) {
List<String> attributeNames = ruleInfo.getAttributes().stream()
.map(attr -> attr.getName())
.collect(Collectors.toList());
return String.format("%s(%s)", ruleName, Joiner.on(", ").join(attributeNames));
}
}
Loading

0 comments on commit bd42322

Please sign in to comment.