Skip to content

Commit 47f2080

Browse files
authored
feat: add eclipse wtp formatter (#12)
2 parents d8d662b + ab51763 commit 47f2080

36 files changed

+1149
-12
lines changed

.idea/compiler.xml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added formatter [`eclipse-wtp`](https://github.com/diffplug/spotless/tree/main/plugin-gradle#eclipse-web-tools-platform)
13+
1014
## [0.2.0] - 2025-07-31
1115

1216
### Fixed

README.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ or apply the formatting to the files.
154154
Available formatting steps:
155155
clang-format Runs clang-format
156156
clean-that CleanThat enables automatic refactoring of Java code.
157+
eclipse-wtp Runs Eclipse WTP formatter (4.21.0)
157158
format-annotations Corrects line break formatting of type annotations in
158159
java files.
159160
google-java-format Runs google java format
@@ -186,6 +187,7 @@ Spotless CLI supports the following formatter steps in alphabetical order:
186187

187188
- [clang-format](#clang-format)
188189
- [clean-that](#clean-that)
190+
- [eclipse-wtp](#eclipse-wtp)
189191
- [format-annotations](#format-annotations)
190192
- [google-java-format](#google-java-format)
191193
- [license-header](#license-header)
@@ -311,6 +313,74 @@ Example usage:
311313
spotless --target '**/src/**/*.java' clean-that --exclude-mutator=StreamAnyMatch
312314
```
313315

316+
### eclipse-wtp
317+
318+
<!---freshmark eclipsewtpshields
319+
output = [
320+
link(shield('spotless eclipse wtp version', 'spotless-eclipse-wtp', '{{libs.versions.native.include.spotlessEclipseWtp}}', 'blue'), 'https://central.sonatype.com/artifact/com.diffplug.spotless/spotless-eclipse-wtp/{{libs.versions.native.include.spotlessEclipseWtp}}'),
321+
link(shield('eclipse wtp version', 'eclipse-wtp-formatter', '{{libs.versions.native.include.spotlessEclipseWtpFormatter}}', 'blue'), 'https://github.com/diffplug/spotless/blob/main/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_wtp_formatter/v{{libs.versions.native.include.spotlessEclipseWtpFormatter}}'),
322+
].join('\n')
323+
-->
324+
325+
[![spotless eclipse wtp version](https://img.shields.io/badge/spotless--eclipse--wtp-3.23.0-blue.svg)](https://central.sonatype.com/artifact/com.diffplug.spotless/spotless-eclipse-wtp/3.23.0)
326+
[![eclipse wtp version](https://img.shields.io/badge/eclipse--wtp--formatter-4.21.0-blue.svg)](https://github.com/diffplug/spotless/blob/main/lib-extra/src/main/resources/com/diffplug/spotless/extra/eclipse_wtp_formatter/v4.21.0)
327+
328+
<!---freshmark /eclipsewtpshields -->
329+
330+
The [eclipse web tools platform (WTP)](https://projects.eclipse.org/projects/webtools) formatter is a formatter for web files such as HTML, CSS, JavaScript, JSON, XML and XHTML.
331+
332+
It comes with reasonable defaults but can be configured using configuration files. For details see the [spotless documentation](https://github.com/diffplug/spotless/tree/main/plugin-gradle#eclipse-web-tools-platform).
333+
334+
To see usage instructions for the eclipse-wtp formatter, run: `spotless eclipse-wtp --help`
335+
336+
<!---freshmark usage_eclipse_wtp
337+
output =
338+
'```\n' +
339+
{{usage.eclipse-wtp.array}}.join('\n') +
340+
'\n```';
341+
-->
342+
343+
```
344+
Usage: spotless eclipse-wtp [-hV] [-f]... [-t=<type>]
345+
Runs Eclipse WTP formatter (4.21.0)
346+
-f, --config-file The path to the Eclipse WTP configuration file. For
347+
supported config file options see spotless
348+
documentation (additional info links).
349+
-h, --help Show this help message and exit.
350+
-t, --type=<type> The type of the Eclipse WTP formatter. If not provided,
351+
the type will be guessed based on the first few files
352+
we find. If that does not work, we fail the formatting
353+
run.
354+
One of: CSS, HTML, JS, JSON, XML, XHTML
355+
-V, --version Print version information and exit.
356+
357+
✅ This step supports the following file types:
358+
* css
359+
* html
360+
* js
361+
* json
362+
* xml
363+
* xhtml
364+
365+
🌎 Additional info:
366+
* https://github.com/diffplug/spotless/tree/main/plugin-gradle#eclipse-web-to
367+
ols-platform
368+
369+
* https://projects.eclipse.org/projects/webtools
370+
```
371+
372+
<!---freshmark /usage_eclipse_wtp -->
373+
374+
Example usage:
375+
376+
```shell
377+
# format all js files using (multiple) project-specific configuration files
378+
# for details regarding the configuration files see the spotless documentation
379+
spotless --target '**/*.js' eclipse-wtp --type js --config-file spotless.xml.prefs --config-file spotless.common.properties
380+
# or use defaults and infer type from files
381+
spotless --target '**/*.css' eclipse-wtp
382+
```
383+
314384
### format-annotations
315385

316386
In Java, type annotations should be on the same line as the type that they qualify. This formatter fixes this for you.

app/build.gradle

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import com.diffplug.spotless.cli.picocli.usage.GenerateUsagePropertiesTask
2+
import com.diffplug.spotless.extra.wtp.EclipseWtpFormatterStep
3+
import org.gradle.plugins.ide.eclipse.model.EclipseWtp
24

35
plugins {
46
id 'buildlogic.picocli-conventions'
@@ -9,6 +11,7 @@ plugins {
911

1012
version = rootProject.version
1113

14+
1215
dependencies {
1316
testImplementation project(':testlib')
1417
implementation libs.bundles.spotless.libs
@@ -17,6 +20,19 @@ dependencies {
1720
// these are fixed versions of the otherwise dynamic dependencies for spotless
1821
// this is necessary to allow for native compilation where reflective access to dynamic jars is not possible
1922
implementation libs.bundles.native.includes
23+
24+
// Eclipse WTP formatter used a custom lockfile to ensure the formatter has the
25+
// correct version of all its libraries
26+
def resourceName = "/com/diffplug/spotless/extra/eclipse_wtp_formatter/v4.21.0.lockfile"
27+
logger.debug("reading eclipse wtp resource: " + resourceName)
28+
EclipseWtpFormatterStep.getResource(resourceName)
29+
.readLines()
30+
.findAll { !it.startsWith('#')} // filter out comments
31+
.each {
32+
implementation("${it}") {
33+
transitive=false
34+
}
35+
}
2036
}
2137

2238
application {
@@ -35,7 +51,7 @@ gradle.taskGraph.whenReady { TaskExecutionGraph graph ->
3551
}
3652

3753
tasks.withType(Test).configureEach {
38-
if (it.name == 'test' || it.name == 'testNpm') {
54+
if (it.name == 'test' || it.name == 'testNpm' || it.name == 'testSeparateJvm') {
3955
it.outputs.dir(nativeCompileMetaDir)
4056
if (project.hasProperty('agent')) {
4157
it.inputs.property('agent', project.property('agent')) // make sure to re-run tests if agent changes

app/src/main/java/com/diffplug/spotless/cli/SpotlessCLI.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import com.diffplug.spotless.cli.logging.output.Output;
4646
import com.diffplug.spotless.cli.steps.ClangFormat;
4747
import com.diffplug.spotless.cli.steps.CleanThat;
48+
import com.diffplug.spotless.cli.steps.EclipseWtp;
4849
import com.diffplug.spotless.cli.steps.FormatAnnotations;
4950
import com.diffplug.spotless.cli.steps.GoogleJavaFormat;
5051
import com.diffplug.spotless.cli.steps.LicenseHeader;
@@ -97,6 +98,7 @@
9798
subcommands = {
9899
ClangFormat.class,
99100
CleanThat.class,
101+
EclipseWtp.class,
100102
FormatAnnotations.class,
101103
GoogleJavaFormat.class,
102104
LicenseHeader.class,
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright 2025 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.cli.steps;
17+
18+
import java.nio.file.Path;
19+
import java.util.List;
20+
import java.util.Locale;
21+
import java.util.function.Supplier;
22+
23+
import org.jetbrains.annotations.NotNull;
24+
import org.jetbrains.annotations.Nullable;
25+
26+
import com.diffplug.spotless.FormatterStep;
27+
import com.diffplug.spotless.cli.core.SpotlessActionContext;
28+
import com.diffplug.spotless.cli.core.TargetFileTypeInferer;
29+
import com.diffplug.spotless.cli.help.AdditionalInfoLinks;
30+
import com.diffplug.spotless.cli.help.OptionConstants;
31+
import com.diffplug.spotless.cli.help.SupportedFileTypes;
32+
import com.diffplug.spotless.extra.EclipseBasedStepBuilder;
33+
import com.diffplug.spotless.extra.wtp.EclipseWtpFormatterStep;
34+
35+
import picocli.CommandLine;
36+
37+
@CommandLine.Command(
38+
name = "eclipse-wtp",
39+
description = "Runs Eclipse WTP formatter (" + EclipseWtp.ECLIPSE_WTP_VERSION + ")")
40+
@SupportedFileTypes({"css", "html", "js", "json", "xml", "xhtml"})
41+
@AdditionalInfoLinks({
42+
"https://github.com/diffplug/spotless/tree/main/plugin-gradle#eclipse-web-tools-platform",
43+
"https://projects.eclipse.org/projects/webtools"
44+
})
45+
public class EclipseWtp extends SpotlessFormatterStep {
46+
47+
public static final String ECLIPSE_WTP_VERSION = "4.21.0"; // TODO we need to slurp in the lock file also
48+
49+
@CommandLine.Option(
50+
names = {"-f", "--config-file"},
51+
arity = "0",
52+
description = "The path to the Eclipse WTP configuration file. "
53+
+ "For supported config file options see spotless documentation (additional info links).")
54+
List<Path> configFiles;
55+
56+
@CommandLine.Option(
57+
names = {"-t", "--type"},
58+
description =
59+
"The type of the Eclipse WTP formatter. If not provided, the type will be guessed based on the first few files we find. If that does not work, we fail the formatting run."
60+
+ OptionConstants.VALID_VALUES_SUFFIX)
61+
Type type;
62+
63+
public enum Type {
64+
CSS(EclipseWtpFormatterStep.CSS),
65+
HTML(EclipseWtpFormatterStep.HTML),
66+
JS(EclipseWtpFormatterStep.JS),
67+
JSON(EclipseWtpFormatterStep.JSON),
68+
XML(EclipseWtpFormatterStep.XML),
69+
XHTML(EclipseWtpFormatterStep.HTML); // XHTML is treated as HTML in Eclipse WTP
70+
71+
private final EclipseWtpFormatterStep backendEclipseWtpType;
72+
73+
Type(EclipseWtpFormatterStep backendEclipseWtpType) {
74+
this.backendEclipseWtpType = backendEclipseWtpType;
75+
}
76+
77+
public @NotNull EclipseWtpFormatterStep toEclipseWtpType() {
78+
return this.backendEclipseWtpType;
79+
}
80+
81+
public static @Nullable Type fromTargetFileType(@NotNull TargetFileTypeInferer.TargetFileType targetFileType) {
82+
return switch (targetFileType.fileExtension().toLowerCase(Locale.getDefault())) {
83+
case "css" -> CSS;
84+
case "html", "htm" -> HTML;
85+
case "js" -> JS;
86+
case "json" -> JSON;
87+
case "xml" -> XML;
88+
case "xhtml" -> XHTML;
89+
default -> null;
90+
};
91+
}
92+
}
93+
94+
@Override
95+
public @NotNull List<FormatterStep> prepareFormatterSteps(SpotlessActionContext context) {
96+
EclipseWtpFormatterStep wtpType = type(context::targetFileType).toEclipseWtpType();
97+
EclipseBasedStepBuilder builder = wtpType.createBuilder(context.provisioner());
98+
builder.setVersion(ECLIPSE_WTP_VERSION);
99+
if (configFiles != null && !configFiles.isEmpty()) {
100+
builder.setPreferences(configFiles.stream()
101+
.map(context::resolvePath)
102+
.map(Path::toFile)
103+
.toList());
104+
}
105+
return List.of(builder.build());
106+
}
107+
108+
private Type type(Supplier<TargetFileTypeInferer.TargetFileType> targetFileTypeSupplier) {
109+
if (type != null) {
110+
return type;
111+
}
112+
// try type inferring
113+
TargetFileTypeInferer.TargetFileType targetFileType = targetFileTypeSupplier.get();
114+
Type inferredType = Type.fromTargetFileType(targetFileType);
115+
if (inferredType != null) {
116+
return inferredType;
117+
} else {
118+
throw new IllegalArgumentException("Could not infer Eclipse WTP type from target file type: "
119+
+ targetFileType.fileExtension() + " - workaround by specifying the --type option.");
120+
}
121+
}
122+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2025 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.cli.steps;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.api.parallel.Isolated;
20+
21+
import com.diffplug.spotless.tag.CliNativeTest;
22+
import com.diffplug.spotless.tag.CliProcessTest;
23+
import com.diffplug.spotless.tag.SeparateJvmTest;
24+
25+
@Isolated
26+
@SeparateJvmTest
27+
@CliNativeTest
28+
@CliProcessTest
29+
public class EclipseWtpCssTest extends EclipseWtpTestBase {
30+
31+
@Test
32+
void itSupportsFormattingCssFileType() {
33+
String fileName = runEclipseWtpWithType(EclipseWtp.Type.CSS, "body {\n" + "a: v; b: \n" + "v;\n" + "} \n");
34+
selfie().expectResource(fileName).toMatchDisk();
35+
}
36+
37+
@Test
38+
void itInfersCssFileTypeFromFileExtension() {
39+
String fileName = runEclipseWtpWithTypeInferred("css", "body {\n" + "a: v; b: \n" + "v;\n" + "} \n");
40+
selfie().expectResource(fileName).toMatchDisk();
41+
}
42+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2025 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.cli.steps;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.junit.jupiter.api.parallel.Isolated;
20+
21+
import com.diffplug.spotless.tag.CliNativeTest;
22+
import com.diffplug.spotless.tag.CliProcessTest;
23+
import com.diffplug.spotless.tag.SeparateJvmTest;
24+
25+
@Isolated
26+
@SeparateJvmTest
27+
@CliNativeTest
28+
@CliProcessTest
29+
public class EclipseWtpCssWithConfigTest extends EclipseWtpTestBase {
30+
31+
@Test
32+
void itUsesConfigurationFile() {
33+
String fileName = runEclipseWtpWithTypeAndConfigFile(
34+
EclipseWtp.Type.CSS,
35+
"body {\n" + "a: v; b: \n" + "v;\n" + "} \n",
36+
"org.eclipse.wst.css.core.prefs");
37+
selfie().expectResource(fileName).toMatchDisk();
38+
}
39+
}

0 commit comments

Comments
 (0)