Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native markdown formatting for maven #1011

Merged
merged 10 commits into from Dec 23, 2021
2 changes: 2 additions & 0 deletions CHANGES.md
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
* Added support for Markdown with `flexmark` at `0.62.2` ([#1011](https://github.com/diffplug/spotless/pull/1011)).

## [2.20.3] - 2021-12-15
### Fixed
Expand Down
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -64,6 +64,7 @@ lib('kotlin.KtLintStep') +'{{yes}} | {{yes}}
lib('kotlin.KtfmtStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('kotlin.DiktatStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('markdown.FreshMarkStep') +'{{yes}} | {{no}} | {{no}} | {{no}} |',
lib('markdown.FlexmarkStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |',
lib('npm.PrettierFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('npm.TsFmtFormatterStep') +'{{yes}} | {{yes}} | {{no}} | {{no}} |',
lib('pom.SortPomStepStep') +'{{no}} | {{yes}} | {{no}} | {{no}} |',
Expand Down Expand Up @@ -103,6 +104,7 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}}
| [`kotlin.KtfmtStep`](lib/src/main/java/com/diffplug/spotless/kotlin/KtfmtStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`kotlin.DiktatStep`](lib/src/main/java/com/diffplug/spotless/kotlin/DiktatStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`markdown.FreshMarkStep`](lib/src/main/java/com/diffplug/spotless/markdown/FreshMarkStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
| [`markdown.FlexmarkStep`](lib/src/main/java/com/diffplug/spotless/markdown/FlexmarkStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: |
| [`npm.PrettierFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`npm.TsFmtFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
| [`pom.SortPomStepStep`](lib/src/main/java/com/diffplug/spotless/pom/SortPomStepStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: |
Expand Down
6 changes: 5 additions & 1 deletion lib/build.gradle
Expand Up @@ -8,7 +8,8 @@ apply from: rootProject.file('gradle/java-publish.gradle')

def NEEDS_GLUE = [
'sortPom',
'ktlint'
'ktlint',
'flexmark'
]
for (glue in NEEDS_GLUE) {
sourceSets.register(glue) {
Expand All @@ -32,6 +33,9 @@ dependencies {
ktlintCompileOnly "com.pinterest:ktlint:$VER_KTLINT"
ktlintCompileOnly "com.pinterest.ktlint:ktlint-core:$VER_KTLINT"
ktlintCompileOnly "com.pinterest.ktlint:ktlint-ruleset-standard:$VER_KTLINT"

// used for markdown formatting
flexmarkCompileOnly 'com.vladsch.flexmark:flexmark-all:0.62.2'
}

// we'll hold the core lib to a high standard
Expand Down
@@ -0,0 +1,91 @@
/*
* Copyright 2021 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.markdown;

import com.vladsch.flexmark.formatter.Formatter;
import com.vladsch.flexmark.parser.Parser;
import com.vladsch.flexmark.parser.ParserEmulationProfile;
import com.vladsch.flexmark.parser.PegdownExtensions;
import com.vladsch.flexmark.profile.pegdown.PegdownOptionsAdapter;
import com.vladsch.flexmark.util.ast.Document;
import com.vladsch.flexmark.util.data.MutableDataHolder;
import com.vladsch.flexmark.util.data.MutableDataSet;

import com.diffplug.spotless.FormatterFunc;

/**
* The formatter function for <a href="https://github.com/vsch/flexmark-java">flexmark-java</a>.
*/
public class FlexmarkFormatterFunc implements FormatterFunc {

/**
* The emulation profile is used by both the parser and the formatter and generally determines the markdown flavor.
* COMMONMARK is the default defined by flexmark-java.
*/
private static final String DEFAULT_EMULATION_PROFILE = "COMMONMARK";

private final Parser parser;
private final Formatter formatter;

public FlexmarkFormatterFunc() {
// flexmark-java has a separate parser and renderer (formatter)
// this is build from the example in https://github.com/vsch/flexmark-java/wiki/Markdown-Formatter

// The emulation profile generally determines the markdown flavor. We use the same one for both the parser and
// the formatter, to make sure this formatter func is idempotent.
final ParserEmulationProfile emulationProfile = ParserEmulationProfile.valueOf(DEFAULT_EMULATION_PROFILE);

final MutableDataHolder parserOptions = createParserOptions(emulationProfile);
final MutableDataHolder formatterOptions = createFormatterOptions(parserOptions, emulationProfile);

parser = Parser.builder(parserOptions).build();
formatter = Formatter.builder(formatterOptions).build();
}

/**
* Creates the parser options.
* See: https://github.com/vsch/flexmark-java/wiki/Markdown-Formatter#options
*
* @param emulationProfile the emulation profile (or flavor of markdown) the parser should use
* @return the created parser options
*/
private static MutableDataHolder createParserOptions(ParserEmulationProfile emulationProfile) {
final MutableDataHolder parserOptions = PegdownOptionsAdapter.flexmarkOptions(PegdownExtensions.ALL).toMutable();
parserOptions.set(Parser.PARSER_EMULATION_PROFILE, emulationProfile);
return parserOptions;
}

/**
* Creates the formatter options, copies the parser extensions and changes defaults that make sense for a formatter.
* See: https://github.com/vsch/flexmark-java/wiki/Markdown-Formatter#options
*
* @param parserOptions the options used for the parser
* @param emulationProfile the emulation profile (or flavor of markdown) the formatter should use
* @return the created formatter options
*/
private static MutableDataHolder createFormatterOptions(MutableDataHolder parserOptions, ParserEmulationProfile emulationProfile) {
final MutableDataHolder formatterOptions = new MutableDataSet();
formatterOptions.set(Parser.EXTENSIONS, Parser.EXTENSIONS.get(parserOptions));
formatterOptions.set(Formatter.FORMATTER_EMULATION_PROFILE, emulationProfile);
return formatterOptions;
}

@Override
public String apply(String input) throws Exception {
final Document parsedMarkdown = parser.parse(input);
return formatter.render(parsedMarkdown);
}
}
Expand Up @@ -115,7 +115,7 @@ public static boolean defaultReflowLongStrings() {
static final class State implements Serializable {
private static final long serialVersionUID = 1L;

/** The jar that contains the eclipse formatter. */
/** The jar that contains the formatter. */
final JarState jarState;
final String stepName;
final String version;
Expand Down
Expand Up @@ -82,7 +82,7 @@ static final class State implements Serializable {
/** Are the files being linted Kotlin script files. */
private final boolean isScript;
private final String pkg;
/** The jar that contains the eclipse formatter. */
/** The jar that contains the formatter. */
final JarState jarState;
private final TreeMap<String, String> userData;
private final boolean useParams;
Expand Down
Expand Up @@ -108,7 +108,7 @@ static final class State implements Serializable {
* Option that allows to apply formatting options to perform a 4 spaces block and continuation indent.
*/
private final Style style;
/** The jar that contains the eclipse formatter. */
/** The jar that contains the formatter. */
final JarState jarState;

State(String version, Provisioner provisioner, Style style) throws IOException {
Expand Down
72 changes: 72 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/markdown/FlexmarkStep.java
@@ -0,0 +1,72 @@
/*
* Copyright 2016-2021 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.markdown;

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

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

/** A step for <a href="https://github.com/vsch/flexmark-java">flexmark-java</a>. */
public class FlexmarkStep {
// prevent direct instantiation
private FlexmarkStep() {}

private static final String DEFAULT_VERSION = "0.62.2";
private static final String NAME = "flexmark-java";
private static final String MAVEN_COORDINATE = "com.vladsch.flexmark:flexmark-all:";

/** Creates a formatter step for the default version. */
public static FormatterStep create(Provisioner provisioner) {
return create(defaultVersion(), provisioner);
}

/** Creates a formatter step for the given version. */
public static FormatterStep create(String version, Provisioner provisioner) {
Objects.requireNonNull(version, "version");
Objects.requireNonNull(provisioner, "provisioner");
return FormatterStep.createLazy(NAME,
() -> new State(JarState.from(MAVEN_COORDINATE + version, provisioner)),
State::createFormat);
}

public static String defaultVersion() {
return DEFAULT_VERSION;
}

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

/** The jar that contains the formatter. */
final JarState jarState;

State(JarState jarState) {
this.jarState = jarState;
}

FormatterFunc createFormat() throws Exception {
final ClassLoader classLoader = jarState.getClassLoader();
final Class<?> formatterFunc = classLoader.loadClass("com.diffplug.spotless.glue.markdown.FlexmarkFormatterFunc");
final Constructor<?> constructor = formatterFunc.getConstructor();
return (FormatterFunc) constructor.newInstance();
}

}
}
Expand Up @@ -65,7 +65,7 @@ public static String defaultVersion() {
private static class State implements Serializable {
private static final long serialVersionUID = 1L;

/** The jar that contains the eclipse formatter. */
/** The jar that contains the formatter. */
final JarState jarState;
final NavigableMap<String, ?> properties;

Expand Down
1 change: 1 addition & 0 deletions plugin-maven/CHANGES.md
Expand Up @@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]
### Added
* Incremental up-to-date checking ([#935](https://github.com/diffplug/spotless/pull/935)).
* Support for Markdown with `flexmark` at `0.62.2` ([#1011](https://github.com/diffplug/spotless/pull/1011)).

## [2.17.7] - 2021-12-16
### Fixed
Expand Down
23 changes: 23 additions & 0 deletions plugin-maven/README.md
Expand Up @@ -56,6 +56,7 @@ user@machine repo % mvn spotless:check
- [Antlr4](#antlr4) ([antlr4formatter](#antlr4formatter))
- [Sql](#sql) ([dbeaver](#dbeaver))
- [Maven Pom](#maven-pom) ([sortPom](#sortpom))
- [Markdown](#markdown) ([flexmark](#flexmark))
- [Typescript](#typescript) ([tsfmt](#tsfmt), [prettier](#prettier))
- Multiple languages
- [Prettier](#prettier) ([plugins](#prettier-plugins), [npm detection](#npm-detection), [`.npmrc` detection](#npmrc-detection))
Expand Down Expand Up @@ -585,6 +586,28 @@ All configuration settings are optional, they are described in detail [here](htt
</sortPom>
```

## Markdown

[code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/markdown/Markdown.java). [available steps](https://github.com/diffplug/spotless/tree/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/markdown).

```xml
<configuration>
<markdown>
<includes> <!-- You have to set the target manually -->
<include>**/*.md</include>
</includes>

<flexmark/> <!-- has its own section below -->
</markdown>
</configuration>
```

### Flexmark

[homepage](https://github.com/vsch/flexmark-java). [code](https://github.com/diffplug/spotless/blob/main/plugin-maven/src/main/java/com/diffplug/spotless/maven/markdown/Flexmark.java). Flexmark is a flexible Commonmark/Markdown parser that can be used to format Markdown files. It supports different [flavors of Markdown](https://github.com/vsch/flexmark-java#markdown-processor-emulation) and [many formatting options](https://github.com/vsch/flexmark-java/wiki/Markdown-Formatter#options).

Currently, none of the available options can be configured yet. It uses only the default options together with `COMMONMARK` as `FORMATTER_EMULATION_PROFILE`.

<a name="applying-to-typescript-source"></a>

## Typescript
Expand Down
Expand Up @@ -63,6 +63,7 @@
import com.diffplug.spotless.maven.incremental.UpToDateChecking;
import com.diffplug.spotless.maven.java.Java;
import com.diffplug.spotless.maven.kotlin.Kotlin;
import com.diffplug.spotless.maven.markdown.Markdown;
import com.diffplug.spotless.maven.pom.Pom;
import com.diffplug.spotless.maven.python.Python;
import com.diffplug.spotless.maven.scala.Scala;
Expand Down Expand Up @@ -151,6 +152,9 @@ public abstract class AbstractSpotlessMojo extends AbstractMojo {
@Parameter
private Python python;

@Parameter
private Markdown markdown;

@Parameter(property = "spotlessFiles")
private String filePatterns;

Expand Down Expand Up @@ -278,7 +282,7 @@ private Set<String> getIncludes(FormatterFactory formatterFactory) {
Set<String> configuredIncludes = formatterFactory.includes();
Set<String> includes = configuredIncludes.isEmpty() ? formatterFactory.defaultIncludes() : configuredIncludes;
if (includes.isEmpty()) {
throw new PluginException("You must specify some files to include, such as '<includes><include>src/**</include></includes>'");
throw new PluginException("You must specify some files to include, such as '<includes><include>src/**/*.blah</include></includes>'");
}
return includes;
}
Expand Down Expand Up @@ -308,7 +312,7 @@ private FileLocator getFileLocator() {
}

private List<FormatterFactory> getFormatterFactories() {
return Stream.concat(formats.stream(), Stream.of(groovy, java, scala, kotlin, cpp, typescript, antlr4, pom, sql, python))
return Stream.concat(formats.stream(), Stream.of(groovy, java, scala, kotlin, cpp, typescript, antlr4, pom, sql, python, markdown))
.filter(Objects::nonNull)
.collect(toList());
}
Expand Down
@@ -0,0 +1,35 @@
/*
* Copyright 2021 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.maven.markdown;

import org.apache.maven.plugins.annotations.Parameter;

import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.markdown.FlexmarkStep;
import com.diffplug.spotless.maven.FormatterStepConfig;
import com.diffplug.spotless.maven.FormatterStepFactory;

public class Flexmark implements FormatterStepFactory {

@Parameter
private String version;

@Override
public FormatterStep newFormatterStep(FormatterStepConfig config) {
String version = this.version != null ? this.version : FlexmarkStep.defaultVersion();
return FlexmarkStep.create(version, config.getProvisioner());
}
}