Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ This document is intended for Spotless developers.
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
### Added
- Add support for AsciiDoc formatting via `adocfmt`. ([#2960](https://github.com/diffplug/spotless/pull/2960))
### Fixed
- Support `ktfmt` 0.63 and use its new builder API for formatting options to better avoid future breaking changes.
### Changes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.asciidoc;

import java.io.Serial;
import java.io.Serializable;
import java.util.Objects;

public class AdocfmtConfig implements Serializable {
@Serial
private static final long serialVersionUID = 1L;

public boolean normalizeSetextHeadings = false;
public boolean collapseConsecutiveBlankLines = true;
public boolean oneSentencePerLine = false;
public boolean normalizeBlockDelimiters = true;
public boolean removeTrailingHeaderEqualsSign = true;
public boolean titleCase = false;
public boolean removeTrailingWhitespace = true;
public boolean normalizeListBullets = false;
public boolean normalizeOrderedListMarkers = false;
public boolean ensureHeadingBlankLines = true;
public boolean ensureSourceDelimiters = false;

public AdocfmtConfig() {}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof AdocfmtConfig other))
return false;
return normalizeSetextHeadings == other.normalizeSetextHeadings
&& collapseConsecutiveBlankLines == other.collapseConsecutiveBlankLines
&& oneSentencePerLine == other.oneSentencePerLine
&& normalizeBlockDelimiters == other.normalizeBlockDelimiters
&& removeTrailingHeaderEqualsSign == other.removeTrailingHeaderEqualsSign
&& titleCase == other.titleCase
&& removeTrailingWhitespace == other.removeTrailingWhitespace
&& normalizeListBullets == other.normalizeListBullets
&& normalizeOrderedListMarkers == other.normalizeOrderedListMarkers
&& ensureHeadingBlankLines == other.ensureHeadingBlankLines
&& ensureSourceDelimiters == other.ensureSourceDelimiters;
}

@Override
public int hashCode() {
return Objects.hash(normalizeSetextHeadings, collapseConsecutiveBlankLines, oneSentencePerLine,
normalizeBlockDelimiters, removeTrailingHeaderEqualsSign, titleCase, removeTrailingWhitespace,
normalizeListBullets, normalizeOrderedListMarkers, ensureHeadingBlankLines, ensureSourceDelimiters);
}
}
117 changes: 117 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/asciidoc/AdocfmtStep.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright 2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.spotless.asciidoc;

import java.io.Serial;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;

import com.diffplug.spotless.FormatterFunc;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.JarState;
import com.diffplug.spotless.Provisioner;
import com.diffplug.spotless.ThrowingEx;

public final class AdocfmtStep implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private static final String DEFAULT_VERSION = "0.2.0";
private static final String NAME = "adocfmt";
private static final String MAVEN_COORDINATE = "org.drjekyll:adocfmt:";

private final String version;
private final AdocfmtConfig config;
private final JarState.Promised jarState;

private AdocfmtStep(String version, AdocfmtConfig config, JarState.Promised jarState) {
this.version = version;
this.config = config;
this.jarState = jarState;
}

public static FormatterStep create(Provisioner provisioner) {
return create(defaultVersion(), provisioner);
}

public static FormatterStep create(String version, Provisioner provisioner) {
return create(version, provisioner, new AdocfmtConfig());
}

public static FormatterStep create(String version, Provisioner provisioner, AdocfmtConfig config) {
Objects.requireNonNull(version, "version");
Objects.requireNonNull(provisioner, "provisioner");
Objects.requireNonNull(config, "config");
return FormatterStep.create(NAME,
new AdocfmtStep(version, config, JarState.promise(() -> JarState.from(MAVEN_COORDINATE + version, provisioner))),
AdocfmtStep::equalityState,
State::createFormat);
}

public static String defaultVersion() {
return DEFAULT_VERSION;
}

private State equalityState() {
return new State(version, config, jarState.get());
}

private static final class State implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private final String version;
private final AdocfmtConfig config;
private final JarState jarState;

State(String version, AdocfmtConfig config, JarState jarState) {
this.version = version;
this.config = config;
this.jarState = jarState;
}

