Skip to content

Commit 82a36c8

Browse files
WIP: snippet generator interface
This adds and makes use of a snippet generator interface that lets other generators provide examples to be included in the docs. It's unfortunately more ugly than I'd like, notbably with config. Generators will essentially have to run much of their generator to be able to provide an accurate example, so we have to give a fake plugin context that they can use. The codegen director will need to be updated to make this more workable. This is just a proof of concept meant to show how it might work from the doc generator side.
1 parent 1e78aaa commit 82a36c8

File tree

10 files changed

+322
-21
lines changed

10 files changed

+322
-21
lines changed

smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/DirectedDocGen.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
package software.amazon.smithy.docgen.core;
77

8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.ServiceLoader;
11+
import software.amazon.smithy.build.PluginContext;
812
import software.amazon.smithy.codegen.core.SymbolProvider;
913
import software.amazon.smithy.codegen.core.directed.CreateContextDirective;
1014
import software.amazon.smithy.codegen.core.directed.CreateSymbolProviderDirective;
@@ -19,6 +23,8 @@
1923
import software.amazon.smithy.codegen.core.directed.GenerateUnionDirective;
2024
import software.amazon.smithy.docgen.core.generators.OperationGenerator;
2125
import software.amazon.smithy.docgen.core.generators.ServiceGenerator;
26+
import software.amazon.smithy.docgen.core.generators.SnippetGenerator;
27+
import software.amazon.smithy.docgen.core.generators.SnippetGenerator.SnippetComparator;
2228
import software.amazon.smithy.docgen.core.generators.StructureGenerator;
2329
import software.amazon.smithy.utils.SmithyUnstableApi;
2430

