Skip to content

Commit

Permalink
Add support for gherkin (#1649)
Browse files Browse the repository at this point in the history
  • Loading branch information
nedtwigg authored Apr 5, 2023
2 parents 02b6f60 + 77190d4 commit 4ca8189
Show file tree
Hide file tree
Showing 36 changed files with 824 additions and 5 deletions.
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,13 @@ indent_style = space
[*.{yml,yaml}]
indent_style = space
indent_size = 2

# Prevent unexpected automatic indentation when crafting test-cases
[/testlib/src/main/resources/**]
charset = unset
end_of_line = unset
insert_final_newline = unset
trim_trailing_whitespace = unset
indent_style = unset
indent_size = unset
ij_formatter_enabled = false
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
### Added
* Support configuration of mirrors for P2 repositories in `EquoBasedStepBuilder` ([#1629](https://github.com/diffplug/spotless/issues/1629)).
* The `style` option in Palantir Java Format ([#1654](https://github.com/diffplug/spotless/pull/1654)).
* Added formatter for Gherkin feature files ([#1649](https://github.com/diffplug/spotless/issues/1649)).
### Changes
* **POTENTIALLY BREAKING** Converted `googleJavaFormat` to a compile-only dependency and drop support for versions < `1.8`. ([#1630](https://github.com/diffplug/spotless/pull/1630))
* Bump default `googleJavaFormat` version `1.15.0` -> `1.16.0`. ([#1630](https://github.com/diffplug/spotless/pull/1630))
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ lib('generic.TrimTrailingWhitespaceStep') +'{{yes}} | {{yes}}
lib('antlr4.Antlr4FormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('cpp.ClangFormatStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |',
extra('cpp.EclipseFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('gherkin.GherkinUtilsStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
extra('groovy.GrEclipseFormatterStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('java.GoogleJavaFormatStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
lib('java.ImportOrderStep') +'{{yes}} | {{yes}} | {{yes}} | {{no}} |',
Expand Down Expand Up @@ -125,6 +126,7 @@ lib('yaml.JacksonYamlStep') +'{{yes}} | {{yes}}
| [`antlr4.Antlr4FormatterStep`](lib/src/main/java/com/diffplug/spotless/antlr4/Antlr4FormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`cpp.ClangFormatStep`](lib/src/main/java/com/diffplug/spotless/cpp/ClangFormatStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
| [`cpp.EclipseFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/cpp/EclipseFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`gherkin.GherkinUtilsStep`](lib/src/main/java/com/diffplug/spotless/gherkin/GherkinUtilsStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`groovy.GrEclipseFormatterStep`](lib-extra/src/main/java/com/diffplug/spotless/extra/groovy/GrEclipseFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`java.GoogleJavaFormatStep`](lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
| [`java.ImportOrderStep`](lib/src/main/java/com/diffplug/spotless/java/ImportOrderStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
Expand Down
6 changes: 5 additions & 1 deletion lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def NEEDS_GLUE = [
'scalafmt',
'jackson',
'gson',
'cleanthat'
'cleanthat',
'gherkin'
]
for (glue in NEEDS_GLUE) {
sourceSets.register(glue) {
Expand Down Expand Up @@ -115,6 +116,9 @@ dependencies {

cleanthatCompileOnly 'io.github.solven-eu.cleanthat:java:2.6'
compatCleanthat2Dot1CompileAndTestOnly 'io.github.solven-eu.cleanthat:java:2.6'

gherkinCompileOnly 'io.cucumber:gherkin-utils:8.0.2'
gherkinCompileOnly 'org.slf4j:slf4j-api:2.0.0'
}

// we'll hold the core lib to a high standard
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2023 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.gherkin;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.diffplug.spotless.FormatterFunc;
import com.diffplug.spotless.gherkin.GherkinUtilsConfig;

import io.cucumber.gherkin.GherkinParser;
import io.cucumber.gherkin.utils.pretty.Pretty;
import io.cucumber.gherkin.utils.pretty.Syntax;
import io.cucumber.messages.types.Envelope;
import io.cucumber.messages.types.GherkinDocument;
import io.cucumber.messages.types.Source;
import io.cucumber.messages.types.SourceMediaType;

public class GherkinUtilsFormatterFunc implements FormatterFunc {
private static final Logger LOGGER = LoggerFactory.getLogger(GherkinUtilsFormatterFunc.class);

private final GherkinUtilsConfig gherkinSimpleConfig;

public GherkinUtilsFormatterFunc(GherkinUtilsConfig gherkinSimpleConfig) {
this.gherkinSimpleConfig = gherkinSimpleConfig;
}

// Follows https://github.com/cucumber/gherkin-utils/blob/main/java/src/test/java/io/cucumber/gherkin/utils/pretty/PrettyTest.java
private GherkinDocument parse(String gherkin) {
GherkinParser parser = GherkinParser
.builder()
.includeSource(false)
.build();
return parser.parse(Envelope.of(new Source("test.feature", gherkin, SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN)))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No envelope"))
.getGherkinDocument()
.orElseThrow(() -> new IllegalArgumentException("No gherkin document"));
}

@Override
public String apply(String inputString) {
GherkinDocument gherkinDocument = parse(inputString);

return Pretty.prettyPrint(gherkinDocument, Syntax.gherkin);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2023 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.gherkin;

import java.io.Serializable;

public class GherkinUtilsConfig implements Serializable {
private static final long serialVersionUID = 1L;

public static int defaultIndentSpaces() {
// https://cucumber.io/docs/gherkin/reference/
// Recommended indentation is 2 spaces
return 2;
}

final int indentSpaces;

public GherkinUtilsConfig(int indentSpaces) {
this.indentSpaces = indentSpaces;
}

public int getIndentSpaces() {
return indentSpaces;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2021-2023 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.gherkin;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;

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

public class GherkinUtilsStep {
private static final String MAVEN_COORDINATE = "io.cucumber:gherkin-utils:";
private static final String DEFAULT_VERSION = "8.0.2";

public static String defaultVersion() {
return DEFAULT_VERSION;
}

public static FormatterStep create(GherkinUtilsConfig gherkinSimpleConfig,
String formatterVersion, Provisioner provisioner) {
Objects.requireNonNull(provisioner, "provisioner cannot be null");
return FormatterStep.createLazy("gherkin", () -> new GherkinUtilsStep.State(gherkinSimpleConfig, formatterVersion, provisioner), GherkinUtilsStep.State::toFormatter);
}

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

private final GherkinUtilsConfig gherkinSimpleConfig;
private final JarState jarState;

private State(GherkinUtilsConfig gherkinSimpleConfig, String formatterVersion, Provisioner provisioner) throws IOException {
this.gherkinSimpleConfig = gherkinSimpleConfig;
this.jarState = JarState.from(MAVEN_COORDINATE + formatterVersion, provisioner);
}

FormatterFunc toFormatter() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
InstantiationException, IllegalAccessException {
Class<?> formatterFunc = jarState.getClassLoader().loadClass("com.diffplug.spotless.glue.gherkin.GherkinUtilsFormatterFunc");
Constructor<?> constructor = formatterFunc.getConstructor(GherkinUtilsConfig.class);
return (FormatterFunc) constructor.newInstance(gherkinSimpleConfig);
}
}

private GherkinUtilsStep() {
// cannot be directly instantiated
}
}
1 change: 1 addition & 0 deletions plugin-gradle/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
Mirrors are selected by prefix match, for example `https://download.eclipse.org/eclipse/updates/4.26/` will be redirected to `https://some.internal.mirror/eclipse/eclipse/updates/4.26/`.
The same configuration exists for `greclipse` and `eclipseCdt`.
* The `style` option in Palantir Java Format ([#1654](https://github.com/diffplug/spotless/pull/1654)).
* Added support for Gherkin feature files ([#1649](https://github.com/diffplug/spotless/issues/1649)).
### Changes
* **POTENTIALLY BREAKING** Drop support for `googleJavaFormat` versions &lt; `1.8`. ([#1630](https://github.com/diffplug/spotless/pull/1630))
* Bump default `googleJavaFormat` version `1.15.0` -> `1.16.0`. ([#1630](https://github.com/diffplug/spotless/pull/1630))
Expand Down
31 changes: 30 additions & 1 deletion plugin-gradle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ Spotless supports all of Gradle's built-in performance features (incremental bui
- [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier), [ESLint](#eslint-typescript))
- [Javascript](#javascript) ([prettier](#prettier), [ESLint](#eslint-javascript))
- [JSON](#json)
- [YAML](#yaml)
- [Gherkin](#gherkin)
- Multiple languages
- [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection), [caching `npm install` results](#caching-results-of-npm-install))
- javascript, jsx, angular, vue, flow, typescript, css, less, scss, html, json, graphql, markdown, ymaml
Expand Down Expand Up @@ -850,7 +852,34 @@ spotless {
}
```
<a name="applying-prettier-to-javascript--flow--typescript--css--scss--less--jsx--graphql--yaml--etc"></a>
## Gherkin
- `com.diffplug.gradle.spotless.GherkinExtension` [javadoc](https://javadoc.io/doc/com.diffplug.spotless/spotless-plugin-gradle/6.17.0/com/diffplug/gradle/spotless/GherkinExtension.html), [code](https://github.com/diffplug/spotless/blob/main/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/GherkinExtension.java)
```gradle
spotless {
gherkin {
target 'src/**/*.feature' // you have to set the target manually
gherkinUtils() // has its own section below
}
}
```
### gherkinUtils
[homepage](https://github.com/cucumber/gherkin-utils). [changelog](https://github.com/cucumber/gherkin-utils/blob/main/CHANGELOG.md).
Uses a Gherkin pretty-printer that optionally allows configuring the number of spaces that are used to pretty print objects:
```gradle
spotless {
gherkin {
target 'src/**/*.feature' // required to be set explicitly
gherkinUtils()
.version('8.0.2') // optional: custom version of 'io.cucumber:gherkin-utils'
}
}
```
## Prettier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright 2016-2023 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.FormatterStep;
import com.diffplug.spotless.gherkin.GherkinUtilsStep;

public class GherkinExtension extends FormatExtension {
static final String NAME = "gherkin";

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

@Override
protected void setupTask(SpotlessTask task) {
if (target == null) {
throw noDefaultTargetException();
}
super.setupTask(task);
}

public GherkinUtilsConfig gherkinUtils() {
return new GherkinUtilsConfig();
}

public class GherkinUtilsConfig {
private String version;
private int indent;

public GherkinUtilsConfig() {
this.version = GherkinUtilsStep.defaultVersion();
this.indent = com.diffplug.spotless.gherkin.GherkinUtilsConfig.defaultIndentSpaces();
addStep(createStep());
}

public void version(String version) {
this.version = version;
replaceStep(createStep());
}

private FormatterStep createStep() {
return GherkinUtilsStep.create(new com.diffplug.spotless.gherkin.GherkinUtilsConfig(indent), version, provisioner());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ public void yaml(Action<YamlExtension> closure) {
format(YamlExtension.NAME, YamlExtension.class, closure);
}

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

/** Configures a custom extension. */
public void format(String name, Action<FormatExtension> closure) {
requireNonNull(name, "name");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2021-2023 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 java.io.IOException;

import org.junit.jupiter.api.Test;

public class GherkinExtensionTest extends GradleIntegrationHarness {
@Test
public void defaultFormatting() throws IOException {
setFile("build.gradle").toLines(
"plugins {",
" id 'java'",
" id 'com.diffplug.spotless'",
"}",
"repositories { mavenCentral() }",
"spotless {",
" gherkin {",
" target 'examples/**/*.feature'",
" gherkinUtils()",
" }",
"}");
setFile("src/main/resources/example.feature").toResource("gherkin/minimalBefore.feature");
setFile("examples/main/resources/example.feature").toResource("gherkin/minimalBefore.feature");
gradleRunner().withArguments("spotlessApply").build();
assertFile("src/main/resources/example.feature").sameAsResource("gherkin/minimalBefore.feature");
assertFile("examples/main/resources/example.feature").sameAsResource("gherkin/minimalAfter.feature");
}

}
Loading

0 comments on commit 4ca8189

Please sign in to comment.