Skip to content

Commit a1ee495

Browse files
Merge pull request #9 from smithy-lang/shape-generation
Generate docs for structures and members
2 parents 023e543 + cae2df3 commit a1ee495

File tree

18 files changed

+916
-46
lines changed

18 files changed

+916
-46
lines changed

smithy-docgen-core/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ dependencies {
1616
implementation("software.amazon.smithy:smithy-model:$smithyVersion")
1717
implementation("software.amazon.smithy:smithy-utils:$smithyVersion")
1818
implementation("software.amazon.smithy:smithy-codegen-core:$smithyVersion")
19+
implementation("software.amazon.smithy:smithy-linters:$smithyVersion")
1920
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import software.amazon.smithy.codegen.core.directed.GenerateStructureDirective;
1818
import software.amazon.smithy.codegen.core.directed.GenerateUnionDirective;
1919
import software.amazon.smithy.docgen.core.generators.ServiceGenerator;
20+
import software.amazon.smithy.docgen.core.generators.StructureGenerator;
2021
import software.amazon.smithy.utils.SmithyUnstableApi;
2122

2223
/**
@@ -48,7 +49,7 @@ public void generateService(GenerateServiceDirective<DocGenerationContext, DocSe
4849

4950
@Override
5051
public void generateStructure(GenerateStructureDirective<DocGenerationContext, DocSettings> directive) {
51-
// no-op for now
52+
new StructureGenerator().accept(directive);
5253
}
5354

5455
@Override

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

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import java.util.Locale;
1111
import java.util.Objects;
12+
import java.util.Optional;
1213
import java.util.logging.Logger;
1314
import software.amazon.smithy.codegen.core.Symbol;
1415
import software.amazon.smithy.codegen.core.SymbolProvider;
@@ -17,9 +18,11 @@
1718
import software.amazon.smithy.model.shapes.ServiceShape;
1819
import software.amazon.smithy.model.shapes.Shape;
1920
import software.amazon.smithy.model.shapes.ShapeVisitor;
21+
import software.amazon.smithy.model.shapes.StructureShape;
2022
import software.amazon.smithy.model.traits.StringTrait;
2123
import software.amazon.smithy.model.traits.TitleTrait;
2224
import software.amazon.smithy.utils.SmithyUnstableApi;
25+
import software.amazon.smithy.utils.StringUtils;
2326

2427
/**
2528
* Creates documentation Symbols for each shape in the model.
@@ -31,11 +34,11 @@
3134
* <li>{@code name}: The name of the symbol will be used as the title for its
3235
* definition section. For services, this defaults to the value of the
3336
* {@code title} trait. For other shapes, it defaults to the shape name including
34-
* any renames from the attached service. For members, the member name is appended
35-
* to the parent with a separating {@code -}.
37+
* any renames from the attached service.
3638
* <li>{@code definitionFile}: The file in which the documentation for this shape
3739
* should be written. By default these are all written to a single flat directory.
38-
* If this is empty, the shape does not have its own definition section.
40+
* If this is empty, the shape does not have its own definition section and cannot
41+
* be linked to.
3942
* <li>{@link #SHAPE_PROPERTY}: A named Shape property containing the shape that
4043
* the symbol represents. Decorators provided by
4144
* {@link DocIntegration#decorateSymbolProvider} MUST set or preserve this
@@ -128,23 +131,39 @@ public Symbol serviceShape(ServiceShape shape) {
128131
.build();
129132
}
130133

134+
@Override
135+
public Symbol structureShape(StructureShape shape) {
136+
return getSymbolBuilder(shape)
137+
.definitionFile(getDefinitionFile(serviceShape, shape))
138+
.build();
139+
}
140+
141+
@Override
142+
public Symbol memberShape(MemberShape shape) {
143+
var builder = getSymbolBuilder(shape)
144+
.definitionFile(getDefinitionFile(serviceShape, model.expectShape(shape.getId().withoutMember())));
145+
146+
Optional<String> containerLinkId = model.expectShape(shape.getContainer())
147+
.accept(this)
148+
.getProperty(LINK_ID_PROPERTY, String.class);
149+
if (containerLinkId.isPresent()) {
150+
var linkId = containerLinkId.get() + "-" + getLinkId(getShapeName(serviceShape, shape));
151+
builder.putProperty(LINK_ID_PROPERTY, linkId);
152+
}
153+
return builder.build();
154+
}
155+
131156
private Symbol.Builder getSymbolBuilder(Shape shape) {
132157
var name = getShapeName(serviceShape, shape);
133158
return Symbol.builder()
134-
.name(name)
135-
.putProperty(SHAPE_PROPERTY, shape)
136-
.definitionFile(getDefinitionFile(serviceShape, shape))
137-
.putProperty(LINK_ID_PROPERTY, getLinkId(name))
138-
.putProperty(ENABLE_DEFAULT_FILE_EXTENSION, true);
159+
.name(name)
160+
.putProperty(SHAPE_PROPERTY, shape)
161+
.putProperty(LINK_ID_PROPERTY, getLinkId(name))
162+
.putProperty(ENABLE_DEFAULT_FILE_EXTENSION, true);
139163
}
140164

141165
private String getDefinitionFile(ServiceShape serviceShape, Shape shape) {
142-
if (shape.isMemberShape()) {
143-
return getDefinitionFile(serviceShape, model.expectShape(shape.getId().withoutMember()));
144-
}
145-
return getDefinitionFile(
146-
getShapeName(serviceShape, shape).replaceAll("\\s+", "")
147-
);
166+
return getDefinitionFile(getShapeName(serviceShape, shape).replaceAll("\\s+", ""));
148167
}
149168

150169
private String getDefinitionFile(String filename) {
@@ -157,11 +176,11 @@ private String getShapeName(ServiceShape serviceShape, Shape shape) {
157176
.map(StringTrait::getValue)
158177
.orElse(shape.getId().getName());
159178
}
160-
var name = shape.getId().getName(serviceShape);
161179
if (shape.isMemberShape()) {
162-
name += "-" + toMemberName(shape.asMemberShape().get());
180+
return toMemberName(shape.asMemberShape().get());
181+
} else {
182+
return shape.getId().getName(serviceShape);
163183
}
164-
return name;
165184
}
166185

167186
private String getLinkId(String shapeName) {
@@ -172,7 +191,7 @@ private String getLinkId(String shapeName) {
172191
// have impact.
173192
@Override
174193
protected Symbol getDefault(Shape shape) {
175-
return null;
194+
return getSymbolBuilder(shape).build();
176195
}
177196

178197
/**
@@ -209,7 +228,7 @@ public Symbol toSymbol(Shape shape) {
209228
}
210229

211230
private String addExtension(String path) {
212-
if (!path.endsWith(extension)) {
231+
if (!StringUtils.isBlank(path) && !path.endsWith(extension)) {
213232
path += extension;
214233
}
215234
return path;

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@
1212
import java.io.InputStreamReader;
1313
import java.nio.charset.Charset;
1414
import java.nio.file.Path;
15+
import java.nio.file.Paths;
1516
import java.util.ArrayList;
1617
import java.util.List;
18+
import java.util.Optional;
1719
import java.util.logging.Logger;
1820
import software.amazon.smithy.codegen.core.CodegenException;
21+
import software.amazon.smithy.codegen.core.Symbol;
1922
import software.amazon.smithy.utils.SmithyUnstableApi;
23+
import software.amazon.smithy.utils.StringUtils;
2024

2125
/**
2226
* Provides various utility methods.
@@ -83,4 +87,29 @@ public static String runCommand(String command, Path directory) {
8387
public static String normalizeNewlines(String input) {
8488
return input.replaceAll("\r?\n", System.lineSeparator());
8589
}
90+
91+
/**
92+
* Gets a relative link pointing to a given symbol.
93+
*
94+
* <p>If the given symbol has no definition file or no
95+
* {@link DocSymbolProvider#LINK_ID_PROPERTY}, the response will be empty.
96+
*
97+
* @param symbol The symbol to link to.
98+
* @param relativeTo A path that the symbol should be relative to. This must be the
99+
* path to the file containing the link.
100+
* @return Optionally returns a relative link to the given symbol.
101+
*/
102+
public static Optional<String> getSymbolLink(Symbol symbol, Path relativeTo) {
103+
Optional<String> linkId = symbol.getProperty(DocSymbolProvider.LINK_ID_PROPERTY, String.class);
104+
var relativeToParent = relativeTo.getParent();
105+
if (StringUtils.isBlank(symbol.getDefinitionFile())
106+
|| linkId.isEmpty()
107+
|| StringUtils.isBlank(linkId.get())
108+
|| relativeToParent == null) {
109+
return Optional.empty();
110+
}
111+
return Optional.of(format(
112+
"./%s#%s", relativeToParent.relativize(Paths.get(symbol.getDefinitionFile())), linkId.get()
113+
));
114+
}
86115
}

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

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,21 @@
66
package software.amazon.smithy.docgen.core;
77

