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

WIP: snippet generator interface #13

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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 @@ -5,6 +5,10 @@

package software.amazon.smithy.docgen.core;

import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
import software.amazon.smithy.build.PluginContext;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.codegen.core.directed.CreateContextDirective;
import software.amazon.smithy.codegen.core.directed.CreateSymbolProviderDirective;
Expand All @@ -21,6 +25,8 @@
import software.amazon.smithy.docgen.core.generators.OperationGenerator;
import software.amazon.smithy.docgen.core.generators.ResourceGenerator;
import software.amazon.smithy.docgen.core.generators.ServiceGenerator;
import software.amazon.smithy.docgen.core.generators.SnippetGenerator;
import software.amazon.smithy.docgen.core.generators.SnippetGenerator.SnippetComparator;
import software.amazon.smithy.docgen.core.generators.StructuredShapeGenerator;
import software.amazon.smithy.model.node.ExpectationNotMetException;
import software.amazon.smithy.model.traits.InputTrait;
Expand All @@ -33,19 +39,39 @@
@SmithyUnstableApi
final class DirectedDocGen implements DirectedCodegen<DocGenerationContext, DocSettings, DocIntegration> {

private final PluginContext pluginContext;

DirectedDocGen(PluginContext pluginContext) {
this.pluginContext = pluginContext;
}

@Override
public SymbolProvider createSymbolProvider(CreateSymbolProviderDirective<DocSettings> directive) {
return new DocSymbolProvider(directive.model(), directive.settings());
}

@Override
public DocGenerationContext createContext(CreateContextDirective<DocSettings, DocIntegration> directive) {
var contextGenerator = new MockPluginContextGenerator(
pluginContext, directive.settings().snippetGeneratorSettings());
var classLoader = pluginContext.getPluginClassLoader().orElse(getClass().getClassLoader());

List<SnippetGenerator> snippetGenerators = new ArrayList<>();
for (SnippetGenerator generator : ServiceLoader.load(SnippetGenerator.class, classLoader)) {
var pluginContext = contextGenerator.getStubbedContext(directive.model(), generator.name());
if (generator.configure(pluginContext, directive.service())) {
snippetGenerators.add(generator);
}
}
snippetGenerators = snippetGenerators.stream().sorted(new SnippetComparator()).toList();

return new DocGenerationContext(
directive.model(),
directive.settings(),
directive.symbolProvider(),
directive.fileManifest(),
directive.integrations()
directive.model(),
directive.settings(),
directive.symbolProvider(),
directive.fileManifest(),
directive.integrations(),
snippetGenerators
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.codegen.core.WriterDelegator;
import software.amazon.smithy.docgen.core.DocSymbolProvider.FileExtensionDecorator;
import software.amazon.smithy.docgen.core.generators.SnippetGenerator;
import software.amazon.smithy.docgen.core.writers.DocWriter;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.utils.SmithyUnstableApi;
Expand All @@ -30,6 +31,7 @@ public final class DocGenerationContext implements CodegenContext<DocSettings, D
private final WriterDelegator<DocWriter> writerDelegator;
private final List<DocIntegration> docIntegrations;
private final DocFormat docFormat;
private final List<SnippetGenerator> snippetGenerators;

/**
* Constructor.
Expand All @@ -39,13 +41,15 @@ public final class DocGenerationContext implements CodegenContext<DocSettings, D
* @param symbolProvider The symbol provider to use to turn shapes into symbols.
* @param fileManifest The file manifest to write to.
* @param docIntegrations A list of integrations to apply during generation.
* @param snippetGenerators A list of snippet generators to add snippets to docs.
*/
public DocGenerationContext(
Model model,
DocSettings docSettings,
SymbolProvider symbolProvider,
FileManifest fileManifest,
List<DocIntegration> docIntegrations
List<DocIntegration> docIntegrations,
List<SnippetGenerator> snippetGenerators
) {
this.model = model;
this.docSettings = docSettings;
Expand Down Expand Up @@ -74,6 +78,7 @@ public DocGenerationContext(
this.docFormat = resolvedFormat;
this.symbolProvider = symbolProvider;
this.writerDelegator = new WriterDelegator<>(fileManifest, symbolProvider, resolvedFormat.writerFactory());
this.snippetGenerators = snippetGenerators;
}

@Override
Expand Down Expand Up @@ -112,4 +117,11 @@ public List<DocIntegration> integrations() {
public DocFormat docFormat() {
return this.docFormat;
}

/**
* @return returns the generators used to add snippets to docs.
*/
public List<SnippetGenerator> snippetGenerators() {
return snippetGenerators;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package software.amazon.smithy.docgen.core;

import java.util.Objects;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.utils.SmithyUnstableApi;
Expand All @@ -16,9 +17,12 @@
*
* @param service The shape id of the service to generate documentation for.
* @param format The format to generate documentation in. The default is markdown.
* @param snippetGeneratorSettings Settings to pass along to snippet generators. By
* default, the settings for the plugin in the current projection will be used,
* if available.
*/
@SmithyUnstableApi
public record DocSettings(ShapeId service, String format) {
public record DocSettings(ShapeId service, String format, ObjectNode snippetGeneratorSettings) {

/**
* Settings for documentation generation. These can be set in the
Expand All @@ -30,6 +34,7 @@ public record DocSettings(ShapeId service, String format) {
public DocSettings {
Objects.requireNonNull(service);
Objects.requireNonNull(format);
Objects.requireNonNull(snippetGeneratorSettings);
}

/**
Expand All @@ -41,7 +46,8 @@ public record DocSettings(ShapeId service, String format) {
public static DocSettings fromNode(ObjectNode pluginSettings) {
return new DocSettings(
pluginSettings.expectStringMember("service").expectShapeId(),
pluginSettings.getStringMemberOrDefault("format", "sphinx-markdown")
pluginSettings.getStringMemberOrDefault("format", "sphinx-markdown"),
pluginSettings.getObjectMember("snippetGeneratorSettings").orElse(Node.objectNode())
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.docgen.core;

import java.util.Optional;
import software.amazon.smithy.build.MockManifest;
import software.amazon.smithy.build.PluginContext;
import software.amazon.smithy.build.model.ProjectionConfig;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.node.ObjectNode;
import software.amazon.smithy.utils.SmithyUnstableApi;

/**
* Generates mock plugin contexts to pass along to snippet generators.
*/
@SmithyUnstableApi
class MockPluginContextGenerator {
private final PluginContext baseContext;
private final ObjectNode pluginSettings;

/**
* Constructs a MockPluginContextGenerator.
*
* @param baseContext The context to base plugin context generation on.
* @param pluginSettings Settings specifically given to pass to plugins via the doc generator.
*/
MockPluginContextGenerator(PluginContext baseContext, ObjectNode pluginSettings) {
if (baseContext.getProjection().isEmpty()) {
// PluginContext will NPE if there's no projection config so you have to do it manually
// until that gets fixed.
var builder = PluginContext.builder()
.projection(baseContext.getProjectionName(), ProjectionConfig.builder().build())
.model(baseContext.getModel())
.originalModel(baseContext.getOriginalModel().orElse(baseContext.getModel()))
.events(baseContext.getEvents())
.settings(baseContext.getSettings())
.fileManifest(baseContext.getFileManifest())
.sources(baseContext.getSources());
baseContext.getPluginClassLoader().ifPresent(builder::pluginClassLoader);
baseContext.getOriginalModel().ifPresent(builder::originalModel);
baseContext.getArtifactName().ifPresent(builder::artifactName);
baseContext = builder.build();
}
this.baseContext = baseContext;
this.pluginSettings = pluginSettings;
}

/**
* Create a new plugin context with the given model and plugin name.
*
* @param model The version of the model to hand to the plugin.
* @param pluginName The name of the plugin. This is used to search for existing config.
* @return Returns a plugin context with the given model and config for the named plugin.
*/
PluginContext getStubbedContext(Model model, String pluginName) {
ObjectNode settings = pluginSettings.getObjectMember(pluginName)
.or(() -> baseContext.getProjection()
.map(ProjectionConfig::getPlugins)
.flatMap(plugins -> Optional.ofNullable(plugins.get(pluginName))))
.orElse(Node.objectNode());

return baseContext.toBuilder()
.fileManifest(new MockManifest())
.artifactName(pluginName)
.model(model)
.settings(settings)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void execute(PluginContext pluginContext) {
CodegenDirector<DocWriter, DocIntegration, DocGenerationContext, DocSettings> runner
= new CodegenDirector<>();

runner.directedCodegen(new DirectedDocGen());
runner.directedCodegen(new DirectedDocGen(pluginContext));
runner.integrationClass(DocIntegration.class);
runner.fileManifest(pluginContext.getFileManifest());
runner.model(getValidatedModel(pluginContext.getModel()).unwrap());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.docgen.core.generators;

import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.traits.ExamplesTrait.Example;

public class ExampleInputGenerator implements SnippetGenerator {
@Override
public String name() {
return "input";
}

@Override
public boolean isWireProtocolGenerator() {
return true;
}

@Override
public String tabTitle() {
return "Input";
}

@Override
public String language() {
return "json";
}

@Override
public String generateShapeSnippet(Shape shape, Node value) {
return "";
}

@Override
public String generateExampleSnippet(OperationShape operation, Example example) {
return Node.prettyPrintJson(example.getInput());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package software.amazon.smithy.docgen.core.generators;

import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.traits.ExamplesTrait.Example;

public class ExampleOutputGenerator implements SnippetGenerator {
@Override
public String name() {
return "output";
}

@Override
public String tabTitle() {
return "Output";
}

@Override
public String language() {
return "json";
}

@Override
public String generateShapeSnippet(Shape shape, Node value) {
return "";
}

@Override
public String generateExampleSnippet(OperationShape operation, Example example) {
return Node.prettyPrintJson(example.getOutput().orElse(Node.objectNode()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import software.amazon.smithy.docgen.core.sections.ShapeSubheadingSection;
import software.amazon.smithy.docgen.core.writers.DocWriter;
import software.amazon.smithy.docgen.core.writers.DocWriter.ListType;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.model.shapes.OperationShape;
import software.amazon.smithy.model.shapes.ServiceShape;
import software.amazon.smithy.model.traits.ExamplesTrait;
Expand Down Expand Up @@ -168,17 +167,11 @@ private void writeExamples(
example.getDocumentation().ifPresent(writer::writeCommonMark);

writer.openTabGroup();
// TODO: create example writer interface allow integrations to register them

// This is just a dummy placehodler tab here to exercise tab creation before
// there's an interface for it.
writer.openCodeTab("Input", "json");
writer.write(Node.prettyPrintJson(example.getInput()));
writer.closeCodeTab();
writer.openCodeTab("Output", "json");
writer.write(Node.prettyPrintJson(example.getOutput().orElse(Node.objectNode())));
writer.closeCodeTab();

for (var snippetGenerator : context.snippetGenerators()) {
writer.openCodeTab(snippetGenerator.tabTitle(), snippetGenerator.language());
writer.write(snippetGenerator.generateExampleSnippet(operation, example));
writer.closeCodeTab();
}
writer.closeTabGroup();

writer.closeHeading();
Expand Down
Loading