FormatterFunc createFormat() throws Exception {
final ClassLoader classLoader = jarState.getClassLoader();
final Class<?> formatterClass = classLoader.loadClass("org.drjekyll.adocfmt.AsciidocFormatter");
final Class<?> configClass = classLoader.loadClass("org.drjekyll.adocfmt.AsciidocFormatterConfig");
final Method builderMethod = configClass.getMethod("builder");
Object builder = builderMethod.invoke(null);
final Class<?> builderClass = builder.getClass();
builder = builderClass.getMethod("normalizeSetextHeadings", boolean.class).invoke(builder, config.normalizeSetextHeadings);
builder = builderClass.getMethod("collapseConsecutiveBlankLines", boolean.class).invoke(builder, config.collapseConsecutiveBlankLines);
builder = builderClass.getMethod("oneSentencePerLine", boolean.class).invoke(builder, config.oneSentencePerLine);
builder = builderClass.getMethod("normalizeBlockDelimiters", boolean.class).invoke(builder, config.normalizeBlockDelimiters);
builder = builderClass.getMethod("removeTrailingHeaderEqualsSign", boolean.class).invoke(builder, config.removeTrailingHeaderEqualsSign);
builder = builderClass.getMethod("titleCase", boolean.class).invoke(builder, config.titleCase);
builder = builderClass.getMethod("removeTrailingWhitespace", boolean.class).invoke(builder, config.removeTrailingWhitespace);
builder = builderClass.getMethod("normalizeListBullets", boolean.class).invoke(builder, config.normalizeListBullets);
builder = builderClass.getMethod("normalizeOrderedListMarkers", boolean.class).invoke(builder, config.normalizeOrderedListMarkers);
builder = builderClass.getMethod("ensureHeadingBlankLines", boolean.class).invoke(builder, config.ensureHeadingBlankLines);
builder = builderClass.getMethod("ensureSourceDelimiters", boolean.class).invoke(builder, config.ensureSourceDelimiters);
final Object adocfmtConfig = builderClass.getMethod("build").invoke(builder);

final Object formatter = formatterClass.getConstructor(configClass).newInstance(adocfmtConfig);
final Method formatMethod = formatterClass.getMethod("format", String.class);
return input -> {
try {
return (String) formatMethod.invoke(formatter, input);
} catch (InvocationTargetException e) {
throw ThrowingEx.unwrapCause(e);
}
};
}
}
}
2 changes: 2 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (

## [Unreleased]

### Added
- Add support for AsciiDoc formatting via `adocfmt`. ([#2960](https://github.com/diffplug/spotless/pull/2960))
### Fixed
- Prevent build caches from interfering when executing under the `-PspotlessIdeHook` mode. ([#2365](https://github.com/diffplug/spotless/issues/2365))
### Changes
Expand Down
36 changes: 36 additions & 0 deletions plugin-gradle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Spotless supports all of Gradle's built-in performance features (incremental bui
- [Groovy](#groovy) ([eclipse groovy](#eclipse-groovy))
- [Kotlin](#kotlin) ([ktfmt](#ktfmt), [ktlint](#ktlint), [diktat](#diktat), [tabletest-formatter](#tabletest-formatter-1), [prettier](#prettier))
- [Scala](#scala) ([scalafmt](#scalafmt))
- [Asciidoc](#asciidoc) ([adocfmt](#adocfmt))
- [C/C++](#cc) ([clang-format](#clang-format), [eclipse cdt](#eclipse-cdt))
- [Protobuf](#protobuf) ([buf](#buf), [clang-format](#clang-format))
- [Python](#python) ([black](#black))
Expand Down Expand Up @@ -830,6 +831,41 @@ spotless {
antlr4formatter('1.2.1') // version is optional
```

## Asciidoc

`com.diffplug.gradle.spotless.AsciidocExtension` [javadoc](https://javadoc.io/doc/com.diffplug.spotless/spotless-plugin-gradle/8.6.0/com/diffplug/gradle/spotless/AsciidocExtension.html), [code](https://github.com/diffplug/spotless/blob/main/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/AsciidocExtension.java)

```gradle
spotless {
asciidoc {
target 'src/docs/**/*.adoc' // you have to set the target manually

adocfmt() // has its own section below
}
}
```

### adocfmt

[homepage](https://github.com/dheid/adocfmt). [available versions](https://search.maven.org/artifact/org.drjekyll/adocfmt).

```gradle
adocfmt('0.2.0') // version is optional
.normalizeSetextHeadings(false) // convert === underlines to ATX == (default: false)
.collapseConsecutiveBlankLines(true) // max 1 blank line (default: true)
.oneSentencePerLine(false) // each sentence on its own line (default: false)
.normalizeBlockDelimiters(true) // exactly 4 characters (e.g. ----) (default: true)
.removeTrailingHeaderEqualsSign(true) // == Title == -> == Title (default: true)
.titleCase(false) // Title Case headings (default: false)
.removeTrailingWhitespace(true) // trim end of line (default: true)
.normalizeListBullets(false) // - -> * (default: false)
.normalizeOrderedListMarkers(false) // 1. -> . (default: false)
.ensureHeadingBlankLines(true) // blank line before/after headings (default: true)
.ensureSourceDelimiters(false) // wrap [source] in ---- (default: false)
```

Options default to `true` or `false` as shown above. This formatter is designed to help you maintain a clean, consistent AsciiDoc structure. Surface-altering options like `oneSentencePerLine` and `normalizeSetextHeadings` default to `false` so that zero-config formatting is conservative; opt in explicitly if you want those transforms.

<a name="sql-dbeaver"></a>
<a name="applying-dbeaver-to-sql-scripts"></a>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.gradle.spotless;

import javax.inject.Inject;

import com.diffplug.spotless.asciidoc.AdocfmtConfig;
import com.diffplug.spotless.asciidoc.AdocfmtStep;

public class AsciidocExtension extends FormatExtension {
static final String NAME = "asciidoc";

@Inject
public AsciidocExtension(SpotlessExtension spotless) {
super(spotless);
}

public AdocfmtFormatterConfig adocfmt() {
return adocfmt(AdocfmtStep.defaultVersion());
}

public AdocfmtFormatterConfig adocfmt(String version) {
return new AdocfmtFormatterConfig(version);
}

public class AdocfmtFormatterConfig {
private final String version;
private final AdocfmtConfig config = new AdocfmtConfig();

AdocfmtFormatterConfig(String version) {
this.version = version;
addStep(AdocfmtStep.create(version, provisioner(), config));
}

public AdocfmtFormatterConfig normalizeSetextHeadings(boolean normalizeSetextHeadings) {
config.normalizeSetextHeadings = normalizeSetextHeadings;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig collapseConsecutiveBlankLines(boolean collapseConsecutiveBlankLines) {
config.collapseConsecutiveBlankLines = collapseConsecutiveBlankLines;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig oneSentencePerLine(boolean oneSentencePerLine) {
config.oneSentencePerLine = oneSentencePerLine;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig normalizeBlockDelimiters(boolean normalizeBlockDelimiters) {
config.normalizeBlockDelimiters = normalizeBlockDelimiters;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig removeTrailingHeaderEqualsSign(boolean removeTrailingHeaderEqualsSign) {
config.removeTrailingHeaderEqualsSign = removeTrailingHeaderEqualsSign;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig titleCase(boolean titleCase) {
config.titleCase = titleCase;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig removeTrailingWhitespace(boolean removeTrailingWhitespace) {
config.removeTrailingWhitespace = removeTrailingWhitespace;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig normalizeListBullets(boolean normalizeListBullets) {
config.normalizeListBullets = normalizeListBullets;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig normalizeOrderedListMarkers(boolean normalizeOrderedListMarkers) {
config.normalizeOrderedListMarkers = normalizeOrderedListMarkers;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig ensureHeadingBlankLines(boolean ensureHeadingBlankLines) {
config.ensureHeadingBlankLines = ensureHeadingBlankLines;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}

public AdocfmtFormatterConfig ensureSourceDelimiters(boolean ensureSourceDelimiters) {
config.ensureSourceDelimiters = ensureSourceDelimiters;
replaceStep(AdocfmtStep.create(version, provisioner(), config));
return this;
}
}

@Override
protected void setupTask(SpotlessTask task) {
if (target == null) {
throw noDefaultTargetException();
}
super.setupTask(task);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ public void gherkin(Action<GherkinExtension> closure) {
format(GherkinExtension.NAME, GherkinExtension.class, closure);
}

/** Configures the special asciidoc-specific extension. */
public void asciidoc(Action<AsciidocExtension> closure) {
requireNonNull(closure);
format(AsciidocExtension.NAME, AsciidocExtension.class, closure);
}

public void toml(Action<TomlExtension> closure) {
requireNonNull(closure);
format(TomlExtension.NAME, TomlExtension.class, closure);
Expand Down
Loading
Loading