From e993213729d68638587a4be06539576e9349efd3 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 9 Aug 2024 14:13:01 +0200 Subject: [PATCH 01/10] Config Doc - Use a common pattern for formatting data --- .../quarkus/maven/config/doc/AsciidocFormatter.java | 9 +++++++++ .../maven/config/doc/GenerateAsciidocMojo.java | 12 +++++++++--- .../src/main/resources/templates/allConfig.qute.adoc | 2 +- .../templates/tags/configProperty.qute.adoc | 4 ++-- .../resources/templates/tags/configSection.qute.adoc | 2 +- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java index cd347cdf5f07e..29e33c81c4985 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java @@ -7,6 +7,7 @@ import io.quarkus.annotation.processor.documentation.config.model.ConfigProperty; import io.quarkus.annotation.processor.documentation.config.model.ConfigSection; +import io.quarkus.annotation.processor.documentation.config.model.Extension; import io.quarkus.annotation.processor.documentation.config.model.JavadocElements.JavadocElement; import io.quarkus.annotation.processor.documentation.config.util.Types; @@ -185,6 +186,14 @@ String formatSectionTitle(ConfigSection configSection) { return javadoc.substring(0, dotIndex); } + String formatName(Extension extension) { + if (extension.name() == null) { + return extension.artifactId(); + } + + return extension.name(); + } + /** * Note that this is extremely brittle. Apparently, colons breaks the tooltips but if escaped with \, the \ appears in the * output. diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java index 66a3b538878df..8ab65c4aaef36 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java @@ -438,22 +438,28 @@ private static Engine initializeQuteEngine(AsciidocFormatter asciidocFormatter) .build()) .addValueResolver(ValueResolver.builder() .applyToBaseClass(ConfigProperty.class) - .applyToName("typeDescription") + .applyToName("formatTypeDescription") .applyToNoParameters() .resolveSync(ctx -> asciidocFormatter.formatTypeDescription((ConfigProperty) ctx.getBase())) .build()) .addValueResolver(ValueResolver.builder() .applyToBaseClass(ConfigProperty.class) - .applyToName("description") + .applyToName("formatDescription") .applyToNoParameters() .resolveSync(ctx -> asciidocFormatter.formatDescription((ConfigProperty) ctx.getBase())) .build()) .addValueResolver(ValueResolver.builder() .applyToBaseClass(ConfigSection.class) - .applyToName("title") + .applyToName("formatTitle") .applyToNoParameters() .resolveSync(ctx -> asciidocFormatter.formatSectionTitle((ConfigSection) ctx.getBase())) .build()) + .addValueResolver(ValueResolver.builder() + .applyToBaseClass(Extension.class) + .applyToName("formatName") + .applyToNoParameters() + .resolveSync(ctx -> asciidocFormatter.formatName((Extension) ctx.getBase())) + .build()) .build(); engine.putTemplate("configReference.qute.adoc", diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc index c4004ad655d26..698d764a2a577 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc @@ -5,7 +5,7 @@ icon:lock[title=Fixed at build time] Configuration property fixed at build time |=== {#for extensionConfigRootsEntry in configRootsByExtensions} -h|{extensionConfigRootsEntry.key.name.or(extensionConfigRootsEntry.key.artifactId)} +h|{extensionConfigRootsEntry.key.formatName.escapeCellContent} h|Type h|Default diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc index 33d7ca0c5430d..4ca746a55b740 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc @@ -6,8 +6,8 @@ a|{#if configProperty.phase.fixedAtBuildTime}icon:lock[title=Fixed at build time [.description] -- -{configProperty.description.escapeCellContent.or("")} +{configProperty.formatDescription.escapeCellContent.or("")} {#envVar configProperty /} -- -{#if configProperty.enum}a{/if}|{configProperty.typeDescription} +{#if configProperty.enum}a{/if}|{configProperty.formatTypeDescription.escapeCellContent.or("")} |{#if configProperty.defaultValue}`{configProperty.defaultValue.escapeCellContent}`{#else if !configProperty.optional}required icon:exclamation-circle[title=Configuration property is required]{/if} diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc index c3100613be598..fe23805fd70bc 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc @@ -1,4 +1,4 @@ -h|[[{configSection.toAnchor(extension)}]] {configSection.title.escapeCellContent} +h|[[{configSection.toAnchor(extension)}]] {configSection.formatTitle.escapeCellContent} h|Type h|Default From dc7ddb243e42a6e6684ca2a6dd7065718ee33de4 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 9 Aug 2024 14:21:17 +0200 Subject: [PATCH 02/10] Config Doc - Use enum tooltip also for default value This is an improvement over what we had. Fixes #42374 --- .../java/io/quarkus/maven/config/doc/AsciidocFormatter.java | 4 ++++ .../io/quarkus/maven/config/doc/GenerateAsciidocMojo.java | 6 ++++++ .../main/resources/templates/tags/configProperty.qute.adoc | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java index 29e33c81c4985..7dd76631b4cca 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java @@ -78,6 +78,10 @@ String formatTypeDescription(ConfigProperty configProperty) { String formatDefaultValue(ConfigProperty configProperty) { String defaultValue = configProperty.getDefaultValue(); + if (defaultValue == null) { + return null; + } + if (configProperty.isEnum()) { Optional enumConstant = configProperty.getEnumAcceptedValues().values().entrySet().stream() .filter(e -> e.getValue().configValue().equals(defaultValue)) diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java index 8ab65c4aaef36..82db513771b93 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java @@ -448,6 +448,12 @@ private static Engine initializeQuteEngine(AsciidocFormatter asciidocFormatter) .applyToNoParameters() .resolveSync(ctx -> asciidocFormatter.formatDescription((ConfigProperty) ctx.getBase())) .build()) + .addValueResolver(ValueResolver.builder() + .applyToBaseClass(ConfigProperty.class) + .applyToName("formatDefaultValue") + .applyToNoParameters() + .resolveSync(ctx -> asciidocFormatter.formatDefaultValue((ConfigProperty) ctx.getBase())) + .build()) .addValueResolver(ValueResolver.builder() .applyToBaseClass(ConfigSection.class) .applyToName("formatTitle") diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc index 4ca746a55b740..a96247c5e9728 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc @@ -10,4 +10,4 @@ a|{#if configProperty.phase.fixedAtBuildTime}icon:lock[title=Fixed at build time {#envVar configProperty /} -- {#if configProperty.enum}a{/if}|{configProperty.formatTypeDescription.escapeCellContent.or("")} -|{#if configProperty.defaultValue}`{configProperty.defaultValue.escapeCellContent}`{#else if !configProperty.optional}required icon:exclamation-circle[title=Configuration property is required]{/if} +|{#if configProperty.defaultValue}{configProperty.formatDefaultValue.escapeCellContent}{#else if !configProperty.optional}required icon:exclamation-circle[title=Configuration property is required]{/if} From 21d93600a0d45422c7a938639a0d09b6e88fe017 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 9 Aug 2024 14:36:34 +0200 Subject: [PATCH 03/10] Config Doc - Add CSS classes to extension name/section name/property path Fixes #42370 --- .../src/main/resources/templates/allConfig.qute.adoc | 2 +- .../src/main/resources/templates/configReference.qute.adoc | 2 +- .../src/main/resources/templates/tags/configProperty.qute.adoc | 2 +- .../src/main/resources/templates/tags/configSection.qute.adoc | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc index 698d764a2a577..21e121a194c75 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc @@ -5,7 +5,7 @@ icon:lock[title=Fixed at build time] Configuration property fixed at build time |=== {#for extensionConfigRootsEntry in configRootsByExtensions} -h|{extensionConfigRootsEntry.key.formatName.escapeCellContent} +h|[.extension-name]##{extensionConfigRootsEntry.key.formatName.escapeCellContent}## h|Type h|Default diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/configReference.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/configReference.qute.adoc index 3fe7092a6be20..f72cbba717aa1 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/configReference.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/configReference.qute.adoc @@ -4,7 +4,7 @@ icon:lock[title=Fixed at build time] Configuration property fixed at build time [.configuration-reference{#if searchable}.searchable{/if}, cols="80,.^10,.^10"] |=== -h|Configuration property +h|[.header-title]##Configuration property## h|Type h|Default diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc index a96247c5e9728..1c2e08ed27651 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc @@ -1,4 +1,4 @@ -a|{#if configProperty.phase.fixedAtBuildTime}icon:lock[title=Fixed at build time]{/if} [[{configProperty.toAnchor(extension)}]] `{configProperty.path}` +a|{#if configProperty.phase.fixedAtBuildTime}icon:lock[title=Fixed at build time]{/if} [[{configProperty.toAnchor(extension)}]] [.property-path]##`{configProperty.path}`## {#for additionalPath in configProperty.additionalPaths} `{additionalPath}` diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc index fe23805fd70bc..b551782d4254d 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc @@ -1,4 +1,4 @@ -h|[[{configSection.toAnchor(extension)}]] {configSection.formatTitle.escapeCellContent} +h|[[{configSection.toAnchor(extension)}]] [.section-name]##{configSection.formatTitle.escapeCellContent}## h|Type h|Default From 3ade72cd16d4588ec5637aea2945b17cc3447138 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 9 Aug 2024 14:48:40 +0200 Subject: [PATCH 04/10] Config Doc - Add the notion of section level Fixes #42379 --- .../config/model/ConfigSection.java | 10 ++++++++-- .../config/resolver/ConfigResolver.java | 17 ++++++++++++----- .../templates/tags/configSection.qute.adoc | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigSection.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigSection.java index 5f008988c0361..f816dc8f08f6e 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigSection.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigSection.java @@ -9,11 +9,13 @@ public final class ConfigSection extends AbstractConfigItem implements ConfigIte private boolean generated; private final List items = new ArrayList<>(); + private final int level; - public ConfigSection(String sourceClass, String sourceName, String path, String type, boolean generated, - boolean deprecated) { + public ConfigSection(String sourceClass, String sourceName, String path, String type, int level, + boolean generated, boolean deprecated) { super(sourceClass, sourceName, path, type, deprecated); this.generated = generated; + this.level = level; } @Override @@ -43,6 +45,10 @@ public boolean isGenerated() { return generated; } + public int getLevel() { + return level; + } + /** * This is used when we merge ConfigSection at the ConfigRoot level. * It can happen when for instance a path is both used at a given level and in an unnamed map. diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java index 772c60ddad671..f590012ada8a1 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java @@ -74,7 +74,7 @@ public ResolvedModel resolveModel() { configRoot.addQualifiedName(discoveryConfigRoot.getQualifiedName()); ResolutionContext context = new ResolutionContext(configRoot.getPrefix(), new ArrayList<>(), discoveryConfigRoot, - configRoot, false, false, false); + configRoot, 0, false, false, false); for (DiscoveryConfigProperty discoveryConfigProperty : discoveryConfigRoot.getProperties().values()) { resolveProperty(configRoot, existingRootConfigSections, discoveryConfigRoot.getPhase(), context, discoveryConfigProperty); @@ -132,16 +132,17 @@ private void resolveProperty(ConfigRoot configRoot, Map e } else { configSection = new ConfigSection(discoveryConfigProperty.getSourceClass(), discoveryConfigProperty.getSourceName(), propertyPath, typeQualifiedName, - discoveryConfigProperty.isSectionGenerated(), deprecated); + context.getSectionLevel(), discoveryConfigProperty.isSectionGenerated(), deprecated); context.getItemCollection().addItem(configSection); existingRootConfigSections.put(propertyPath, configSection); } configGroupContext = new ResolutionContext(potentiallyMappedPath, additionalPaths, discoveryConfigGroup, - configSection, isWithinMap, isWithMapWithUnnamedKey, deprecated); + configSection, context.getSectionLevel() + 1, isWithinMap, isWithMapWithUnnamedKey, deprecated); } else { configGroupContext = new ResolutionContext(potentiallyMappedPath, additionalPaths, discoveryConfigGroup, - context.getItemCollection(), isWithinMap, isWithMapWithUnnamedKey, deprecated); + context.getItemCollection(), context.getSectionLevel(), isWithinMap, isWithMapWithUnnamedKey, + deprecated); } for (DiscoveryConfigProperty configGroupProperty : discoveryConfigGroup.getProperties().values()) { @@ -244,13 +245,14 @@ private static class ResolutionContext { private final List additionalPaths; private final DiscoveryRootElement discoveryRootElement; private final ConfigItemCollection itemCollection; + private final int sectionLevel; private final boolean withinMap; private final boolean withinMapWithUnnamedKey; private final boolean deprecated; private ResolutionContext(String path, List additionalPaths, DiscoveryRootElement discoveryRootElement, ConfigItemCollection itemCollection, - boolean withinMap, boolean withinMapWithUnnamedKey, boolean deprecated) { + int sectionLevel, boolean withinMap, boolean withinMapWithUnnamedKey, boolean deprecated) { this.path = path; this.additionalPaths = additionalPaths; this.discoveryRootElement = discoveryRootElement; @@ -258,6 +260,7 @@ private ResolutionContext(String path, List additionalPaths, DiscoveryRo this.withinMap = withinMap; this.withinMapWithUnnamedKey = withinMapWithUnnamedKey; this.deprecated = deprecated; + this.sectionLevel = sectionLevel; } public String getPath() { @@ -276,6 +279,10 @@ public ConfigItemCollection getItemCollection() { return itemCollection; } + public int getSectionLevel() { + return sectionLevel; + } + public boolean isWithinMap() { return withinMap; } diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc index b551782d4254d..a2e6f37d6a921 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc @@ -1,4 +1,4 @@ -h|[[{configSection.toAnchor(extension)}]] [.section-name]##{configSection.formatTitle.escapeCellContent}## +h|[[{configSection.toAnchor(extension)}]] [.section-name.section-level{configSection.level}]##{configSection.formatTitle.escapeCellContent}## h|Type h|Default From 4972e9f50a4504343615398186567f9ba729a953 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 9 Aug 2024 18:43:59 +0200 Subject: [PATCH 05/10] Config Doc - Generate different anchors for section files We might sometimes want to include smaller section files in specific paragraphs and the whole document at the end. It wasn't possible until now because ids would have conflicted. Fixes #42371 --- .../config/doc/GenerateAsciidocMojo.java | 34 ++++++++++++------- .../resources/templates/allConfig.qute.adoc | 4 +-- .../templates/configReference.qute.adoc | 4 +-- .../templates/tags/configProperty.qute.adoc | 2 +- .../templates/tags/configSection.qute.adoc | 6 ++-- .../main/asciidoc/databases-dev-services.adoc | 2 +- .../asciidoc/elasticsearch-dev-services.adoc | 2 +- 7 files changed, 31 insertions(+), 23 deletions(-) diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java index 82db513771b93..d3417e205c159 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java @@ -114,7 +114,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { try { Files.writeString(configRootAdocPath, - generateConfigReference(quteEngine, summaryTableId, extension, configRoot, true)); + generateConfigReference(quteEngine, summaryTableId, extension, configRoot, "", true)); } catch (Exception e) { throw new MojoExecutionException("Unable to render config roots for top level prefix: " + topLevelPrefix + " in extension: " + extension, e); @@ -150,7 +150,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { try { Files.writeString(configRootAdocPath, - generateConfigReference(quteEngine, summaryTableId, extension, configRoot, true)); + generateConfigReference(quteEngine, summaryTableId, extension, configRoot, "", true)); } catch (Exception e) { throw new MojoExecutionException("Unable to render config roots for specific file: " + fileName + " in extension: " + extension, e); @@ -170,7 +170,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { try { Files.writeString(configSectionAdocPath, - generateConfigReference(quteEngine, summaryTableId, extension, generatedConfigSection, false)); + generateConfigReference(quteEngine, summaryTableId, extension, generatedConfigSection, + "_" + generatedConfigSection.getPath(), false)); } catch (Exception e) { throw new MojoExecutionException( "Unable to render config section for section: " + generatedConfigSection.getPath() @@ -193,12 +194,13 @@ public void execute() throws MojoExecutionException, MojoFailureException { } private static String generateConfigReference(Engine quteEngine, String summaryTableId, Extension extension, - ConfigItemCollection configItemCollection, boolean searchable) { + ConfigItemCollection configItemCollection, String additionalAnchorPrefix, boolean searchable) { return quteEngine.getTemplate("configReference.qute.adoc") .data("extension", extension) .data("configItemCollection", configItemCollection) .data("searchable", searchable) .data("summaryTableId", summaryTableId) + .data("additionalAnchorPrefix", additionalAnchorPrefix) .data("includeDurationNote", configItemCollection.hasDurationType()) .data("includeMemorySizeNote", configItemCollection.hasMemorySizeType()) .render(); @@ -210,6 +212,7 @@ private static String generateAllConfig(Engine quteEngine, .data("configRootsByExtensions", configRootsByExtensions) .data("searchable", true) .data("summaryTableId", "all-config") + .data("additionalAnchorPrefix", "") .data("includeDurationNote", true) .data("includeMemorySizeNote", true) .render(); @@ -420,21 +423,26 @@ private static Engine initializeQuteEngine(AsciidocFormatter asciidocFormatter) .addValueResolver(ValueResolver.builder() .applyToBaseClass(ConfigProperty.class) .applyToName("toAnchor") - .applyToParameters(1) - .resolveAsync(ctx -> ctx.evaluate(ctx.getParams().get(0)) - .thenApply(o -> asciidocFormatter.toAnchor( - ((Extension) o).artifactId() + "_" + ((ConfigProperty) ctx.getBase()).getPath()))) + .applyToParameters(2) + .resolveSync(ctx -> asciidocFormatter + .toAnchor(((Extension) ctx.evaluate(ctx.getParams().get(0)).toCompletableFuture().join()) + .artifactId() + + // the additional suffix + ctx.evaluate(ctx.getParams().get(1)).toCompletableFuture().join() + + "_" + ((ConfigProperty) ctx.getBase()).getPath())) .build()) // we need a different anchor for sections as otherwise we can have a conflict // (typically when you have an `enabled` property with parent name just under the section level) .addValueResolver(ValueResolver.builder() .applyToBaseClass(ConfigSection.class) .applyToName("toAnchor") - .applyToParameters(1) - .resolveAsync(ctx -> ctx.evaluate(ctx.getParams().get(0)) - .thenApply(o -> asciidocFormatter.toAnchor( - ((Extension) o).artifactId() + "_section_" - + ((ConfigSection) ctx.getBase()).getPath()))) + .applyToParameters(2) + .resolveSync(ctx -> asciidocFormatter + .toAnchor(((Extension) ctx.evaluate(ctx.getParams().get(0)).toCompletableFuture().join()) + .artifactId() + + // the additional suffix + ctx.evaluate(ctx.getParams().get(1)).toCompletableFuture().join() + + "_section_" + ((ConfigSection) ctx.getBase()).getPath())) .build()) .addValueResolver(ValueResolver.builder() .applyToBaseClass(ConfigProperty.class) diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc index 21e121a194c75..1f8b67336d8f5 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/allConfig.qute.adoc @@ -13,10 +13,10 @@ h|Default {#for item in configRoot.items} {#if !item.deprecated} {#if item.isSection} -{#configSection configSection=item extension=extensionConfigRootsEntry.key /} +{#configSection configSection=item extension=extensionConfigRootsEntry.key additionalAnchorPrefix=additionalAnchorPrefix /} {#else} -{#configProperty configProperty=item extension=extensionConfigRootsEntry.key /} +{#configProperty configProperty=item extension=extensionConfigRootsEntry.key additionalAnchorPrefix=additionalAnchorPrefix /} {/if} {/if} diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/configReference.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/configReference.qute.adoc index f72cbba717aa1..d27a9195b4c00 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/configReference.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/configReference.qute.adoc @@ -11,10 +11,10 @@ h|Default {#for item in configItemCollection.items} {#if !item.deprecated} {#if item.isSection} -{#configSection configSection=item extension=extension /} +{#configSection configSection=item extension=extension additionalAnchorPrefix=additionalAnchorPrefix /} {#else} -{#configProperty configProperty=item extension=extension /} +{#configProperty configProperty=item extension=extension additionalAnchorPrefix=additionalAnchorPrefix /} {/if} {/if} diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc index 1c2e08ed27651..dd3a23f0d0abc 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configProperty.qute.adoc @@ -1,4 +1,4 @@ -a|{#if configProperty.phase.fixedAtBuildTime}icon:lock[title=Fixed at build time]{/if} [[{configProperty.toAnchor(extension)}]] [.property-path]##`{configProperty.path}`## +a|{#if configProperty.phase.fixedAtBuildTime}icon:lock[title=Fixed at build time]{/if} [[{configProperty.toAnchor(extension, additionalAnchorPrefix)}]] [.property-path]##`{configProperty.path}`## {#for additionalPath in configProperty.additionalPaths} `{additionalPath}` diff --git a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc index a2e6f37d6a921..d8673758bdd46 100644 --- a/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc +++ b/devtools/config-doc-maven-plugin/src/main/resources/templates/tags/configSection.qute.adoc @@ -1,14 +1,14 @@ -h|[[{configSection.toAnchor(extension)}]] [.section-name.section-level{configSection.level}]##{configSection.formatTitle.escapeCellContent}## +h|[[{configSection.toAnchor(extension, additionalAnchorPrefix)}]] [.section-name.section-level{configSection.level}]##{configSection.formatTitle.escapeCellContent}## h|Type h|Default {#for item in configSection.items} {#if !item.deprecated} {#if item.isSection} -{#configSection configSection=item extension=extension /} +{#configSection configSection=item extension=extension additionalAnchorPrefix=additionalAnchorPrefix /} {#else} -{#configProperty configProperty=item extension=extension /} +{#configProperty configProperty=item extension=extension additionalAnchorPrefix=additionalAnchorPrefix /} {/if} {/if} diff --git a/docs/src/main/asciidoc/databases-dev-services.adoc b/docs/src/main/asciidoc/databases-dev-services.adoc index ae563a67dc2f2..8d640c4d15e57 100644 --- a/docs/src/main/asciidoc/databases-dev-services.adoc +++ b/docs/src/main/asciidoc/databases-dev-services.adoc @@ -121,7 +121,7 @@ In that case, you will need to stop and remove these containers manually. If you want to reuse containers for some Quarkus applications but not all of them, or some Dev Services but not all of them, you can disable this feature for a specific Dev Service by setting the configuration property -xref:quarkus-datasource_quarkus-datasource-devservices-reuse[`quarkus.datasource.devservices.reuse`/`quarkus.datasource."datasource-name".devservices.reuse`] +xref:quarkus-datasource_quarkus-datasource-devservices_quarkus-datasource-devservices-reuse[`quarkus.datasource.devservices.reuse`/`quarkus.datasource."datasource-name".devservices.reuse`] to `false`. == Mapping volumes into Dev Services for Database diff --git a/docs/src/main/asciidoc/elasticsearch-dev-services.adoc b/docs/src/main/asciidoc/elasticsearch-dev-services.adoc index 1f92726c7f973..48d4ec22fb249 100644 --- a/docs/src/main/asciidoc/elasticsearch-dev-services.adoc +++ b/docs/src/main/asciidoc/elasticsearch-dev-services.adoc @@ -138,7 +138,7 @@ In that case, you will need to stop and remove these containers manually. If you want to reuse containers for some Quarkus applications but not all of them, or some Dev Services but not all of them, you can disable this feature for a specific Dev Service by setting the configuration property -xref:elasticsearch-dev-services.adoc#quarkus-elasticsearch-rest-client_quarkus-elasticsearch-devservices-reuse[`quarkus.elasticsearch.devservices.reuse`] +xref:elasticsearch-dev-services.adoc#quarkus-elasticsearch-rest-client_quarkus-elasticsearch-devservices_quarkus-elasticsearch-devservices-reuse[`quarkus.elasticsearch.devservices.reuse`] to `false`. == Current limitations From 34b67830a7e720e33fef0fa0db60866a25b39f52 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 9 Aug 2024 19:26:30 +0200 Subject: [PATCH 06/10] Config Doc - Allow to override the top level prefix This is useful in extreme cases such as with ConfigConfig. Fixes #42372 --- .../config/discovery/DiscoveryConfigRoot.java | 9 ++++- .../config/model/ConfigRoot.java | 33 ++++++++++++++----- .../config/resolver/ConfigResolver.java | 4 +-- .../scanner/ConfigAnnotationScanner.java | 2 +- .../config/scanner/ConfigMappingListener.java | 20 ++++++++++- .../config/scanner/LegacyConfigListener.java | 20 ++++++++++- .../documentation/config/util/Types.java | 1 + .../java/io/quarkus/runtime/ConfigConfig.java | 4 +-- .../runtime/annotations/ConfigDocPrefix.java | 24 ++++++++++++++ .../config/doc/GenerateAsciidocMojo.java | 18 ++-------- docs/src/main/asciidoc/config-reference.adoc | 2 +- 11 files changed, 103 insertions(+), 34 deletions(-) create mode 100644 core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocPrefix.java diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigRoot.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigRoot.java index b42451ddd609c..86564da225709 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigRoot.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/discovery/DiscoveryConfigRoot.java @@ -11,14 +11,17 @@ public final class DiscoveryConfigRoot extends DiscoveryRootElement { private final String prefix; + private final String overriddenDocPrefix; private final ConfigPhase phase; private final String overriddenDocFileName; - public DiscoveryConfigRoot(Extension extension, String prefix, String binaryName, String qualifiedName, + public DiscoveryConfigRoot(Extension extension, String prefix, String overriddenDocPrefix, + String binaryName, String qualifiedName, ConfigPhase configPhase, String overriddenDocFileName, boolean configMapping) { super(extension, binaryName, qualifiedName, configMapping); this.prefix = prefix; + this.overriddenDocPrefix = overriddenDocPrefix; this.phase = configPhase; this.overriddenDocFileName = overriddenDocFileName; } @@ -27,6 +30,10 @@ public String getPrefix() { return prefix; } + public String getOverriddenDocPrefix() { + return overriddenDocPrefix; + } + public ConfigPhase getPhase() { return phase; } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigRoot.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigRoot.java index 583e081199d3e..756b226d3937b 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigRoot.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/model/ConfigRoot.java @@ -7,6 +7,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; + +import io.quarkus.annotation.processor.documentation.config.util.Markers; /** * At this stage, a config root is actually a prefix: we merged all the config roots with the same prefix. @@ -17,14 +20,19 @@ public class ConfigRoot implements ConfigItemCollection { private final Extension extension; private final String prefix; + // used by the doc generation to classify config roots + private final String topLevelPrefix; - private String overriddenDocFileName; + private final String overriddenDocFileName; private final List items = new ArrayList<>(); private final Set qualifiedNames = new HashSet<>(); - public ConfigRoot(Extension extension, String prefix) { + public ConfigRoot(Extension extension, String prefix, String overriddenDocPrefix, String overriddenDocFileName) { this.extension = extension; this.prefix = prefix; + this.overriddenDocFileName = overriddenDocFileName; + this.topLevelPrefix = overriddenDocPrefix != null ? buildTopLevelPrefix(overriddenDocPrefix) + : buildTopLevelPrefix(prefix); } public Extension getExtension() { @@ -39,13 +47,6 @@ public String getOverriddenDocFileName() { return overriddenDocFileName; } - public void setOverriddenDocFileName(String overriddenDocFileName) { - if (this.overriddenDocFileName != null) { - return; - } - this.overriddenDocFileName = overriddenDocFileName; - } - public void addQualifiedName(String qualifiedName) { qualifiedNames.add(qualifiedName); } @@ -64,6 +65,10 @@ public List getItems() { return Collections.unmodifiableList(items); } + public String getTopLevelPrefix() { + return topLevelPrefix; + } + public void merge(ConfigRoot other) { this.qualifiedNames.addAll(other.getQualifiedNames()); @@ -116,4 +121,14 @@ public boolean hasMemorySizeType() { } return false; } + + private static String buildTopLevelPrefix(String prefix) { + String[] prefixSegments = prefix.split(Pattern.quote(Markers.DOT)); + + if (prefixSegments.length == 1) { + return prefixSegments[0]; + } + + return prefixSegments[0] + Markers.DOT + prefixSegments[1]; + } } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java index f590012ada8a1..88a4098ca0e50 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/resolver/ConfigResolver.java @@ -67,10 +67,10 @@ public ResolvedModel resolveModel() { List configRoots = new ArrayList<>(); for (DiscoveryConfigRoot discoveryConfigRoot : configCollector.getConfigRoots()) { - ConfigRoot configRoot = new ConfigRoot(discoveryConfigRoot.getExtension(), discoveryConfigRoot.getPrefix()); + ConfigRoot configRoot = new ConfigRoot(discoveryConfigRoot.getExtension(), discoveryConfigRoot.getPrefix(), + discoveryConfigRoot.getOverriddenDocPrefix(), discoveryConfigRoot.getOverriddenDocFileName()); Map existingRootConfigSections = new HashMap<>(); - configRoot.setOverriddenDocFileName(discoveryConfigRoot.getOverriddenDocFileName()); configRoot.addQualifiedName(discoveryConfigRoot.getQualifiedName()); ResolutionContext context = new ResolutionContext(configRoot.getPrefix(), new ArrayList<>(), discoveryConfigRoot, diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java index 7f0aa469e9364..3e4df52d458ce 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java @@ -163,7 +163,7 @@ public void scanConfigMappingsWithoutConfigRoot(RoundEnvironment roundEnv, TypeE try { // we need to forge a dummy DiscoveryConfigRoot // it's mostly ignored in the listeners, except for checking if it's a config mapping (for mixed modules) - DiscoveryConfigRoot discoveryConfigRoot = new DiscoveryConfigRoot(config.getExtension(), "dummy", + DiscoveryConfigRoot discoveryConfigRoot = new DiscoveryConfigRoot(config.getExtension(), "dummy", "dummy", utils.element().getBinaryName(configMappingWithoutConfigRoot), configMappingWithoutConfigRoot.getQualifiedName().toString(), ConfigPhase.BUILD_TIME, null, true); diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java index 3fb6b2d777c96..b790c716a245b 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java @@ -38,6 +38,7 @@ public Optional onConfigRoot(TypeElement configRoot) { AnnotationMirror configRootAnnotation = null; AnnotationMirror configMappingAnnotion = null; + AnnotationMirror configDocPrefixAnnotation = null; AnnotationMirror configDocFileNameAnnotation = null; for (AnnotationMirror annotationMirror : configRoot.getAnnotationMirrors()) { @@ -51,6 +52,10 @@ public Optional onConfigRoot(TypeElement configRoot) { configMappingAnnotion = annotationMirror; continue; } + if (annotationName.equals(Types.ANNOTATION_CONFIG_DOC_PREFIX)) { + configDocPrefixAnnotation = annotationMirror; + continue; + } if (annotationName.equals(Types.ANNOTATION_CONFIG_DOC_FILE_NAME)) { configDocFileNameAnnotation = annotationMirror; continue; @@ -76,6 +81,18 @@ public Optional onConfigRoot(TypeElement configRoot) { } } + String overriddenDocPrefix = null; + if (configDocPrefixAnnotation != null) { + for (Map.Entry entry : configDocPrefixAnnotation + .getElementValues() + .entrySet()) { + if ("value()".equals(entry.getKey().toString())) { + overriddenDocPrefix = entry.getValue().getValue().toString(); + break; + } + } + } + String overriddenDocFileName = null; if (configDocFileNameAnnotation != null) { for (Map.Entry entry : configDocFileNameAnnotation @@ -91,7 +108,8 @@ public Optional onConfigRoot(TypeElement configRoot) { String rootPrefix = ConfigNamingUtil.getRootPrefix(prefix, "", configRoot.getSimpleName().toString(), configPhase); String binaryName = utils.element().getBinaryName(configRoot); - DiscoveryConfigRoot discoveryConfigRoot = new DiscoveryConfigRoot(config.getExtension(), rootPrefix, + DiscoveryConfigRoot discoveryConfigRoot = new DiscoveryConfigRoot(config.getExtension(), + rootPrefix, overriddenDocPrefix, binaryName, configRoot.getQualifiedName().toString(), configPhase, overriddenDocFileName, true); configCollector.addConfigRoot(discoveryConfigRoot); return Optional.of(discoveryConfigRoot); diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigListener.java index 8d9d3d823c85e..8e10dedbb7ef1 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigListener.java @@ -39,6 +39,7 @@ public Optional onConfigRoot(TypeElement configRoot) { ConfigPhase configPhase = ConfigPhase.BUILD_TIME; AnnotationMirror configRootAnnotation = null; + AnnotationMirror configDocPrefixAnnotation = null; AnnotationMirror configDocFileNameAnnotation = null; for (AnnotationMirror annotationMirror : configRoot.getAnnotationMirrors()) { @@ -48,6 +49,10 @@ public Optional onConfigRoot(TypeElement configRoot) { configRootAnnotation = annotationMirror; continue; } + if (annotationName.equals(Types.ANNOTATION_CONFIG_DOC_PREFIX)) { + configDocPrefixAnnotation = annotationMirror; + continue; + } if (annotationName.equals(Types.ANNOTATION_CONFIG_DOC_FILE_NAME)) { configDocFileNameAnnotation = annotationMirror; continue; @@ -73,6 +78,18 @@ public Optional onConfigRoot(TypeElement configRoot) { } } + String overriddenDocPrefix = null; + if (configDocPrefixAnnotation != null) { + for (Map.Entry entry : configDocPrefixAnnotation + .getElementValues() + .entrySet()) { + if ("value()".equals(entry.getKey().toString())) { + overriddenDocPrefix = entry.getValue().getValue().toString(); + break; + } + } + } + String overriddenDocFileName = null; if (configDocFileNameAnnotation != null) { for (Map.Entry entry : configDocFileNameAnnotation @@ -88,7 +105,8 @@ public Optional onConfigRoot(TypeElement configRoot) { String rootPrefix = ConfigNamingUtil.getRootPrefix(prefix, name, configRoot.getSimpleName().toString(), configPhase); String binaryName = utils.element().getBinaryName(configRoot); - DiscoveryConfigRoot discoveryConfigRoot = new DiscoveryConfigRoot(config.getExtension(), rootPrefix, + DiscoveryConfigRoot discoveryConfigRoot = new DiscoveryConfigRoot(config.getExtension(), + rootPrefix, overriddenDocPrefix, binaryName, configRoot.getQualifiedName().toString(), configPhase, overriddenDocFileName, false); configCollector.addConfigRoot(discoveryConfigRoot); diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/Types.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/Types.java index d8016274dd929..f98d74f701153 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/Types.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/Types.java @@ -30,6 +30,7 @@ private Types() { public static final String ANNOTATION_CONFIG_DOC_ENUM_VALUE = "io.quarkus.runtime.annotations.ConfigDocEnumValue"; public static final String ANNOTATION_CONFIG_DOC_DEFAULT = "io.quarkus.runtime.annotations.ConfigDocDefault"; public static final String ANNOTATION_CONFIG_DOC_FILE_NAME = "io.quarkus.runtime.annotations.ConfigDocFilename"; + public static final String ANNOTATION_CONFIG_DOC_PREFIX = "io.quarkus.runtime.annotations.ConfigDocPrefix"; public static final String ANNOTATION_CONFIG_WITH_CONVERTER = "io.smallrye.config.WithConverter"; public static final String ANNOTATION_CONFIG_WITH_NAME = "io.smallrye.config.WithName"; diff --git a/core/runtime/src/main/java/io/quarkus/runtime/ConfigConfig.java b/core/runtime/src/main/java/io/quarkus/runtime/ConfigConfig.java index 075f0b6dc70fe..760c2cabc68c5 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/ConfigConfig.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/ConfigConfig.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.Optional; -import io.quarkus.runtime.annotations.ConfigDocFilename; +import io.quarkus.runtime.annotations.ConfigDocPrefix; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; import io.smallrye.config.ConfigMapping; @@ -19,7 +19,7 @@ */ @ConfigMapping(prefix = "quarkus") @ConfigRoot(phase = ConfigPhase.RUN_TIME) -@ConfigDocFilename("quarkus-core_quarkus-config.adoc") +@ConfigDocPrefix("quarkus.config") public interface ConfigConfig { /** * A comma separated list of profiles that will be active when Quarkus launches. diff --git a/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocPrefix.java b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocPrefix.java new file mode 100644 index 0000000000000..befc4bc97c211 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/annotations/ConfigDocPrefix.java @@ -0,0 +1,24 @@ +package io.quarkus.runtime.annotations; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * This annotation can be used when you want to override the top level prefix from the ConfigRoot/ConfigMapping for doc + * generation. + *

+ * This is for instance useful for {@code ConfigConfig}, which is an odd beast. + *

+ * Should be considered very last resort. + */ +@Documented +@Retention(RUNTIME) +@Target({ ElementType.TYPE }) +public @interface ConfigDocPrefix { + + String value(); +} diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java index d3417e205c159..65b7295f4a9f2 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java @@ -18,7 +18,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; -import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.maven.execution.MavenSession; @@ -44,7 +43,6 @@ import io.quarkus.annotation.processor.documentation.config.model.JavadocElements; import io.quarkus.annotation.processor.documentation.config.model.JavadocElements.JavadocElement; import io.quarkus.annotation.processor.documentation.config.model.ResolvedModel; -import io.quarkus.annotation.processor.documentation.config.util.Markers; import io.quarkus.qute.Engine; import io.quarkus.qute.ReflectionValueResolver; import io.quarkus.qute.UserTagSectionHelper; @@ -293,15 +291,13 @@ private static MergedModel mergeModel(List targetDirectories) throws MojoE continue; } - String topLevelPrefix = getTopLevelPrefix(configRoot.getPrefix()); - Map extensionConfigRoots = configRoots.computeIfAbsent(configRoot.getExtension(), e -> new HashMap<>()); - ConfigRoot existingConfigRoot = extensionConfigRoots.get(topLevelPrefix); + ConfigRoot existingConfigRoot = extensionConfigRoots.get(configRoot.getTopLevelPrefix()); if (existingConfigRoot == null) { - extensionConfigRoots.put(topLevelPrefix, configRoot); + extensionConfigRoots.put(configRoot.getTopLevelPrefix(), configRoot); } else { existingConfigRoot.merge(configRoot); } @@ -360,16 +356,6 @@ private static void collectGeneratedConfigSections(List extension } } - private static String getTopLevelPrefix(String prefix) { - String[] prefixSegments = prefix.split(Pattern.quote(Markers.DOT)); - - if (prefixSegments.length == 1) { - return prefixSegments[0]; - } - - return prefixSegments[0] + Markers.DOT + prefixSegments[1]; - } - private static List findTargetDirectories(Path scanDirectory) throws MojoExecutionException { try { List targets = new ArrayList<>(); diff --git a/docs/src/main/asciidoc/config-reference.adoc b/docs/src/main/asciidoc/config-reference.adoc index 5ca6fb93d3191..d11d95c38d2f4 100644 --- a/docs/src/main/asciidoc/config-reference.adoc +++ b/docs/src/main/asciidoc/config-reference.adoc @@ -847,4 +847,4 @@ link:https://smallrye.io/smallrye-config/Main[SmallRye Config documentation]. == Configuration Reference -include::{generated-dir}/config/quarkus-core_quarkus-config.adoc[opts=optional, leveloffset=+1] +include::{generated-dir}/config/quarkus-core_quarkus.config.adoc[opts=optional, leveloffset=+1] From 5d667d292cbe91142cfd2ae2fbe77c32c834253c Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Sat, 10 Aug 2024 17:43:35 +0200 Subject: [PATCH 07/10] Config Doc - Clean up JavadocUtil constructor --- .../processor/documentation/config/util/JavadocUtil.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/JavadocUtil.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/JavadocUtil.java index c9970cefcf8d3..0e4115d278b86 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/JavadocUtil.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/JavadocUtil.java @@ -6,8 +6,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.processing.ProcessingEnvironment; - public final class JavadocUtil { static final String VERTX_JAVA_DOC_SITE = "https://vertx.io/docs/apidocs/"; @@ -24,7 +22,7 @@ public final class JavadocUtil { EXTENSION_JAVA_DOC_LINK.put("io.agroal.", AGROAL_API_JAVA_DOC_SITE); } - private JavadocUtil(ProcessingEnvironment processingEnv) { + private JavadocUtil() { } /** From cc7f8aa5afa796a3bdc49c065b0b0a7dd31e252d Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Sat, 10 Aug 2024 17:49:46 +0200 Subject: [PATCH 08/10] Config Doc - Only keep the title for sections --- .../scanner/ConfigAnnotationScanner.java | 4 ++-- .../scanner/JavadocConfigMappingListener.java | 23 ++++++++++++++----- .../JavadocLegacyConfigRootListener.java | 23 ++++++++++++++----- ...ner.java => LegacyConfigRootListener.java} | 4 ++-- .../config/doc/ConfigSectionJavadoc.java | 19 --------------- 5 files changed, 38 insertions(+), 35 deletions(-) rename core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/{LegacyConfigListener.java => LegacyConfigRootListener.java} (98%) delete mode 100644 devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/ConfigSectionJavadoc.java diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java index 3e4df52d458ce..7618489e6326d 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigAnnotationScanner.java @@ -75,7 +75,7 @@ public ConfigAnnotationScanner(Config config, Utils utils) { configMappingWithoutConfigRootListeners.add(new JavadocConfigMappingListener(config, utils, configCollector)); } else { configRootListeners.add(new JavadocLegacyConfigRootListener(config, utils, configCollector)); - configRootListeners.add(new LegacyConfigListener(config, utils, configCollector)); + configRootListeners.add(new LegacyConfigRootListener(config, utils, configCollector)); } } else { // TODO #42114 remove once fixed @@ -84,7 +84,7 @@ public ConfigAnnotationScanner(Config config, Utils utils) { configRootListeners.add(new JavadocConfigMappingListener(config, utils, configCollector)); configRootListeners.add(new JavadocLegacyConfigRootListener(config, utils, configCollector)); configRootListeners.add(new ConfigMappingListener(config, utils, configCollector)); - configRootListeners.add(new LegacyConfigListener(config, utils, configCollector)); + configRootListeners.add(new LegacyConfigRootListener(config, utils, configCollector)); configMappingWithoutConfigRootListeners.add(new JavadocConfigMappingListener(config, utils, configCollector)); } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/JavadocConfigMappingListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/JavadocConfigMappingListener.java index 8c229e40a494a..385fbf401fbe1 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/JavadocConfigMappingListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/JavadocConfigMappingListener.java @@ -7,6 +7,7 @@ import io.quarkus.annotation.processor.documentation.config.discovery.DiscoveryRootElement; import io.quarkus.annotation.processor.documentation.config.discovery.ParsedJavadoc; +import io.quarkus.annotation.processor.documentation.config.discovery.ParsedJavadocSection; import io.quarkus.annotation.processor.documentation.config.discovery.ResolvedType; import io.quarkus.annotation.processor.documentation.config.formatter.JavadocToAsciidocTransformer; import io.quarkus.annotation.processor.documentation.config.model.JavadocElements.JavadocElement; @@ -38,20 +39,30 @@ public void onEnclosedMethod(DiscoveryRootElement discoveryRootElement, TypeElem } Optional rawJavadoc = utils.element().getJavadoc(method); + boolean isSection = utils.element().isAnnotationPresent(method, Types.ANNOTATION_CONFIG_DOC_SECTION); if (rawJavadoc.isEmpty()) { // We require a Javadoc for config items that are not config groups except if they are a section - if (!resolvedType.isConfigGroup() - || utils.element().isAnnotationPresent(method, Types.ANNOTATION_CONFIG_DOC_SECTION)) { + if (!resolvedType.isConfigGroup() || isSection) { utils.element().addMissingJavadocError(method); } return; } - ParsedJavadoc parsedJavadoc = JavadocToAsciidocTransformer.INSTANCE.parseConfigItemJavadoc(rawJavadoc.get()); + if (isSection) { + // for sections, we only keep the title + ParsedJavadocSection parsedJavadocSection = JavadocToAsciidocTransformer.INSTANCE + .parseConfigSectionJavadoc(rawJavadoc.get()); - configCollector.addJavadocElement( - clazz.getQualifiedName().toString() + Markers.DOT + method.getSimpleName().toString(), - new JavadocElement(parsedJavadoc.description(), parsedJavadoc.since(), rawJavadoc.get())); + configCollector.addJavadocElement( + clazz.getQualifiedName().toString() + Markers.DOT + method.getSimpleName().toString(), + new JavadocElement(parsedJavadocSection.title(), null, rawJavadoc.get())); + } else { + ParsedJavadoc parsedJavadoc = JavadocToAsciidocTransformer.INSTANCE.parseConfigItemJavadoc(rawJavadoc.get()); + + configCollector.addJavadocElement( + clazz.getQualifiedName().toString() + Markers.DOT + method.getSimpleName().toString(), + new JavadocElement(parsedJavadoc.description(), parsedJavadoc.since(), rawJavadoc.get())); + } } } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/JavadocLegacyConfigRootListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/JavadocLegacyConfigRootListener.java index 934c4c7639729..8b08f37a6fac4 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/JavadocLegacyConfigRootListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/JavadocLegacyConfigRootListener.java @@ -7,6 +7,7 @@ import io.quarkus.annotation.processor.documentation.config.discovery.DiscoveryRootElement; import io.quarkus.annotation.processor.documentation.config.discovery.ParsedJavadoc; +import io.quarkus.annotation.processor.documentation.config.discovery.ParsedJavadocSection; import io.quarkus.annotation.processor.documentation.config.discovery.ResolvedType; import io.quarkus.annotation.processor.documentation.config.formatter.JavadocToAsciidocTransformer; import io.quarkus.annotation.processor.documentation.config.model.JavadocElements.JavadocElement; @@ -38,20 +39,30 @@ public void onEnclosedField(DiscoveryRootElement discoveryRootElement, TypeEleme } Optional rawJavadoc = utils.element().getJavadoc(field); + boolean isSection = utils.element().isAnnotationPresent(field, Types.ANNOTATION_CONFIG_DOC_SECTION); if (rawJavadoc.isEmpty()) { // We require a Javadoc for config items that are not config groups except if they are a section - if (!resolvedType.isConfigGroup() - || utils.element().isAnnotationPresent(field, Types.ANNOTATION_CONFIG_DOC_SECTION)) { + if (!resolvedType.isConfigGroup() || isSection) { utils.element().addMissingJavadocError(field); } return; } - ParsedJavadoc parsedJavadoc = JavadocToAsciidocTransformer.INSTANCE.parseConfigItemJavadoc(rawJavadoc.get()); + if (isSection) { + // for sections, we only keep the title + ParsedJavadocSection parsedJavadocSection = JavadocToAsciidocTransformer.INSTANCE + .parseConfigSectionJavadoc(rawJavadoc.get()); - configCollector.addJavadocElement( - clazz.getQualifiedName().toString() + Markers.DOT + field.getSimpleName().toString(), - new JavadocElement(parsedJavadoc.description(), parsedJavadoc.since(), rawJavadoc.get())); + configCollector.addJavadocElement( + clazz.getQualifiedName().toString() + Markers.DOT + field.getSimpleName().toString(), + new JavadocElement(parsedJavadocSection.title(), null, rawJavadoc.get())); + } else { + ParsedJavadoc parsedJavadoc = JavadocToAsciidocTransformer.INSTANCE.parseConfigItemJavadoc(rawJavadoc.get()); + + configCollector.addJavadocElement( + clazz.getQualifiedName().toString() + Markers.DOT + field.getSimpleName().toString(), + new JavadocElement(parsedJavadoc.description(), parsedJavadoc.since(), rawJavadoc.get())); + } } } diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigListener.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java similarity index 98% rename from core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigListener.java rename to core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java index 8e10dedbb7ef1..e4b3e31a84030 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigListener.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/LegacyConfigRootListener.java @@ -23,9 +23,9 @@ import io.quarkus.annotation.processor.util.Strings; import io.quarkus.annotation.processor.util.Utils; -public class LegacyConfigListener extends AbstractConfigListener { +public class LegacyConfigRootListener extends AbstractConfigListener { - LegacyConfigListener(Config config, Utils utils, ConfigCollector configCollector) { + LegacyConfigRootListener(Config config, Utils utils, ConfigCollector configCollector) { super(config, utils, configCollector); } diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/ConfigSectionJavadoc.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/ConfigSectionJavadoc.java deleted file mode 100644 index c7a040950a80f..0000000000000 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/ConfigSectionJavadoc.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.quarkus.maven.config.doc; - -record ConfigSectionJavadoc(String title, String details) { - - static ConfigSectionJavadoc of(String javadoc) { - if (javadoc == null || javadoc.isBlank()) { - return new ConfigSectionJavadoc(null, null); - } - - javadoc = javadoc.trim(); - int dotIndex = javadoc.indexOf("."); - - if (dotIndex == -1 || dotIndex == javadoc.length() - 1) { - return new ConfigSectionJavadoc(javadoc, null); - } - - return new ConfigSectionJavadoc(javadoc.substring(0, dotIndex).trim(), javadoc.substring(dotIndex).trim()); - } -} From 036b79b30180a19e96cc5c4ae1e8dc11f3df1c70 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Sat, 10 Aug 2024 18:05:22 +0200 Subject: [PATCH 09/10] Config Doc - Move the model merger into the annotation processor This way, it can more easily get reused by a potential Gradle plugin. --- .../config/merger/JavadocMerger.java | 45 +++++ .../config/merger}/JavadocRepository.java | 4 +- .../config/merger}/MergedModel.java | 7 +- .../config/merger/ModelMerger.java | 140 ++++++++++++++++ .../config/util/JacksonMappers.java | 33 ++++ .../annotation/processor/util/FilerUtil.java | 25 +-- .../maven/config/doc/AsciidocFormatter.java | 1 + .../config/doc/GenerateAsciidocMojo.java | 155 +----------------- 8 files changed, 236 insertions(+), 174 deletions(-) create mode 100644 core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/JavadocMerger.java rename {devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc => core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger}/JavadocRepository.java (85%) rename {devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc => core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger}/MergedModel.java (84%) create mode 100644 core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/ModelMerger.java create mode 100644 core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/JacksonMappers.java diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/JavadocMerger.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/JavadocMerger.java new file mode 100644 index 0000000000000..43e4f42a36a41 --- /dev/null +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/JavadocMerger.java @@ -0,0 +1,45 @@ +package io.quarkus.annotation.processor.documentation.config.merger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import io.quarkus.annotation.processor.Outputs; +import io.quarkus.annotation.processor.documentation.config.model.JavadocElements; +import io.quarkus.annotation.processor.documentation.config.model.JavadocElements.JavadocElement; +import io.quarkus.annotation.processor.documentation.config.util.JacksonMappers; + +public final class JavadocMerger { + + private JavadocMerger() { + } + + public static JavadocRepository mergeJavadocElements(List buildOutputDirectories) { + Map javadocElementsMap = new HashMap<>(); + + for (Path buildOutputDirectory : buildOutputDirectories) { + Path javadocPath = buildOutputDirectory.resolve(Outputs.QUARKUS_CONFIG_DOC_JAVADOC); + if (!Files.isReadable(javadocPath)) { + continue; + } + + try { + JavadocElements javadocElements = JacksonMappers.yamlObjectReader().readValue(javadocPath.toFile(), + JavadocElements.class); + + if (javadocElements.elements() == null || javadocElements.elements().isEmpty()) { + continue; + } + + javadocElementsMap.putAll(javadocElements.elements()); + } catch (IOException e) { + throw new IllegalStateException("Unable to parse: " + javadocPath, e); + } + } + + return new JavadocRepository(javadocElementsMap); + } +} diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/JavadocRepository.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/JavadocRepository.java similarity index 85% rename from devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/JavadocRepository.java rename to core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/JavadocRepository.java index b4127463455ab..8fcb1840df6b7 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/JavadocRepository.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/JavadocRepository.java @@ -1,4 +1,4 @@ -package io.quarkus.maven.config.doc; +package io.quarkus.annotation.processor.documentation.config.merger; import java.util.Map; import java.util.Optional; @@ -6,7 +6,7 @@ import io.quarkus.annotation.processor.documentation.config.model.JavadocElements.JavadocElement; import io.quarkus.annotation.processor.documentation.config.util.Markers; -final class JavadocRepository { +public final class JavadocRepository { private final Map javadocElementsMap; diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/MergedModel.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/MergedModel.java similarity index 84% rename from devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/MergedModel.java rename to core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/MergedModel.java index 11d93d54d8331..de6f13ce7cdf7 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/MergedModel.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/MergedModel.java @@ -1,4 +1,4 @@ -package io.quarkus.maven.config.doc; +package io.quarkus.annotation.processor.documentation.config.merger; import java.util.Collections; import java.util.List; @@ -8,6 +8,9 @@ import io.quarkus.annotation.processor.documentation.config.model.ConfigSection; import io.quarkus.annotation.processor.documentation.config.model.Extension; +/** + * The merged model we obtain after merging all the ResolvedModels from the current project. + */ public class MergedModel { private final Map> configRoots; @@ -16,7 +19,7 @@ public class MergedModel { private final Map> generatedConfigSections; - public MergedModel(Map> configRoots, + MergedModel(Map> configRoots, Map configRootsInSpecificFile, Map> configSections) { this.configRoots = Collections.unmodifiableMap(configRoots); diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/ModelMerger.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/ModelMerger.java new file mode 100644 index 0000000000000..29867c3299fc2 --- /dev/null +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/merger/ModelMerger.java @@ -0,0 +1,140 @@ +package io.quarkus.annotation.processor.documentation.config.merger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import io.quarkus.annotation.processor.Outputs; +import io.quarkus.annotation.processor.documentation.config.model.AbstractConfigItem; +import io.quarkus.annotation.processor.documentation.config.model.ConfigItemCollection; +import io.quarkus.annotation.processor.documentation.config.model.ConfigRoot; +import io.quarkus.annotation.processor.documentation.config.model.ConfigSection; +import io.quarkus.annotation.processor.documentation.config.model.Extension; +import io.quarkus.annotation.processor.documentation.config.model.Extension.NameSource; +import io.quarkus.annotation.processor.documentation.config.model.ResolvedModel; +import io.quarkus.annotation.processor.documentation.config.util.JacksonMappers; + +public final class ModelMerger { + + private ModelMerger() { + } + + /** + * Merge all the resolved models obtained from a list of build output directories (e.g. in the case of Maven, the list of + * target/ directories found in the parent directory scanned). + */ + public static MergedModel mergeModel(List buildOutputDirectories) { + // keyed on extension and then top level prefix + Map> configRoots = new HashMap<>(); + // keyed on file name + Map configRootsInSpecificFile = new TreeMap<>(); + // keyed on extension + Map> generatedConfigSections = new TreeMap<>(); + + for (Path buildOutputDirectory : buildOutputDirectories) { + Path resolvedModelPath = buildOutputDirectory.resolve(Outputs.QUARKUS_CONFIG_DOC_MODEL); + if (!Files.isReadable(resolvedModelPath)) { + continue; + } + + try { + ResolvedModel resolvedModel = JacksonMappers.yamlObjectReader().readValue(resolvedModelPath.toFile(), + ResolvedModel.class); + + if (resolvedModel.getConfigRoots() == null || resolvedModel.getConfigRoots().isEmpty()) { + continue; + } + + for (ConfigRoot configRoot : resolvedModel.getConfigRoots()) { + if (configRoot.getOverriddenDocFileName() != null) { + ConfigRoot existingConfigRootInSpecificFile = configRootsInSpecificFile + .get(configRoot.getOverriddenDocFileName()); + + if (existingConfigRootInSpecificFile == null) { + configRootsInSpecificFile.put(configRoot.getOverriddenDocFileName(), configRoot); + } else { + if (!existingConfigRootInSpecificFile.getExtension().equals(configRoot.getExtension()) + || !existingConfigRootInSpecificFile.getPrefix().equals(configRoot.getPrefix())) { + throw new IllegalStateException( + "Two config roots with different extensions or prefixes cannot be merged in the same specific config file: " + + configRoot.getOverriddenDocFileName()); + } + + existingConfigRootInSpecificFile.merge(configRoot); + } + + continue; + } + + Map extensionConfigRoots = configRoots.computeIfAbsent(configRoot.getExtension(), + e -> new HashMap<>()); + + ConfigRoot existingConfigRoot = extensionConfigRoots.get(configRoot.getTopLevelPrefix()); + + if (existingConfigRoot == null) { + extensionConfigRoots.put(configRoot.getTopLevelPrefix(), configRoot); + } else { + existingConfigRoot.merge(configRoot); + } + } + } catch (IOException e) { + throw new IllegalStateException("Unable to parse: " + resolvedModelPath, e); + } + } + + configRoots = retainBestExtensionKey(configRoots); + + for (Entry> extensionConfigRootsEntry : configRoots.entrySet()) { + List extensionGeneratedConfigSections = generatedConfigSections + .computeIfAbsent(extensionConfigRootsEntry.getKey(), e -> new ArrayList<>()); + + for (ConfigRoot configRoot : extensionConfigRootsEntry.getValue().values()) { + collectGeneratedConfigSections(extensionGeneratedConfigSections, configRoot); + } + } + + return new MergedModel(configRoots, configRootsInSpecificFile, generatedConfigSections); + } + + private static Map> retainBestExtensionKey( + Map> configRoots) { + return configRoots.entrySet().stream().collect(Collectors.toMap(e -> { + Extension extension = e.getKey(); + + for (ConfigRoot configRoot : e.getValue().values()) { + if (configRoot.getExtension().nameSource().isBetterThan(extension.nameSource())) { + extension = configRoot.getExtension(); + } + if (NameSource.EXTENSION_METADATA.equals(extension.nameSource())) { + // we won't find any better + break; + } + } + + return extension; + }, e -> e.getValue(), (k1, k2) -> k1, TreeMap::new)); + } + + private static void collectGeneratedConfigSections(List extensionGeneratedConfigSections, + ConfigItemCollection configItemCollection) { + for (AbstractConfigItem configItem : configItemCollection.getItems()) { + if (!configItem.isSection()) { + continue; + } + + ConfigSection configSection = (ConfigSection) configItem; + if (configSection.isGenerated()) { + extensionGeneratedConfigSections.add(configSection); + } + + collectGeneratedConfigSections(extensionGeneratedConfigSections, configSection); + } + } +} diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/JacksonMappers.java b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/JacksonMappers.java new file mode 100644 index 0000000000000..525da1c1788b6 --- /dev/null +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/util/JacksonMappers.java @@ -0,0 +1,33 @@ +package io.quarkus.annotation.processor.documentation.config.util; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; + +public final class JacksonMappers { + + private static final ObjectWriter JSON_OBJECT_WRITER = new ObjectMapper() + .setSerializationInclusion(JsonInclude.Include.NON_DEFAULT).writerWithDefaultPrettyPrinter(); + private static final ObjectWriter YAML_OBJECT_WRITER = new ObjectMapper(new YAMLFactory()) + .setSerializationInclusion(JsonInclude.Include.NON_DEFAULT).writer(); + private static final ObjectReader YAML_OBJECT_READER = new ObjectMapper(new YAMLFactory()) + .registerModule(new ParameterNamesModule()).reader(); + + private JacksonMappers() { + } + + public static ObjectWriter jsonObjectWriter() { + return JSON_OBJECT_WRITER; + } + + public static ObjectWriter yamlObjectWriter() { + return YAML_OBJECT_WRITER; + } + + public static ObjectReader yamlObjectReader() { + return YAML_OBJECT_READER; + } +} diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/util/FilerUtil.java b/core/processor/src/main/java/io/quarkus/annotation/processor/util/FilerUtil.java index 8875b96c7abb1..fa8765cdc7798 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/util/FilerUtil.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/util/FilerUtil.java @@ -21,28 +21,11 @@ import javax.tools.FileObject; import javax.tools.StandardLocation; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; - +import io.quarkus.annotation.processor.documentation.config.util.JacksonMappers; import io.quarkus.bootstrap.util.PropertyUtils; public class FilerUtil { - private static final ObjectWriter JSON_OBJECT_WRITER; - private static final ObjectMapper YAML_OBJECT_MAPPER = new ObjectMapper(new YAMLFactory()); - - static { - } - - static { - ObjectMapper jsonObjectMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_DEFAULT); - JSON_OBJECT_WRITER = jsonObjectMapper.writerWithDefaultPrettyPrinter(); - - YAML_OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT); - } - private final ProcessingEnvironment processingEnv; FilerUtil(ProcessingEnvironment processingEnv) { @@ -109,7 +92,7 @@ public void writeJson(String filePath, Object value) { filePath.toString()); try (OutputStream os = jsonResource.openOutputStream()) { - JSON_OBJECT_WRITER.writeValue(os, value); + JacksonMappers.jsonObjectWriter().writeValue(os, value); } } catch (IOException e) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to write " + filePath + ": " + e); @@ -126,7 +109,7 @@ public Path writeModel(Path filePath, Object value) { Path yamlModelPath = getTargetPath().resolve(filePath); try { Files.createDirectories(yamlModelPath.getParent()); - YAML_OBJECT_MAPPER.writeValue(yamlModelPath.toFile(), value); + JacksonMappers.yamlObjectWriter().writeValue(yamlModelPath.toFile(), value); return yamlModelPath; } catch (IOException e) { @@ -171,7 +154,7 @@ public Optional> getExtensionMetadata() { try (InputStream is = fileObject.openInputStream()) { String yamlMetadata = new String(is.readAllBytes(), StandardCharsets.UTF_8); - Map extensionMetadata = YAML_OBJECT_MAPPER.readValue(yamlMetadata, Map.class); + Map extensionMetadata = JacksonMappers.yamlObjectReader().readValue(yamlMetadata, Map.class); return Optional.of(extensionMetadata); } diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java index 7dd76631b4cca..7baf92dc734f3 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java @@ -5,6 +5,7 @@ import java.util.Optional; import java.util.stream.Collectors; +import io.quarkus.annotation.processor.documentation.config.merger.JavadocRepository; import io.quarkus.annotation.processor.documentation.config.model.ConfigProperty; import io.quarkus.annotation.processor.documentation.config.model.ConfigSection; import io.quarkus.annotation.processor.documentation.config.model.Extension; diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java index 65b7295f4a9f2..06ec52ba4afe1 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java @@ -13,12 +13,9 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.TreeMap; -import java.util.stream.Collectors; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; @@ -28,21 +25,15 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; - -import io.quarkus.annotation.processor.Outputs; -import io.quarkus.annotation.processor.documentation.config.model.AbstractConfigItem; +import io.quarkus.annotation.processor.documentation.config.merger.JavadocMerger; +import io.quarkus.annotation.processor.documentation.config.merger.JavadocRepository; +import io.quarkus.annotation.processor.documentation.config.merger.MergedModel; +import io.quarkus.annotation.processor.documentation.config.merger.ModelMerger; import io.quarkus.annotation.processor.documentation.config.model.ConfigItemCollection; import io.quarkus.annotation.processor.documentation.config.model.ConfigProperty; import io.quarkus.annotation.processor.documentation.config.model.ConfigRoot; import io.quarkus.annotation.processor.documentation.config.model.ConfigSection; import io.quarkus.annotation.processor.documentation.config.model.Extension; -import io.quarkus.annotation.processor.documentation.config.model.Extension.NameSource; -import io.quarkus.annotation.processor.documentation.config.model.JavadocElements; -import io.quarkus.annotation.processor.documentation.config.model.JavadocElements.JavadocElement; -import io.quarkus.annotation.processor.documentation.config.model.ResolvedModel; import io.quarkus.qute.Engine; import io.quarkus.qute.ReflectionValueResolver; import io.quarkus.qute.UserTagSectionHelper; @@ -51,8 +42,6 @@ @Mojo(name = "generate-asciidoc", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, threadSafe = true) public class GenerateAsciidocMojo extends AbstractMojo { - private static final ObjectMapper YAML_OBJECT_MAPPER = new ObjectMapper(new YAMLFactory()) - .registerModule(new ParameterNamesModule()); private static final String TARGET = "target"; private static final String ADOC_SUFFIX = ".adoc"; @@ -89,8 +78,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { List targetDirectories = findTargetDirectories(resolvedScanDirectory); - JavadocRepository javadocRepository = findJavadocElements(targetDirectories); - MergedModel mergedModel = mergeModel(targetDirectories); + JavadocRepository javadocRepository = JavadocMerger.mergeJavadocElements(targetDirectories); + MergedModel mergedModel = ModelMerger.mergeModel(targetDirectories); AsciidocFormatter asciidocFormatter = new AsciidocFormatter(javadocRepository); Engine quteEngine = initializeQuteEngine(asciidocFormatter); @@ -224,138 +213,6 @@ private static void initTargetDirectory(Path resolvedTargetDirectory) throws Moj } } - private static JavadocRepository findJavadocElements(List targetDirectories) throws MojoExecutionException { - Map javadocElementsMap = new HashMap<>(); - - for (Path targetDirectory : targetDirectories) { - Path javadocPath = targetDirectory.resolve(Outputs.QUARKUS_CONFIG_DOC_JAVADOC); - if (!Files.isReadable(javadocPath)) { - continue; - } - - try { - JavadocElements javadocElements = YAML_OBJECT_MAPPER.readValue(javadocPath.toFile(), JavadocElements.class); - - if (javadocElements.elements() == null || javadocElements.elements().isEmpty()) { - continue; - } - - javadocElementsMap.putAll(javadocElements.elements()); - } catch (IOException e) { - throw new MojoExecutionException("Unable to parse: " + javadocPath, e); - } - } - - return new JavadocRepository(javadocElementsMap); - } - - private static MergedModel mergeModel(List targetDirectories) throws MojoExecutionException { - // keyed on extension and then top level prefix - Map> configRoots = new HashMap<>(); - // keyed on file name - Map configRootsInSpecificFile = new TreeMap<>(); - // keyed on extension - Map> generatedConfigSections = new TreeMap<>(); - - for (Path targetDirectory : targetDirectories) { - Path javadocPath = targetDirectory.resolve(Outputs.QUARKUS_CONFIG_DOC_MODEL); - if (!Files.isReadable(javadocPath)) { - continue; - } - - try { - ResolvedModel resolvedModel = YAML_OBJECT_MAPPER.readValue(javadocPath.toFile(), ResolvedModel.class); - - if (resolvedModel.getConfigRoots() == null || resolvedModel.getConfigRoots().isEmpty()) { - continue; - } - - for (ConfigRoot configRoot : resolvedModel.getConfigRoots()) { - if (configRoot.getOverriddenDocFileName() != null) { - ConfigRoot existingConfigRootInSpecificFile = configRootsInSpecificFile - .get(configRoot.getOverriddenDocFileName()); - - if (existingConfigRootInSpecificFile == null) { - configRootsInSpecificFile.put(configRoot.getOverriddenDocFileName(), configRoot); - } else { - if (!existingConfigRootInSpecificFile.getExtension().equals(configRoot.getExtension()) - || !existingConfigRootInSpecificFile.getPrefix().equals(configRoot.getPrefix())) { - throw new MojoExecutionException( - "Two config roots with different extensions or prefixes cannot be merged in the same specific config file: " - + configRoot.getOverriddenDocFileName()); - } - - existingConfigRootInSpecificFile.merge(configRoot); - } - - continue; - } - - Map extensionConfigRoots = configRoots.computeIfAbsent(configRoot.getExtension(), - e -> new HashMap<>()); - - ConfigRoot existingConfigRoot = extensionConfigRoots.get(configRoot.getTopLevelPrefix()); - - if (existingConfigRoot == null) { - extensionConfigRoots.put(configRoot.getTopLevelPrefix(), configRoot); - } else { - existingConfigRoot.merge(configRoot); - } - } - } catch (IOException e) { - throw new MojoExecutionException("Unable to parse: " + javadocPath, e); - } - } - - configRoots = retainBestExtensionKey(configRoots); - - for (Entry> extensionConfigRootsEntry : configRoots.entrySet()) { - List extensionGeneratedConfigSections = generatedConfigSections - .computeIfAbsent(extensionConfigRootsEntry.getKey(), e -> new ArrayList<>()); - - for (ConfigRoot configRoot : extensionConfigRootsEntry.getValue().values()) { - collectGeneratedConfigSections(extensionGeneratedConfigSections, configRoot); - } - } - - return new MergedModel(configRoots, configRootsInSpecificFile, generatedConfigSections); - } - - private static Map> retainBestExtensionKey( - Map> configRoots) { - return configRoots.entrySet().stream().collect(Collectors.toMap(e -> { - Extension extension = e.getKey(); - - for (ConfigRoot configRoot : e.getValue().values()) { - if (configRoot.getExtension().nameSource().isBetterThan(extension.nameSource())) { - extension = configRoot.getExtension(); - } - if (NameSource.EXTENSION_METADATA.equals(extension.nameSource())) { - // we won't find any better - break; - } - } - - return extension; - }, e -> e.getValue(), (k1, k2) -> k1, TreeMap::new)); - } - - private static void collectGeneratedConfigSections(List extensionGeneratedConfigSections, - ConfigItemCollection configItemCollection) { - for (AbstractConfigItem configItem : configItemCollection.getItems()) { - if (!configItem.isSection()) { - continue; - } - - ConfigSection configSection = (ConfigSection) configItem; - if (configSection.isGenerated()) { - extensionGeneratedConfigSections.add(configSection); - } - - collectGeneratedConfigSections(extensionGeneratedConfigSections, configSection); - } - } - private static List findTargetDirectories(Path scanDirectory) throws MojoExecutionException { try { List targets = new ArrayList<>(); From 2b8bd8356989e4349c9570a006ae2f1506834920 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Sat, 10 Aug 2024 18:09:57 +0200 Subject: [PATCH 10/10] Config Doc - Disable enum tooltips by default There is a good chance we don't have them working in the Quarkiverse doc so let's be cautious. --- .../io/quarkus/maven/config/doc/AsciidocFormatter.java | 8 +++++--- .../io/quarkus/maven/config/doc/GenerateAsciidocMojo.java | 5 ++++- docs/pom.xml | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java index 7baf92dc734f3..211e27875de24 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/AsciidocFormatter.java @@ -18,9 +18,11 @@ final class AsciidocFormatter { private static final String MORE_INFO_ABOUT_TYPE_FORMAT = "link:#%s[icon:question-circle[title=More information about the %s format]]"; private final JavadocRepository javadocRepository; + private final boolean enableEnumTooltips; - AsciidocFormatter(JavadocRepository javadocRepository) { + AsciidocFormatter(JavadocRepository javadocRepository, boolean enableEnumTooltips) { this.javadocRepository = javadocRepository; + this.enableEnumTooltips = enableEnumTooltips; } String formatDescription(ConfigProperty configProperty) { @@ -42,7 +44,7 @@ String formatDescription(ConfigProperty configProperty) { String formatTypeDescription(ConfigProperty configProperty) { String typeContent = ""; - if (configProperty.isEnum()) { + if (configProperty.isEnum() && enableEnumTooltips) { typeContent = configProperty.getEnumAcceptedValues().values().entrySet().stream() .map(e -> { Optional javadocElement = javadocRepository.getElement(configProperty.getType(), @@ -83,7 +85,7 @@ String formatDefaultValue(ConfigProperty configProperty) { return null; } - if (configProperty.isEnum()) { + if (configProperty.isEnum() && enableEnumTooltips) { Optional enumConstant = configProperty.getEnumAcceptedValues().values().entrySet().stream() .filter(e -> e.getValue().configValue().equals(defaultValue)) .map(e -> e.getKey()) diff --git a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java index 06ec52ba4afe1..bd9c3c74aadba 100644 --- a/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java +++ b/devtools/config-doc-maven-plugin/src/main/java/io/quarkus/maven/config/doc/GenerateAsciidocMojo.java @@ -61,6 +61,9 @@ public class GenerateAsciidocMojo extends AbstractMojo { @Parameter(defaultValue = "false") private boolean generateAllConfig; + @Parameter(defaultValue = "false") + private boolean enableEnumTooltips; + @Parameter(defaultValue = "false") private boolean skip; @@ -81,7 +84,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { JavadocRepository javadocRepository = JavadocMerger.mergeJavadocElements(targetDirectories); MergedModel mergedModel = ModelMerger.mergeModel(targetDirectories); - AsciidocFormatter asciidocFormatter = new AsciidocFormatter(javadocRepository); + AsciidocFormatter asciidocFormatter = new AsciidocFormatter(javadocRepository, enableEnumTooltips); Engine quteEngine = initializeQuteEngine(asciidocFormatter); // we generate a file per extension + top level prefix diff --git a/docs/pom.xml b/docs/pom.xml index da9bc9f05edeb..5201246f04796 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -3189,6 +3189,7 @@ ${skipDocs} true + true