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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 Java formatting via [`prince-of-space`](https://github.com/agustafson/prince-of-space). ([#2991](https://github.com/diffplug/spotless/pull/2991))

## [4.8.0] - 2026-06-29
### Added
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ gson = "com.google.code.gson:gson:2.14.0"
javaparser-symbol-solver-core = "com.github.javaparser:javaparser-symbol-solver-core:3.27.1"
ktfmt = "com.facebook:ktfmt:0.63"
palantir-java-format = "com.palantir.javaformat:palantir-java-format:1.1.0"
prince-of-space-core = "io.github.agustafson.princeofspace:prince-of-space-core:2.2.0"
scalafmt-core = "org.scalameta:scalafmt-core_2.13:3.8.1"
sortpom-sorter = "com.github.ekryd.sortpom:sortpom-sorter:4.0.0"
tabletest-formatter-core = "org.tabletest:tabletest-formatter-core:1.1.1"
Expand Down
3 changes: 3 additions & 0 deletions lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def NEEDS_GLUE = [
'ktfmt',
'ktlint',
'palantirJavaFormat',
'princeOfSpace',
'scalafmt',
'sortPom',
'tableTestFormatter',
Expand Down Expand Up @@ -117,6 +118,8 @@ dependencies {
compatKtLint1Dot0Dot0CompileAndTestOnly libs.slf4j.api
// palantirJavaFormat
palantirJavaFormatCompileOnly libs.palantir.java.format // this version needs to stay compilable against Java 8 for CI Job testNpm
// princeOfSpace
princeOfSpaceCompileOnly libs.prince.of.space.core
// scalafmt
scalafmtCompileOnly libs.scalafmt.core
// sortPom
Expand Down
159 changes: 159 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/java/PrinceOfSpaceStep.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* 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.java;

import java.io.Serial;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.Objects;

import javax.annotation.Nullable;

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

/** Wraps up <a href="https://github.com/agustafson/prince-of-space">prince-of-space</a> as a FormatterStep. */
public final class PrinceOfSpaceStep implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private static final String NAME = "prince-of-space";
public static final String MAVEN_COORDINATE = "io.github.agustafson.princeofspace:prince-of-space-core:";
public static final String DEFAULT_VERSION = "2.2.0";

/** The jar that contains the formatter. */
private final JarState.Promised jarState;
/** Version of the formatter jar. */
private final String formatterVersion;
@Nullable private final Options options;

private PrinceOfSpaceStep(JarState.Promised jarState, String formatterVersion, @Nullable Options options) {
this.jarState = jarState;
this.formatterVersion = formatterVersion;
this.options = options;
}

/** Creates a step which formats Java source with prince-of-space's default options. */
public static FormatterStep create(Provisioner provisioner) {
return create(defaultVersion(), provisioner);
}

/** Creates a step which formats Java source with prince-of-space's default options. */
public static FormatterStep create(String version, Provisioner provisioner) {
return create(version, provisioner, null);
}

/** Creates a step which formats Java source with the given options. */
public static FormatterStep create(String version, Provisioner provisioner, @Nullable Options options) {
Objects.requireNonNull(version, "version");
Objects.requireNonNull(provisioner, "provisioner");
return FormatterStep.create(NAME,
new PrinceOfSpaceStep(JarState.promise(() -> JarState.from(MAVEN_COORDINATE + version, provisioner)), version, options),
PrinceOfSpaceStep::equalityState,
State::createFormat);
}

/** Get default formatter version */
public static String defaultVersion() {
return DEFAULT_VERSION;
}

private State equalityState() {
return new State(jarState.get(), formatterVersion, options);
}

/** Mutable bag of the optional prince-of-space {@code FormatterConfig} knobs; unset (null) fields keep prince-of-space's own defaults. */
public static class Options implements Serializable {
@Serial
private static final long serialVersionUID = 1L;

@Nullable private String indentStyle;
@Nullable private Integer indentSize;
@Nullable private Integer lineLength;
@Nullable private String wrapStyle;
@Nullable private Boolean closingParenOnNewLine;
@Nullable private Boolean trailingCommas;
@Nullable private Integer javaLanguageLevel;

public Options() {}

public void setIndentStyle(String indentStyle) {
this.indentStyle = indentStyle;
}

public void setIndentSize(int indentSize) {
this.indentSize = indentSize;
}

public void setLineLength(int lineLength) {
this.lineLength = lineLength;
}

public void setWrapStyle(String wrapStyle) {
this.wrapStyle = wrapStyle;
}

public void setClosingParenOnNewLine(boolean closingParenOnNewLine) {
this.closingParenOnNewLine = closingParenOnNewLine;
}

public void setTrailingCommas(boolean trailingCommas) {
this.trailingCommas = trailingCommas;
}

public void setJavaLanguageLevel(int javaLanguageLevel) {
this.javaLanguageLevel = javaLanguageLevel;
}
}

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

private final JarState jarState;
private final String formatterVersion;
@Nullable private final Options options;

State(JarState jarState, String formatterVersion, @Nullable Options options) {
if (Jvm.version() < 17) {
throw new IllegalStateException("prince-of-space requires a JDK 17+ host runtime, this JVM is " + Jvm.version());
}
this.jarState = jarState;
this.formatterVersion = formatterVersion;
this.options = options;
}

FormatterFunc createFormat() throws Exception {
final ClassLoader classLoader = jarState.getClassLoader();
final Class<?> formatterFunc = classLoader.loadClass("com.diffplug.spotless.glue.pos.PrinceOfSpaceFormatterFunc");
final Constructor<?> constructor = formatterFunc.getConstructor(
String.class, Integer.class, Integer.class, String.class, Boolean.class, Boolean.class, Integer.class);
if (options == null) {
return (FormatterFunc) constructor.newInstance(null, null, null, null, null, null, null);
}
return (FormatterFunc) constructor.newInstance(
options.indentStyle,
options.indentSize,
options.lineLength,
options.wrapStyle,
options.closingParenOnNewLine,
options.trailingCommas,
options.javaLanguageLevel);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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.glue.pos;

import java.io.File;

import io.princeofspace.Formatter;
import io.princeofspace.model.FormatterConfig;
import io.princeofspace.model.IndentStyle;
import io.princeofspace.model.JavaLanguageLevel;
import io.princeofspace.model.WrapStyle;

import com.diffplug.spotless.FormatterFunc;

public class PrinceOfSpaceFormatterFunc implements FormatterFunc.NeedsFile {

private final Formatter formatter;

public PrinceOfSpaceFormatterFunc(String indentStyle, Integer indentSize, Integer lineLength, String wrapStyle,
Boolean closingParenOnNewLine, Boolean trailingCommas, Integer javaLanguageLevel) {
FormatterConfig.Builder builder = FormatterConfig.builder();
if (indentStyle != null) {
builder.indentStyle(IndentStyle.valueOf(indentStyle));
}
if (indentSize != null) {
builder.indentSize(indentSize);
}
if (lineLength != null) {
builder.lineLength(lineLength);
}
if (wrapStyle != null) {
builder.wrapStyle(WrapStyle.valueOf(wrapStyle));
}
if (closingParenOnNewLine != null) {
builder.closingParenOnNewLine(closingParenOnNewLine);
}
if (trailingCommas != null) {
builder.trailingCommas(trailingCommas);
}
if (javaLanguageLevel != null) {
builder.javaLanguageLevel(JavaLanguageLevel.of(javaLanguageLevel));
}
this.formatter = new Formatter(builder.build());
}

@Override
public String applyWithFile(String unix, File file) throws Exception {
return formatter.format(unix, file.toPath());
}

@Override
public String toString() {
return "PrinceOfSpaceFormatterFunc{formatter=" + formatter + '}';
}
}
2 changes: 2 additions & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).

## [Unreleased]
### Added
- Add support for Java formatting via [`prince-of-space`](https://github.com/agustafson/prince-of-space) with the new `princeOfSpace()` step. ([#2991](https://github.com/diffplug/spotless/pull/2991))

## [8.8.0] - 2026-06-29
### Added
Expand Down
20 changes: 19 additions & 1 deletion plugin-gradle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Spotless supports all of Gradle's built-in performance features (incremental bui
- [Git hook (optional)](#git-hook)
- [Linting](#linting)
- **Languages**
- [Java](#java) ([google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [clang-format](#clang-format), [prettier](#prettier), [palantir-java-format](#palantir-java-format), [formatAnnotations](#formatAnnotations), [cleanthat](#cleanthat), [tabletest-formatter](#tabletest-formatter), [IntelliJ IDEA](#intellij-idea))
- [Java](#java) ([google-java-format](#google-java-format), [eclipse jdt](#eclipse-jdt), [clang-format](#clang-format), [prettier](#prettier), [palantir-java-format](#palantir-java-format), [prince-of-space](#prince-of-space), [formatAnnotations](#formatAnnotations), [cleanthat](#cleanthat), [tabletest-formatter](#tabletest-formatter), [IntelliJ IDEA](#intellij-idea))
- [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))
Expand Down Expand Up @@ -312,6 +312,24 @@ spotless {
palantirJavaFormat('2.39.0').formatJavadoc(true)
```

### prince-of-space

[homepage](https://github.com/agustafson/prince-of-space). Requires a JDK 17+ host runtime.
```gradle
spotless {
java {
princeOfSpace()
// optional: configure any of the formatter's options
princeOfSpace('2.2.0')
.indentStyle('SPACES') // or 'TABS'
.indentSize(4)
.lineLength(120)
.wrapStyle('BALANCED') // or 'WIDE', 'NARROW'
.closingParenOnNewLine(true)
.trailingCommas(false)
.javaLanguageLevel(17)
```

### eclipse jdt

[homepage](https://download.eclipse.org/eclipse/downloads/). See [here](../ECLIPSE_SCREENSHOTS.md) for screenshots that demonstrate how to get and install the config file mentioned below.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.diffplug.spotless.java.GoogleJavaFormatStep;
import com.diffplug.spotless.java.ImportOrderStep;
import com.diffplug.spotless.java.PalantirJavaFormatStep;
import com.diffplug.spotless.java.PrinceOfSpaceStep;
import com.diffplug.spotless.java.RemoveUnusedImportsStep;
import com.diffplug.spotless.java.TableTestFormatterStep;

Expand Down Expand Up @@ -308,6 +309,73 @@ private FormatterStep createStep() {
}
}

/** Uses the <a href="https://github.com/agustafson/prince-of-space">prince-of-space</a> jar to format source code. */
public PrinceOfSpaceConfig princeOfSpace() {
return princeOfSpace(PrinceOfSpaceStep.defaultVersion());
}

/** Uses the given version of <a href="https://github.com/agustafson/prince-of-space">prince-of-space</a> to format source code. */
public PrinceOfSpaceConfig princeOfSpace(String version) {
Objects.requireNonNull(version);
return new PrinceOfSpaceConfig(version);
}

public class PrinceOfSpaceConfig {
final String version;
final PrinceOfSpaceStep.Options options = new PrinceOfSpaceStep.Options();

PrinceOfSpaceConfig(String version) {
this.version = Objects.requireNonNull(version);
addStep(createStep());
}

public PrinceOfSpaceConfig indentStyle(String indentStyle) {
options.setIndentStyle(indentStyle);
replaceStep(createStep());
return this;
}

public PrinceOfSpaceConfig indentSize(int indentSize) {
options.setIndentSize(indentSize);
replaceStep(createStep());
return this;
}

public PrinceOfSpaceConfig lineLength(int lineLength) {
options.setLineLength(lineLength);
replaceStep(createStep());
return this;
}

public PrinceOfSpaceConfig wrapStyle(String wrapStyle) {
options.setWrapStyle(wrapStyle);
replaceStep(createStep());
return this;
}

public PrinceOfSpaceConfig closingParenOnNewLine(boolean closingParenOnNewLine) {
options.setClosingParenOnNewLine(closingParenOnNewLine);
replaceStep(createStep());
return this;
}

public PrinceOfSpaceConfig trailingCommas(boolean trailingCommas) {
options.setTrailingCommas(trailingCommas);
replaceStep(createStep());
return this;
}

public PrinceOfSpaceConfig javaLanguageLevel(int javaLanguageLevel) {
options.setJavaLanguageLevel(javaLanguageLevel);
replaceStep(createStep());
return this;
}

private FormatterStep createStep() {
return PrinceOfSpaceStep.create(version, provisioner(), options);
}
}

public EclipseConfig eclipse() {
return eclipse(EclipseJdtFormatterStep.defaultVersion());
}
Expand Down
2 changes: 2 additions & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
### Added
- Add support for Java formatting via [`prince-of-space`](https://github.com/agustafson/prince-of-space) with the new `<princeOfSpace>` step. ([#2991](https://github.com/diffplug/spotless/pull/2991))

## [3.8.0] - 2026-06-29
### Added
Expand Down
Loading