@@ -28,19 +34,39 @@
2834
@SmithyUnstableApi
2935
final class DirectedDocGen implements DirectedCodegen<DocGenerationContext, DocSettings, DocIntegration> {
3036

37+
private final PluginContext pluginContext;
38+
39+
DirectedDocGen(PluginContext pluginContext) {
40+
this.pluginContext = pluginContext;
41+
}
42+
3143
@Override
3244
public SymbolProvider createSymbolProvider(CreateSymbolProviderDirective<DocSettings> directive) {
3345
return new DocSymbolProvider(directive.model(), directive.settings());
3446
}
3547

3648
@Override
3749
public DocGenerationContext createContext(CreateContextDirective<DocSettings, DocIntegration> directive) {
50+
var contextGenerator = new MockPluginContextGenerator(
51+
pluginContext, directive.settings().snippetGeneratorSettings());
52+
var classLoader = pluginContext.getPluginClassLoader().orElse(getClass().getClassLoader());
53+
54+
List<SnippetGenerator> snippetGenerators = new ArrayList<>();
55+
for (SnippetGenerator generator : ServiceLoader.load(SnippetGenerator.class, classLoader)) {
56+
var pluginContext = contextGenerator.getStubbedContext(directive.model(), generator.name());
57+
if (generator.configure(pluginContext, directive.service())) {
58+
snippetGenerators.add(generator);
59+
}
60+
}
61+
snippetGenerators = snippetGenerators.stream().sorted(new SnippetComparator()).toList();
62+
3863
return new DocGenerationContext(
39-
directive.model(),
40-
directive.settings(),
41-
directive.symbolProvider(),
42-
directive.fileManifest(),
43-
directive.integrations()
64+
directive.model(),
65+
directive.settings(),
66+
directive.symbolProvider(),
67+
directive.fileManifest(),
68+
directive.integrations(),
69+
snippetGenerators
4470
);
4571
}
4672

smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/DocGenerationContext.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import software.amazon.smithy.codegen.core.SymbolProvider;
1414
import software.amazon.smithy.codegen.core.WriterDelegator;
1515
import software.amazon.smithy.docgen.core.DocSymbolProvider.FileExtensionDecorator;
16+
import software.amazon.smithy.docgen.core.generators.SnippetGenerator;
1617
import software.amazon.smithy.docgen.core.writers.DocWriter;
1718
import software.amazon.smithy.model.Model;
1819
import software.amazon.smithy.utils.SmithyUnstableApi;
@@ -30,6 +31,7 @@ public final class DocGenerationContext implements CodegenContext<DocSettings, D
3031
private final WriterDelegator<DocWriter> writerDelegator;
3132
private final List<DocIntegration> docIntegrations;
3233
private final DocFormat docFormat;
34+
private final List<SnippetGenerator> snippetGenerators;
3335

3436
/**
3537
* Constructor.
@@ -39,13 +41,15 @@ public final class DocGenerationContext implements CodegenContext<DocSettings, D
3941
* @param symbolProvider The symbol provider to use to turn shapes into symbols.
4042
* @param fileManifest The file manifest to write to.
4143
* @param docIntegrations A list of integrations to apply during generation.
44+
* @param snippetGenerators A list of snippet generators to add snippets to docs.
4245
*/
4346
public DocGenerationContext(
4447
Model model,
4548
DocSettings docSettings,
4649
SymbolProvider symbolProvider,
4750
FileManifest fileManifest,
48-
List<DocIntegration> docIntegrations
51+
List<DocIntegration> docIntegrations,
52+
List<SnippetGenerator> snippetGenerators
4953
) {
5054
this.model = model;
5155
this.docSettings = docSettings;
@@ -74,6 +78,7 @@ public DocGenerationContext(
7478
this.docFormat = resolvedFormat;
7579
this.symbolProvider = symbolProvider;
7680
this.writerDelegator = new WriterDelegator<>(fileManifest, symbolProvider, resolvedFormat.writerFactory());
81+
this.snippetGenerators = snippetGenerators;
7782
}
7883

7984
@Override
@@ -112,4 +117,11 @@ public List<DocIntegration> integrations() {
112117
public DocFormat docFormat() {
113118
return this.docFormat;
114119
}
120+
121+
/**
122+
* @return returns the generators used to add snippets to docs.
123+
*/
124+
public List<SnippetGenerator> snippetGenerators() {
125+
return snippetGenerators;
126+
}
115127
}

smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/DocSettings.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package software.amazon.smithy.docgen.core;
77

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

2327
/**
2428
* Settings for documentation generation. These can be set in the
@@ -30,6 +34,7 @@ public record DocSettings(ShapeId service, String format) {
3034
public DocSettings {
3135
Objects.requireNonNull(service);
3236
Objects.requireNonNull(format);
37+
Objects.requireNonNull(snippetGeneratorSettings);
3338
}
3439

3540
/**
@@ -41,7 +46,8 @@ public record DocSettings(ShapeId service, String format) {
4146
public static DocSettings fromNode(ObjectNode pluginSettings) {
4247
return new DocSettings(
4348
pluginSettings.expectStringMember("service").expectShapeId(),
44-
pluginSettings.getStringMemberOrDefault("format", "sphinx-markdown")
49+
pluginSettings.getStringMemberOrDefault("format", "sphinx-markdown"),
50+
pluginSettings.getObjectMember("snippetGeneratorSettings").orElse(Node.objectNode())
4551
);
4652
}
4753
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.docgen.core;
7+
8+
import java.util.Optional;
9+
import software.amazon.smithy.build.MockManifest;
10+
import software.amazon.smithy.build.PluginContext;
11+
import software.amazon.smithy.build.model.ProjectionConfig;
12+
import software.amazon.smithy.model.Model;
13+
import software.amazon.smithy.model.node.Node;
14+
import software.amazon.smithy.model.node.ObjectNode;
15+
import software.amazon.smithy.utils.SmithyUnstableApi;
16+
17+
/**
18+
* Generates mock plugin contexts to pass along to snippet generators.
19+
*/
20+
@SmithyUnstableApi
21+
class MockPluginContextGenerator {
22+
private final PluginContext baseContext;
23+
private final ObjectNode pluginSettings;
24+
25+
/**
26+
* Constructs a MockPluginContextGenerator.
27+
*
28+
* @param baseContext The context to base plugin context generation on.
29+
* @param pluginSettings Settings specifically given to pass to plugins via the doc generator.
30+
*/
31+
MockPluginContextGenerator(PluginContext baseContext, ObjectNode pluginSettings) {
32+
if (baseContext.getProjection().isEmpty()) {
33+
// PluginContext will NPE if there's no projection config so you have to do it manually
34+
// until that gets fixed.
35+
var builder = PluginContext.builder()
36+
.projection(baseContext.getProjectionName(), ProjectionConfig.builder().build())
37+
.model(baseContext.getModel())
38+
.originalModel(baseContext.getOriginalModel().orElse(baseContext.getModel()))
39+
.events(baseContext.getEvents())
40+
.settings(baseContext.getSettings())
41+
.fileManifest(baseContext.getFileManifest())
42+
.sources(baseContext.getSources());
43+
baseContext.getPluginClassLoader().ifPresent(builder::pluginClassLoader);
44+
baseContext.getOriginalModel().ifPresent(builder::originalModel);
45+
baseContext.getArtifactName().ifPresent(builder::artifactName);
46+
baseContext = builder.build();
47+
}
48+
this.baseContext = baseContext;
49+
this.pluginSettings = pluginSettings;
50+
}
51+
52+
/**
53+
* Create a new plugin context with the given model and plugin name.
54+
*
55+
* @param model The version of the model to hand to the plugin.
56+
* @param pluginName The name of the plugin. This is used to search for existing config.
57+
* @return Returns a plugin context with the given model and config for the named plugin.
58+
*/
59+
PluginContext getStubbedContext(Model model, String pluginName) {
60+
ObjectNode settings = pluginSettings.getObjectMember(pluginName)
61+
.or(() -> baseContext.getProjection()
62+
.map(ProjectionConfig::getPlugins)
63+
.flatMap(plugins -> Optional.ofNullable(plugins.get(pluginName))))
64+
.orElse(Node.objectNode());
65+
66+
return baseContext.toBuilder()
67+
.fileManifest(new MockManifest())
68+
.artifactName(pluginName)
69+
.model(model)
70+
.settings(settings)
71+
.build();
72+
}
73+
}

smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/SmithyDocPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void execute(PluginContext pluginContext) {
4242
CodegenDirector<DocWriter, DocIntegration, DocGenerationContext, DocSettings> runner
4343
= new CodegenDirector<>();
4444

45-
runner.directedCodegen(new DirectedDocGen());
45+
runner.directedCodegen(new DirectedDocGen(pluginContext));
4646
runner.integrationClass(DocIntegration.class);
4747
runner.fileManifest(pluginContext.getFileManifest());
4848
runner.model(getValidatedModel(pluginContext.getModel()).unwrap());
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.docgen.core.generators;
7+
8+
import software.amazon.smithy.model.node.Node;
9+
import software.amazon.smithy.model.shapes.OperationShape;
10+
import software.amazon.smithy.model.shapes.Shape;
11+
import software.amazon.smithy.model.traits.ExamplesTrait.Example;
12+
13+
public class ExampleInputGenerator implements SnippetGenerator {
14+
@Override
15+
public String name() {
16+
return "input";
17+
}
18+
19+
@Override
20+
public boolean isWireProtocolGenerator() {
21+
return true;
22+
}
23+
24+
@Override
25+
public String tabTitle() {
26+
return "Input";
27+
}
28+
29+
@Override
30+
public String language() {
31+
return "json";
32+
}
33+
34+
@Override
35+
public String generateShapeSnippet(Shape shape, Node value) {
36+
return "";
37+
}
38+
39+
@Override
40+
public String generateExampleSnippet(OperationShape operation, Example example) {
41+
return Node.prettyPrintJson(example.getInput());
42+
}
43+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package software.amazon.smithy.docgen.core.generators;
7+
8+
import software.amazon.smithy.model.node.Node;
9+
import software.amazon.smithy.model.shapes.OperationShape;
10+
import software.amazon.smithy.model.shapes.Shape;
11+
import software.amazon.smithy.model.traits.ExamplesTrait.Example;
12+
13+
public class ExampleOutputGenerator implements SnippetGenerator {
14+
@Override
15+
public String name() {
16+
return "output";
17+
}
18+
19+
@Override
20+
public String tabTitle() {
21+
return "Output";
22+
}
23+
24+
@Override
25+
public String language() {
26+
return "json";
27+
}
28+
29+
@Override
30+
public String generateShapeSnippet(Shape shape, Node value) {
31+
return "";
32+
}
33+
34+
@Override
35+
public String generateExampleSnippet(OperationShape operation, Example example) {
36+
return Node.prettyPrintJson(example.getOutput().orElse(Node.objectNode()));
37+
}
38+
}

smithy-docgen-core/src/main/java/software/amazon/smithy/docgen/core/generators/OperationGenerator.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import software.amazon.smithy.docgen.core.sections.ShapeSection;
2121
import software.amazon.smithy.docgen.core.writers.DocWriter;
2222
import software.amazon.smithy.docgen.core.writers.DocWriter.ListType;
23-
import software.amazon.smithy.model.node.Node;
2423
import software.amazon.smithy.model.shapes.OperationShape;
2524
import software.amazon.smithy.model.shapes.ServiceShape;
2625
import software.amazon.smithy.model.traits.ExamplesTrait;
@@ -144,17 +143,11 @@ private void writeExamples(
144143
example.getDocumentation().ifPresent(writer::writeCommonMark);
145144

146145
writer.openTabGroup();
147-
// TODO: create example writer interface allow integrations to register them
148-
149-
// This is just a dummy placehodler tab here to exercise tab creation before
150-
// there's an interface for it.
151-
writer.openCodeTab("Input", "json");
152-
writer.write(Node.prettyPrintJson(example.getInput()));
153-
writer.closeCodeTab();
154-
writer.openCodeTab("Output", "json");
155-
writer.write(Node.prettyPrintJson(example.getOutput().orElse(Node.objectNode())));
156-
writer.closeCodeTab();
157-
146+
for (var snippetGenerator : context.snippetGenerators()) {
147+
writer.openCodeTab(snippetGenerator.tabTitle(), snippetGenerator.language());
148+
writer.write(snippetGenerator.generateExampleSnippet(operation, example));
149+
writer.closeCodeTab();
150+
}
158151
writer.closeTabGroup();
159152

160153
writer.closeHeading();

0 commit comments

Comments
 (0)