88

9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.Optional;
912
import java.util.logging.Logger;
1013
import software.amazon.smithy.build.PluginContext;
1114
import software.amazon.smithy.build.SmithyBuildPlugin;
1215
import software.amazon.smithy.codegen.core.directed.CodegenDirector;
16+
import software.amazon.smithy.docgen.core.validation.DocValidationEventDecorator;
1317
import software.amazon.smithy.docgen.core.writers.DocWriter;
18+
import software.amazon.smithy.linters.InputOutputStructureReuseValidator;
19+
import software.amazon.smithy.model.Model;
20+
import software.amazon.smithy.model.validation.ValidatedResult;
21+
import software.amazon.smithy.model.validation.ValidationEvent;
22+
import software.amazon.smithy.model.validation.ValidationEventDecorator;
23+
import software.amazon.smithy.model.validation.suppressions.ModelBasedEventDecorator;
1424
import software.amazon.smithy.utils.SmithyInternalApi;
1525

1626
/**
@@ -29,19 +39,44 @@ public String getName() {
2939
@Override
3040
public void execute(PluginContext pluginContext) {
3141
LOGGER.fine("Beginning documentation generation.");
32-
DocSettings docSettings = DocSettings.from(pluginContext.getSettings());
42+
DocSettings settings = DocSettings.from(pluginContext.getSettings());
3343

3444
CodegenDirector<DocWriter, DocIntegration, DocGenerationContext, DocSettings> runner
3545
= new CodegenDirector<>();
3646

3747
runner.directedCodegen(new DirectedDocGen());
3848
runner.integrationClass(DocIntegration.class);
3949
runner.fileManifest(pluginContext.getFileManifest());
40-
runner.model(pluginContext.getModel());
41-
runner.settings(docSettings);
42-
runner.service(docSettings.service());
50+
runner.model(getValidatedModel(pluginContext.getModel()).unwrap());
51+
runner.settings(settings);
52+
runner.service(settings.service());
4353
runner.performDefaultCodegenTransforms();
4454
runner.run();
4555
LOGGER.fine("Finished documentation generation.");
4656
}
57+
58+
private ValidatedResult<Model> getValidatedModel(Model model) {
59+
// This decorator will add context for why these are particularly important for docs.
60+
ValidationEventDecorator eventDecorator = new DocValidationEventDecorator();
61+
62+
// This will discover and apply suppressions from the model.
63+
Optional<ValidationEventDecorator> modelDecorator = new ModelBasedEventDecorator()
64+
.createDecorator(model).getResult();
65+
if (modelDecorator.isPresent()) {
66+
eventDecorator = ValidationEventDecorator.compose(List.of(modelDecorator.get(), eventDecorator));
67+
}
68+
69+
var events = new ArrayList<ValidationEvent>();
70+
for (var event : validate(model)) {
71+
if (eventDecorator.canDecorate(event)) {
72+
event = eventDecorator.decorate(event);
73+
}
74+
events.add(event);
75+
}
76+
return new ValidatedResult<>(model, events);
77+
}
78+
79+
private List<ValidationEvent> validate(Model model) {
80+
return new InputOutputStructureReuseValidator().validate(model);
81+
}
4782
}

0 commit comments

Comments
 (0)