diff --git a/WORKSPACE b/WORKSPACE index 4a8e7664..c0a0edeb 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -53,6 +53,22 @@ http_file( url = "https://github.com/JasonSteving99/claro-lang/releases/download/v0.1.271/claro-cli-install.tar.gz", ) +# ClaroDocs is built atop Google's Closure Templates in order to ensure that I'm not generating unsafe html since the +# intention is for users to be able to trust and host ClaroDocs themselves (particularly relevant since ClaroDocs +# automatically generate inlined docs for all of the binaries dependencies, whether first or 3rd party). +http_archive( + name = "io_bazel_rules_closure", + sha256 = "9498e57368efb82b985db1ed426a767cbf1ba0398fd7aed632fc3908654e1b1e", + strip_prefix = "rules_closure-0.12.0", + urls = [ + "https://github.com/bazelbuild/rules_closure/archive/0.12.0.tar.gz", + ], +) + +load("@io_bazel_rules_closure//closure:repositories.bzl", "rules_closure_dependencies", "rules_closure_toolchains") +rules_closure_dependencies() +rules_closure_toolchains() + # See this documentation to understand how fetching Maven deps works in Bazel: # https://github.com/bazelbuild/rules_jvm_external # When you add a new maven dep run the following command to update new deps: diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/BUILD b/src/java/com/claro/module_system/clarodocs/html_rendering/BUILD index 6b8e31aa..ac20abb6 100644 --- a/src/java/com/claro/module_system/clarodocs/html_rendering/BUILD +++ b/src/java/com/claro/module_system/clarodocs/html_rendering/BUILD @@ -1,6 +1,21 @@ +load("@io_bazel_rules_closure//closure:defs.bzl", "closure_java_template_library") + +closure_java_template_library( + name = "html_soy", + srcs = [ + "code_block.soy", + "procedures.soy", + "tokens.soy", + "types.soy", + "utils.soy", + ], + java_package = "com.claro.module_system.clarodocs.html_rendering", + visibility = ["//src/java/com/claro/module_system/clarodocs/html_rendering:__subpackages__"], +) java_library( name = "util", srcs = ["Util.java"], + deps = [":html_soy"], visibility = ["//src/java/com/claro/module_system/clarodocs/html_rendering:__subpackages__"], ) \ No newline at end of file diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/Util.java b/src/java/com/claro/module_system/clarodocs/html_rendering/Util.java index 737edcdd..9e206a18 100644 --- a/src/java/com/claro/module_system/clarodocs/html_rendering/Util.java +++ b/src/java/com/claro/module_system/clarodocs/html_rendering/Util.java @@ -1,11 +1,25 @@ package com.claro.module_system.clarodocs.html_rendering; +import com.google.template.soy.SoyFileSet; +import com.google.template.soy.tofu.SoyTofu; + import java.util.Arrays; import java.util.stream.Collectors; import static com.claro.module_system.clarodocs.html_rendering.Util.CssClass.*; +// TODO(steving) Migrate to pre-compiled SoySauce templates instead of slower (hence deprecated) SoyTofu templates. +@SuppressWarnings("deprecation") public class Util { + public static final SoyTofu SOY = + SoyFileSet.builder() + .add(Util.class.getResource("code_block.soy")) + .add(Util.class.getResource("procedures.soy")) + .add(Util.class.getResource("tokens.soy")) + .add(Util.class.getResource("types.soy")) + .add(Util.class.getResource("utils.soy")) + .build() + .compileToTofu(); public enum CssClass { TOKEN_GROUP_1("tokenGroup1"), diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/aliases/AliasHtml.java b/src/java/com/claro/module_system/clarodocs/html_rendering/aliases/AliasHtml.java index b0de7ef0..3633b9ff 100644 --- a/src/java/com/claro/module_system/clarodocs/html_rendering/aliases/AliasHtml.java +++ b/src/java/com/claro/module_system/clarodocs/html_rendering/aliases/AliasHtml.java @@ -17,7 +17,7 @@ public static void renderAliasHtml( res.append(String.format( Util.wrapAsDefaultCodeBlock(ALIAS_DEF_CLASS_NAME, aliasName, ALIAS_DEF_TEMPLATE), aliasName, - TypeHtml.renderType(new StringBuilder(), wrappedType) + TypeHtml.renderTypeHtml(wrappedType) )); } } diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/code_block.soy b/src/java/com/claro/module_system/clarodocs/html_rendering/code_block.soy new file mode 100644 index 00000000..a23a6461 --- /dev/null +++ b/src/java/com/claro/module_system/clarodocs/html_rendering/code_block.soy @@ -0,0 +1,13 @@ +{namespace codeblock} + + +{template .code} + {@param codeContent: html} + {@param class: string} + {@param id: string} +
+
+ {$codeContent}
+
+
+{/template}
\ No newline at end of file
diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/contracts/ContractHtml.java b/src/java/com/claro/module_system/clarodocs/html_rendering/contracts/ContractHtml.java
index eefbcc52..d793df3b 100644
--- a/src/java/com/claro/module_system/clarodocs/html_rendering/contracts/ContractHtml.java
+++ b/src/java/com/claro/module_system/clarodocs/html_rendering/contracts/ContractHtml.java
@@ -25,7 +25,7 @@ public static void renderContractDefHtml(
contractDef.getName().substring(0, contractDef.getName().indexOf('$')),
Joiner.on(", ").join(contractDef.getTypeParamNamesList()),
contractDef.getSignaturesList().stream()
- .map(proc -> ProcedureHtml.generateProcedureHtmlWithIndentationLevel(proc, 1))
+ .map(ProcedureHtml::generateProcedureHtml)
.collect(Collectors.joining("\n"))
));
}
@@ -33,16 +33,11 @@ public static void renderContractDefHtml(
public static void renderContractImplHtml(
StringBuilder res, SerializedClaroModule.ExportedContractImplementation contractImpl) {
String implementedContractName = contractImpl.getImplementedContractName();
- StringBuilder placeholder = new StringBuilder();
res.append(String.format(
Util.wrapAsDefaultCodeBlock(CONTRACT_IMPL_CLASS_NAME, implementedContractName, CONTRACT_IMPL_TEMPLATE),
implementedContractName.substring(0, implementedContractName.indexOf('$')),
contractImpl.getConcreteTypeParamsList().stream()
- .map(t -> {
- String typeHtml = TypeHtml.renderType(placeholder, Types.parseTypeProto(t)).toString();
- placeholder.setLength(0);
- return typeHtml;
- })
+ .map(t -> TypeHtml.renderTypeHtml(Types.parseTypeProto(t)).toString())
.collect(Collectors.joining(", "))
));
}
diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/homepage/HomePageHtml.java b/src/java/com/claro/module_system/clarodocs/html_rendering/homepage/HomePageHtml.java
index cf700eb3..fabc4f6e 100644
--- a/src/java/com/claro/module_system/clarodocs/html_rendering/homepage/HomePageHtml.java
+++ b/src/java/com/claro/module_system/clarodocs/html_rendering/homepage/HomePageHtml.java
@@ -63,6 +63,27 @@ public static String renderHomePage(
" color: #f8f8f2;\n" +
" overflow-x: scroll;\n" +
"}\n" +
+ ".procedure-def {\n" +
+ " margin-left: 30px;" +
+ "}\n" +
+ ".initializers {\n" +
+ " margin-left: 30px;" +
+ "}\n" +
+ ".unwrappers {\n" +
+ " margin-left: 30px;" +
+ "}\n" +
+ ".contract-def {\n" +
+ " margin-left: 30px;" +
+ "}\n" +
+ ".initializers .procedure-def {\n" +
+ " margin-left: 60px;" +
+ "}\n" +
+ ".unwrappers .procedure-def {\n" +
+ " margin-left: 60px;" +
+ "}\n" +
+ ".contract-def .procedure-def {\n" +
+ " margin-left: 60px;" +
+ "}\n" +
"\n" +
".tj_container {\n" +
" height: 100%;\n" +
diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/initializers/BUILD b/src/java/com/claro/module_system/clarodocs/html_rendering/initializers/BUILD
index ca210c4d..ede0e7db 100644
--- a/src/java/com/claro/module_system/clarodocs/html_rendering/initializers/BUILD
+++ b/src/java/com/claro/module_system/clarodocs/html_rendering/initializers/BUILD
@@ -3,6 +3,8 @@ java_library(
name = "initializers",
srcs = ["InitializersHtml.java"],
deps = [
+ "//:guava",
+ "//src/java/com/claro/module_system/clarodocs/html_rendering:html_soy",
"//src/java/com/claro/module_system/clarodocs/html_rendering:util",
"//src/java/com/claro/module_system/clarodocs/html_rendering/procedures:procedure_html",
"//src/java/com/claro/module_system/module_serialization/proto:serialized_claro_module_java_proto",
diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/initializers/InitializersHtml.java b/src/java/com/claro/module_system/clarodocs/html_rendering/initializers/InitializersHtml.java
index 0af234e7..e1576f95 100644
--- a/src/java/com/claro/module_system/clarodocs/html_rendering/initializers/InitializersHtml.java
+++ b/src/java/com/claro/module_system/clarodocs/html_rendering/initializers/InitializersHtml.java
@@ -3,12 +3,17 @@
import com.claro.module_system.clarodocs.html_rendering.Util;
import com.claro.module_system.clarodocs.html_rendering.procedures.ProcedureHtml;
import com.claro.module_system.module_serialization.proto.SerializedClaroModule;
+import com.google.template.soy.tofu.SoyTofu;
import java.util.stream.Collectors;
import static com.claro.module_system.clarodocs.html_rendering.Util.GrammarPart.INITIALIZERS;
+// TODO(steving) Migrate to pre-compiled SoySauce templates instead of slower (hence deprecated) SoyTofu templates.
+@SuppressWarnings("deprecation")
public class InitializersHtml {
+ private static final SoyTofu.Renderer INITIALIZERS_TEMPLATE =
+ Util.SOY.newRenderer("initializers.initializers");
public static final String INITIALIZERS_CLASS = "initializers";
public static final String INITIALIZERS_BLOCK_TEMPLATE =
INITIALIZERS + " %s {\n%s\n}";
@@ -25,7 +30,7 @@ public static void renderInitializersBlock(
INITIALIZERS_BLOCK_TEMPLATE,
initializedTypeName,
procedures.getProceduresList().stream()
- .map(procedure -> ProcedureHtml.generateProcedureHtmlWithIndentationLevel(procedure, 1))
+ .map(ProcedureHtml::generateProcedureHtml)
.collect(Collectors.joining("\n"))
)
));
diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/procedures.soy b/src/java/com/claro/module_system/clarodocs/html_rendering/procedures.soy
new file mode 100644
index 00000000..0dbbdfd8
--- /dev/null
+++ b/src/java/com/claro/module_system/clarodocs/html_rendering/procedures.soy
@@ -0,0 +1,87 @@
+{namespace procedures}
+
+
+{template .exportedProcedure}
+ {@param name: string}
+ {@param? requiredContracts: list<[contractName: string, genericTypeParams: list\n" +
" \n" +
@@ -29,171 +31,162 @@ public static StringBuilder renderTypeDef(
newTypeDef.getTypeParamNamesCount() == 0
? ""
: String.format("%s%s%s", LT, String.join(", ", newTypeDef.getTypeParamNamesList()), GT),
- renderType(new StringBuilder(), Types.parseTypeProto(newTypeDef.getWrappedType()))
+ renderTypeHtml(Types.parseTypeProto(newTypeDef.getWrappedType()))
));
return res;
}
- public static StringBuilder renderType(StringBuilder res, Type type) {
- StringBuilder placeholder = new StringBuilder();
+ public static SanitizedContent renderTypeHtml(Type type) {
switch (type.baseType()) {
case ATOM:
- res.append(((Types.AtomType) type).getName());
- break;
+ return renderSoy("atom", ImmutableMap.of("name", ((Types.AtomType) type).getName()));
case INTEGER:
- res.append(INT);
- break;
+ return renderToken("INT");
case FLOAT:
- res.append(FLOAT);
- break;
+ return renderToken("FLOAT");
case BOOLEAN:
- res.append(BOOLEAN);
- break;
+ return renderToken("BOOLEAN");
case STRING:
- res.append(STRING);
- break;
+ return renderToken("STRING");
case LIST:
Types.ListType listType = (Types.ListType) type;
- maybeRenderMut(res, listType).append("[");
- renderType(res, listType.getElementType())
- .append("]");
- break;
+ return renderSoy(
+ "list",
+ ImmutableMap.of(
+ "elemsType", renderTypeHtml(listType.getElementType()),
+ "isMut", listType.isMutable()
+ )
+ );
case TUPLE:
Types.TupleType tupleType = (Types.TupleType) type;
- maybeRenderMut(res, tupleType).append("tuple").append(LT).append(
- tupleType.getValueTypes().stream()
- .map(t -> {
- String fmt = renderType(placeholder, t).toString();
- placeholder.setLength(0);
- return fmt;
- }).collect(Collectors.joining(", ")))
- .append(GT);
- break;
+ return renderSoy(
+ "tuple",
+ ImmutableMap.of(
+ "elemsTypes", tupleType.getValueTypes()
+ .stream()
+ .map(TypeHtml::renderTypeHtml)
+ .collect(Collectors.toList()),
+ "isMut", tupleType.isMutable()
+ )
+ );
case SET:
- maybeRenderMut(res, (Types.SetType) type).append("{");
- renderType(res, type.parameterizedTypeArgs().get(Types.SetType.PARAMETERIZED_TYPE))
- .append("}");
- break;
+ return renderSoy(
+ "set",
+ ImmutableMap.of(
+ "elemsType", renderTypeHtml(type.parameterizedTypeArgs().get(Types.SetType.PARAMETERIZED_TYPE)),
+ "isMut", ((Types.SetType) type).isMutable()
+ )
+ );
case MAP:
- maybeRenderMut(res, (Types.MapType) type).append("{");
- renderType(res, type.parameterizedTypeArgs().get(Types.MapType.PARAMETERIZED_TYPE_KEYS))
- .append(COLON).append(" ");
- renderType(res, type.parameterizedTypeArgs().get(Types.MapType.PARAMETERIZED_TYPE_VALUES))
- .append("}");
- break;
+ return renderSoy(
+ "map",
+ ImmutableMap.of(
+ "keyType", renderTypeHtml(type.parameterizedTypeArgs().get(Types.MapType.PARAMETERIZED_TYPE_KEYS)),
+ "valueType", renderTypeHtml(type.parameterizedTypeArgs().get(Types.MapType.PARAMETERIZED_TYPE_VALUES)),
+ "isMut", ((Types.MapType) type).isMutable()
+ )
+ );
case STRUCT:
Types.StructType structType = (Types.StructType) type;
- maybeRenderMut(res, structType).append(STRUCT).append("{");
- res.append(
- IntStream.range(0, structType.getFieldTypes().size()).boxed()
- .map(i -> {
- String fmt = String.format(
- "%s: %s",
- structType.getFieldNames().get(i),
- renderType(placeholder, structType.getFieldTypes().get(i)).toString()
- );
- placeholder.setLength(0);
- return fmt;
- })
- .collect(Collectors.joining(", ")));
- res.append("}");
- break;
+ return renderSoy(
+ "struct",
+ ImmutableMap.builder()
+ .put("fieldNames", structType.getFieldNames())
+ .put(
+ "fieldTypes",
+ structType.getFieldTypes()
+ .stream()
+ .map(TypeHtml::renderTypeHtml)
+ .collect(Collectors.toList())
+ ).put("isMut", ((Types.StructType) type).isMutable()).build()
+ );
case ONEOF:
- res.append(ONEOF).append(LT).append(
- ((Types.OneofType) type).getVariantTypes().stream()
- .map(t -> {
- String fmt = renderType(placeholder, t).toString();
- placeholder.setLength(0);
- return fmt;
- })
- .collect(Collectors.joining(", ")))
- .append(GT);
- break;
+ return renderSoy(
+ "oneof",
+ ImmutableMap.of(
+ "variantTypes",
+ ((Types.OneofType) type).getVariantTypes()
+ .stream()
+ .map(TypeHtml::renderTypeHtml)
+ .collect(Collectors.toList())
+ )
+ );
case FUNCTION:
Types.ProcedureType procedureType = (Types.ProcedureType) type;
- res.append(FUNCTION).append(LT);
- renderProcedureTypeArgs(res, placeholder, procedureType.getArgTypes())
- .append(ARROW).append(" ");
- renderType(res, procedureType.getReturnType()).append(GT);
- break;
+ return renderSoy(
+ "function",
+ ImmutableMap.of(
+ "argTypes",
+ procedureType.getArgTypes().stream().map(TypeHtml::renderTypeHtml).collect(Collectors.toList()),
+ "outputType", renderTypeHtml(procedureType.getReturnType())
+ )
+ );
case CONSUMER_FUNCTION:
procedureType = (Types.ProcedureType) type;
- res.append(CONSUMER).append(LT);
- renderProcedureTypeArgs(res, placeholder, procedureType.getArgTypes()).append(GT);
- break;
+ return renderSoy(
+ "consumer",
+ ImmutableMap.of(
+ "argTypes",
+ procedureType.getArgTypes().stream().map(TypeHtml::renderTypeHtml).collect(Collectors.toList())
+ )
+ );
case PROVIDER_FUNCTION:
procedureType = (Types.ProcedureType) type;
- res.append(PROVIDER).append(LT);
- renderType(res, procedureType.getReturnType()).append(GT);
- break;
+ return renderSoy(
+ "provider",
+ ImmutableMap.of("outputType", renderTypeHtml(procedureType.getReturnType())
+ )
+ );
case FUTURE:
- res.append(FUTURE).append(LT);
- renderType(res, type.parameterizedTypeArgs().get(Types.FutureType.PARAMETERIZED_TYPE_KEY))
- .append(GT);
- break;
+ return renderSoy(
+ "future",
+ ImmutableMap.of(
+ "wrappedType",
+ renderTypeHtml(type.parameterizedTypeArgs().get(Types.FutureType.PARAMETERIZED_TYPE_KEY))
+ )
+ );
case USER_DEFINED_TYPE:
Types.UserDefinedType userDefinedType = (Types.UserDefinedType) type;
- res.append("")
- .append(userDefinedType.getTypeName())
- .append("");
- if (!type.parameterizedTypeArgs().isEmpty()) {
- res.append(
- type.parameterizedTypeArgs().values().stream()
- .map(t -> {
- String fmt = renderType(placeholder, t).toString();
- placeholder.setLength(0);
- return fmt;
- }).collect(Collectors.joining(", ", LT.toString(), GT.toString())));
+ ImmutableMap.Builder args =
+ ImmutableMap.builder()
+ .put("typeName", userDefinedType.getTypeName())
+ .put("definingModuleDisambig", userDefinedType.getDefiningModuleDisambiguator());
+ if (!userDefinedType.parameterizedTypeArgs().isEmpty()) {
+ args.put(
+ "concreteTypeParams",
+ userDefinedType.parameterizedTypeArgs().values().stream()
+ .map(TypeHtml::renderTypeHtml)
+ .collect(Collectors.toList())
+ );
}
- break;
+ return renderSoy("userDefinedType", args.build());
case HTTP_SERVICE:
- res.append("HttpService").append(LT).append(((Types.HttpServiceType) type).getServiceName()).append(GT);
- break;
- case HTTP_SERVER:
- res.append("HttpServer").append(LT);
- renderType(res, type.parameterizedTypeArgs().get(Types.HttpServerType.HTTP_SERVICE_TYPE)).append(GT);
- break;
+ return renderSoy(
+ "httpService",
+ ImmutableMap.of("serviceName", ((Types.HttpServiceType) type).getServiceName())
+ );
case HTTP_CLIENT:
- res.append("HttpClient").append(LT).append(((Types.HttpClientType) type).getServiceName()).append(GT);
- break;
+ return renderSoy(
+ "httpClient",
+ ImmutableMap.of("serviceName", ((Types.HttpClientType) type).getServiceName())
+ );
case HTTP_RESPONSE:
- res.append("HttpResponse");
- break;
+ return renderSoy("httpResponse", ImmutableMap.of());
case $GENERIC_TYPE_PARAM:
- res.append(((Types.$GenericTypeParam) type).getTypeParamName());
- break;
+ return renderSoy(
+ "genericTypeParam",
+ ImmutableMap.of("paramName", ((Types.$GenericTypeParam) type).getTypeParamName())
+ );
default:
throw new RuntimeException("Internal ClaroDocs Error! Attempt to render unknown type: " + type);
}
- return res;
}
- private static StringBuilder maybeRenderMut(StringBuilder res, SupportsMutableVariant> type) {
- if (type.isMutable()) {
- res.append(MUT).append(" ");
- }
- return res;
+ public static SanitizedContent renderToken(String templateName) {
+ return Util.SOY.newRenderer("tokens." + templateName).renderHtml();
}
- private static StringBuilder renderProcedureTypeArgs(
- StringBuilder res, StringBuilder placeholder, ImmutableList args) {
- if (args.size() == 1) {
- return renderType(res, args.get(0));
- }
- return res.append(BAR).append(
- args.stream()
- .map(t -> {
- String fmt = renderType(placeholder, t).toString();
- placeholder.setLength(0);
- return fmt;
- })
- .collect(Collectors.joining(", "))
- ).append(BAR);
+ public static SanitizedContent renderSoy(String templateName, ImmutableMap args) {
+ return SOY.newRenderer("." + templateName).setData(args).renderHtml();
}
}
diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/types.soy b/src/java/com/claro/module_system/clarodocs/html_rendering/types.soy
new file mode 100644
index 00000000..66d961b0
--- /dev/null
+++ b/src/java/com/claro/module_system/clarodocs/html_rendering/types.soy
@@ -0,0 +1,167 @@
+{namespace types}
+
+{template .atom}
+ {@param name: string}
+ {$name}
+{/template}
+
+{template .list}
+ {@param elemsType: html}
+ {@param isMut: bool}
+ {call .maybeMut data="all" /}
+ [{$elemsType}]
+{/template}
+
+{template .tuple}
+ {@param elemsTypes: list}
+ {@param isMut: bool}
+ {call .maybeMut data="all" /}
+ {call tokens.TUPLE /}{call tokens.LT /}
+ {call utils.commaSep}
+ {param elems: $elemsTypes /}
+ {/call}
+ {call tokens.GT /}
+{/template}
+
+{template .set}
+ {@param elemsType: html}
+ {@param isMut: bool}
+ {call .maybeMut data="all" /}
+ {'{'}{$elemsType}{'}'}
+{/template}
+
+{template .map}
+ {@param keyType: html}
+ {@param valueType: html}
+ {@param isMut: bool}
+ {call .maybeMut data="all" /}
+ {'{'}{$keyType}{call tokens.COLON /} {$valueType}{'}'}
+{/template}
+
+{template .struct}
+ {@param fieldNames: list}
+ {@param fieldTypes: list}
+ {@param isMut: bool}
+ {call .maybeMut data="all" /}
+ {call tokens.STRUCT /}{'{'}
+ {for $fieldName, $i in $fieldNames}
+ {if $i > 0}, {/if}
+ {$fieldName}{call tokens.COLON /} {$fieldTypes[$i]}
+ {/for}
+ {'}'}
+{/template}
+
+{template .oneof}
+ {@param variantTypes: list}
+ {call tokens.ONEOF /}{call tokens.LT /}
+ {call utils.commaSep}
+ {param elems: $variantTypes /}
+ {/call}
+ {call tokens.GT /}
+{/template}
+
+{template .barGroup visibility="private"}
+ {@param content: html}
+ {call tokens.BAR /}{$content}{call tokens.BAR /}
+{/template}
+
+{template .function}
+ {@param argTypes: list}
+ {@param outputType: html}
+
+ {call tokens.FUNCTION /}{call tokens.LT /}
+ {let $argsHtml kind="html"}
+ {call utils.commaSep}
+ {param elems: $argTypes /}
+ {/call}
+ {/let}
+ {if length($argTypes) > 1}
+ {call .barGroup}
+ {param content: $argsHtml /}
+ {/call}
+ {else}
+ {$argsHtml}
+ {/if}
+ {sp}{call tokens.ARROW /}{sp}{$outputType}{call tokens.GT /}
+{/template}
+
+{template .consumer}
+ {@param argTypes: list}
+
+ {call tokens.CONSUMER /}{call tokens.LT /}
+ {let $argsHtml kind="html"}
+ {call utils.commaSep}
+ {param elems: $argTypes /}
+ {/call}
+ {/let}
+ {if length($argTypes) > 1}
+ {call .barGroup}
+ {param content: $argsHtml /}
+ {/call}
+ {else}
+ {$argsHtml}
+ {/if}
+ {call tokens.GT /}
+{/template}
+
+{template .provider}
+ {@param outputType: html}
+
+ {call tokens.PROVIDER /}{call tokens.LT /}{$outputType}{call tokens.GT /}
+{/template}
+
+{template .future}
+ {@param wrappedType: html}
+ {call tokens.FUTURE /}{call tokens.LT /}{$wrappedType}{call tokens.GT /}
+{/template}
+
+{template .typeDefiningModuleLink visibility="private"}
+ {@param typeName: string}
+ {@param definingModuleDisambig: string}
+
+ {$typeName}
+
+{/template}
+
+{template .userDefinedType}
+ {@param typeName: string}
+ {@param definingModuleDisambig: string}
+ {@param? concreteTypeParams: list}
+ {call .typeDefiningModuleLink data="all"}{/call}
+ {if $concreteTypeParams}
+ {call tokens.LT /}
+ {call utils.commaSep}
+ {param elems: $concreteTypeParams /}
+ {/call}
+ {call tokens.GT /}
+ {/if}
+{/template}
+
+{template .httpService}
+ {@param serviceName: string}
+ HttpService{call tokens.LT /}{$serviceName}{call tokens.GT /}
+{/template}
+
+{template .httpClient}
+ {@param serviceName: string}
+ HttpClient{call tokens.LT /}{$serviceName}{call tokens.GT /}
+{/template}
+
+{template .httpResponse}
+ HttpResponse
+{/template}
+
+{template .genericTypeParam}
+ {@param paramName: string}
+ {$paramName}
+{/template}
+
+{template .maybeMut}
+ {@param isMut: bool}
+ {if $isMut}{call tokens.MUT /}{sp}{/if}
+{/template}
\ No newline at end of file
diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/unwrappers/UnwrappersHtml.java b/src/java/com/claro/module_system/clarodocs/html_rendering/unwrappers/UnwrappersHtml.java
index c9d2afc2..0c202d98 100644
--- a/src/java/com/claro/module_system/clarodocs/html_rendering/unwrappers/UnwrappersHtml.java
+++ b/src/java/com/claro/module_system/clarodocs/html_rendering/unwrappers/UnwrappersHtml.java
@@ -25,7 +25,7 @@ public static void renderUnwrappersBlock(
UNWRAPPERS_BLOCK_TEMPLATE,
unwrappedTypeName,
procedures.getProceduresList().stream()
- .map(procedure -> ProcedureHtml.generateProcedureHtmlWithIndentationLevel(procedure, 1))
+ .map(ProcedureHtml::generateProcedureHtml)
.collect(Collectors.joining("\n"))
)
));
diff --git a/src/java/com/claro/module_system/clarodocs/html_rendering/utils.soy b/src/java/com/claro/module_system/clarodocs/html_rendering/utils.soy
new file mode 100644
index 00000000..e6f16fc2
--- /dev/null
+++ b/src/java/com/claro/module_system/clarodocs/html_rendering/utils.soy
@@ -0,0 +1,9 @@
+{namespace utils}
+
+{template .commaSep}
+ {@param elems: list}
+ {for $e, $i in $elems}
+ {if $i > 0}, {/if}
+ {$e}
+ {/for}
+{/template}
\ No newline at end of file