From 9c27d39f642fcee2afbd032d0179a30074ea96f9 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 28 Sep 2023 15:09:11 -0700 Subject: [PATCH 1/7] Create new GIT_ATTRIBUTES_FAST_ALLSAME which is the same as GIT_ATTRIBUTES, except that: - it is much faster - it assumes that every file in a given format has the same line endings --- .../com/diffplug/spotless/LineEnding.java | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/LineEnding.java b/lib/src/main/java/com/diffplug/spotless/LineEnding.java index 72b2532f2a..fa487a03b7 100644 --- a/lib/src/main/java/com/diffplug/spotless/LineEnding.java +++ b/lib/src/main/java/com/diffplug/spotless/LineEnding.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2022 DiffPlug + * 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. @@ -37,6 +37,14 @@ public Policy createPolicy() { return super.createPolicy(); } }, + /** Uses the same line endings as Git, and assumes that every single file being formatted will have the same line ending. */ + GIT_ATTRIBUTES_FAST_ALLSAME { + /** .gitattributes is path-specific, so you must use {@link LineEnding#createPolicy(File, Supplier)}. */ + @Override @Deprecated + public Policy createPolicy() { + return super.createPolicy(); + } + }, /** {@code \n} on unix systems, {@code \r\n} on windows systems. */ PLATFORM_NATIVE, /** {@code \r\n} */ @@ -51,7 +59,7 @@ public Policy createPolicy() { public Policy createPolicy(File projectDir, Supplier> toFormat) { Objects.requireNonNull(projectDir, "projectDir"); Objects.requireNonNull(toFormat, "toFormat"); - if (this != GIT_ATTRIBUTES) { + if (this != GIT_ATTRIBUTES && this != GIT_ATTRIBUTES_FAST_ALLSAME) { return createPolicy(); } else { if (gitAttributesPolicyCreator == null) { @@ -64,7 +72,39 @@ public Policy createPolicy(File projectDir, Supplier> toFormat) { } } // gitAttributesPolicyCreator will always be nonnull at this point - return gitAttributesPolicyCreator.apply(projectDir, toFormat); + Policy policy = gitAttributesPolicyCreator.apply(projectDir, toFormat); + if (this == GIT_ATTRIBUTES) { + return policy; + } else if (this == GIT_ATTRIBUTES_FAST_ALLSAME) { + return new LazyAllTheSame(policy, toFormat); + } else { + throw new IllegalArgumentException("Unknown " + this); + } + } + } + + static class LazyAllTheSame extends LazyForwardingEquality implements Policy { + private transient Policy policy; + private transient Supplier> toFormat; + + public LazyAllTheSame(Policy policy, Supplier> toFormat) { + this.policy = policy; + this.toFormat = toFormat; + } + + @Override + protected String calculateState() throws Exception { + var files = toFormat.get().iterator(); + if (files.hasNext()) { + return policy.getEndingFor(files.next()); + } else { + return LineEnding.UNIX.str(); + } + } + + @Override + public String getEndingFor(File file) { + return state(); } } From 54c0af2bb701fc1b0756b8ff1bfd568d0c43bf65 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 28 Sep 2023 15:13:52 -0700 Subject: [PATCH 2/7] Set the default line endings to be GIT_ATTRIBUTES_FAST_ALLSAME. --- .../java/com/diffplug/gradle/spotless/SpotlessExtension.java | 2 +- .../java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java index 838231c9f8..514945f22d 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java @@ -57,7 +57,7 @@ RegisterDependenciesTask getRegisterDependenciesTask() { } /** Line endings (if any). */ - LineEnding lineEndings = LineEnding.GIT_ATTRIBUTES; + LineEnding lineEndings = LineEnding.GIT_ATTRIBUTES_FAST_ALLSAME; public LineEnding getLineEndings() { return lineEndings; diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java index 2c295cef27..97a61ddaea 100644 --- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java +++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/AbstractSpotlessMojo.java @@ -80,7 +80,7 @@ public abstract class AbstractSpotlessMojo extends AbstractMojo { private static final String DEFAULT_INDEX_FILE_NAME = "spotless-index"; private static final String DEFAULT_ENCODING = "UTF-8"; - private static final String DEFAULT_LINE_ENDINGS = "GIT_ATTRIBUTES"; + private static final String DEFAULT_LINE_ENDINGS = "GIT_ATTRIBUTES_FAST_ALLSAME"; /** Value to allow unsetting the ratchet inherited from parent pom configuration. */ static final String RATCHETFROM_NONE = "NONE"; From 809fb4b0785803d96b2acde585db747f86e89c04 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 28 Sep 2023 15:28:26 -0700 Subject: [PATCH 3/7] Make GIT_ATTRIBUTES_FAST_ALLSAME faster. --- .../extra/GitAttributesLineEndings.java | 35 ++++++++++++ .../com/diffplug/spotless/LineEnding.java | 56 ++++--------------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/GitAttributesLineEndings.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/GitAttributesLineEndings.java index fb54be4ba0..c53e9a3e92 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/GitAttributesLineEndings.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/GitAttributesLineEndings.java @@ -68,6 +68,41 @@ public final class GitAttributesLineEndings { // prevent direct instantiation private GitAttributesLineEndings() {} + /** + * Creates a line-endings policy which matches {@link #create(File, Supplier)}, + * which is much faster at the cost that every file under the policy + * is assumed to have the same line endings as the first file. + */ + public static LineEnding.Policy createFastAllSame(File projectDir, Supplier> toFormat) { + return new LazyAllTheSame(projectDir, toFormat); + } + + static class LazyAllTheSame extends LazyForwardingEquality implements LineEnding.Policy { + transient File projectDir; + transient Supplier> toFormat; + + public LazyAllTheSame(File projectDir, Supplier> toFormat) { + this.projectDir = projectDir; + this.toFormat = toFormat; + } + + @Override + protected String calculateState() throws Exception { + var files = toFormat.get().iterator(); + if (files.hasNext()) { + Runtime runtime = new RuntimeInit(projectDir).atRuntime(); + return runtime.getEndingFor(files.next()); + } else { + return LineEnding.UNIX.str(); + } + } + + @Override + public String getEndingFor(File file) { + return state(); + } + } + /** * Creates a line-endings policy whose serialized state is relativized against projectDir, * at the cost of eagerly evaluating the line-ending state of every target file when the diff --git a/lib/src/main/java/com/diffplug/spotless/LineEnding.java b/lib/src/main/java/com/diffplug/spotless/LineEnding.java index fa487a03b7..708699904c 100644 --- a/lib/src/main/java/com/diffplug/spotless/LineEnding.java +++ b/lib/src/main/java/com/diffplug/spotless/LineEnding.java @@ -59,52 +59,20 @@ public Policy createPolicy() { public Policy createPolicy(File projectDir, Supplier> toFormat) { Objects.requireNonNull(projectDir, "projectDir"); Objects.requireNonNull(toFormat, "toFormat"); - if (this != GIT_ATTRIBUTES && this != GIT_ATTRIBUTES_FAST_ALLSAME) { - return createPolicy(); + String gitAttributesMethod; + if (this == GIT_ATTRIBUTES) { + gitAttributesMethod = "create"; + } else if (this == GIT_ATTRIBUTES_FAST_ALLSAME) { + gitAttributesMethod = "createFastAllSame"; } else { - if (gitAttributesPolicyCreator == null) { - try { - Class clazz = Class.forName("com.diffplug.spotless.extra.GitAttributesLineEndings"); - Method method = clazz.getMethod("create", File.class, Supplier.class); - gitAttributesPolicyCreator = (proj, target) -> ThrowingEx.get(() -> (Policy) method.invoke(null, proj, target)); - } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) { - throw new IllegalStateException("LineEnding.GIT_ATTRIBUTES requires the spotless-lib-extra library, but it is not on the classpath", e); - } - } - // gitAttributesPolicyCreator will always be nonnull at this point - Policy policy = gitAttributesPolicyCreator.apply(projectDir, toFormat); - if (this == GIT_ATTRIBUTES) { - return policy; - } else if (this == GIT_ATTRIBUTES_FAST_ALLSAME) { - return new LazyAllTheSame(policy, toFormat); - } else { - throw new IllegalArgumentException("Unknown " + this); - } - } - } - - static class LazyAllTheSame extends LazyForwardingEquality implements Policy { - private transient Policy policy; - private transient Supplier> toFormat; - - public LazyAllTheSame(Policy policy, Supplier> toFormat) { - this.policy = policy; - this.toFormat = toFormat; - } - - @Override - protected String calculateState() throws Exception { - var files = toFormat.get().iterator(); - if (files.hasNext()) { - return policy.getEndingFor(files.next()); - } else { - return LineEnding.UNIX.str(); - } + return createPolicy(); } - - @Override - public String getEndingFor(File file) { - return state(); + try { + Class clazz = Class.forName("com.diffplug.spotless.extra.GitAttributesLineEndings"); + Method method = clazz.getMethod(gitAttributesMethod, File.class, Supplier.class); + return ThrowingEx.get(() -> (Policy) method.invoke(null, projectDir, toFormat)); + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) { + throw new IllegalStateException("LineEnding.GIT_ATTRIBUTES requires the spotless-lib-extra library, but it is not on the classpath", e); } } From d56acd51206381d68cc9112c16376d502478edd5 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 28 Sep 2023 15:47:24 -0700 Subject: [PATCH 4/7] Fix spotbugs. --- .../com/diffplug/spotless/extra/GitAttributesLineEndings.java | 1 + 1 file changed, 1 insertion(+) diff --git a/lib-extra/src/main/java/com/diffplug/spotless/extra/GitAttributesLineEndings.java b/lib-extra/src/main/java/com/diffplug/spotless/extra/GitAttributesLineEndings.java index c53e9a3e92..3be75637be 100644 --- a/lib-extra/src/main/java/com/diffplug/spotless/extra/GitAttributesLineEndings.java +++ b/lib-extra/src/main/java/com/diffplug/spotless/extra/GitAttributesLineEndings.java @@ -78,6 +78,7 @@ public static LineEnding.Policy createFastAllSame(File projectDir, Supplier implements LineEnding.Policy { + private static final long serialVersionUID = 727912266173243664L; transient File projectDir; transient Supplier> toFormat; From 0cfe58f1e9b7693cbddd177de95d8d1812d94cc0 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 28 Sep 2023 15:47:32 -0700 Subject: [PATCH 5/7] Update docs. --- plugin-gradle/README.md | 4 ++-- plugin-maven/README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md index 6543d21b79..d4537d4b11 100644 --- a/plugin-gradle/README.md +++ b/plugin-gradle/README.md @@ -1425,12 +1425,12 @@ spotless { encoding 'Cp1252' // except java, which will be Cp1252 ``` -Line endings can also be set globally or per-format using the `lineEndings` property. Spotless supports four line ending modes: `UNIX`, `WINDOWS`, `MAC_CLASSIC`, `PLATFORM_NATIVE`, and `GIT_ATTRIBUTES`. The default value is `GIT_ATTRIBUTES`, and *we highly recommend that you* ***do not change*** *this value*. Git has opinions about line endings, and if Spotless and git disagree, then you're going to have a bad time. +Line endings can also be set globally or per-format using the `lineEndings` property. Spotless supports four line ending modes: `UNIX`, `WINDOWS`, `MAC_CLASSIC`, `PLATFORM_NATIVE`, `GIT_ATTRIBUTES`, and `GIT_ATTRIBUTES_FAST_ALLSAME`. The default value is `GIT_ATTRIBUTES_FAST_ALLSAME`, and *we highly recommend that you* ***do not change*** *this value*. Git has opinions about line endings, and if Spotless and git disagree, then you're going to have a bad time. `FAST_ALLSAME` just means that Spotless can assume that every file being formatted has the same line endings ([more info](https://github.com/diffplug/spotless/pull/1838)). You can easily set the line endings of different files using [a `.gitattributes` file](https://help.github.com/articles/dealing-with-line-endings/). Here's an example `.gitattributes` which sets all files to unix newlines: `* text eol=lf`. - + ## Custom steps diff --git a/plugin-maven/README.md b/plugin-maven/README.md index 8942b6c059..919a05dfbb 100644 --- a/plugin-maven/README.md +++ b/plugin-maven/README.md @@ -1586,7 +1586,7 @@ Spotless uses UTF-8 by default, but you can use [any encoding which Java support ``` -Line endings can also be set globally or per-format using the `lineEndings` property. Spotless supports four line ending modes: `UNIX`, `WINDOWS`, `MAC_CLASSIC`, `PLATFORM_NATIVE`, and `GIT_ATTRIBUTES`. The default value is `GIT_ATTRIBUTES`, and *we highly recommend that you* ***do not change*** *this value*. Git has opinions about line endings, and if Spotless and git disagree, then you're going to have a bad time. +Line endings can also be set globally or per-format using the `lineEndings` property. Spotless supports four line ending modes: `UNIX`, `WINDOWS`, `MAC_CLASSIC`, `PLATFORM_NATIVE`, `GIT_ATTRIBUTES`, and `GIT_ATTRIBUTES_FAST_ALLSAME`. The default value is `GIT_ATTRIBUTES_FAST_ALLSAME`, and *we highly recommend that you* ***do not change*** *this value*. Git has opinions about line endings, and if Spotless and git disagree, then you're going to have a bad time. `FAST_ALLSAME` just means that Spotless can assume that every file being formatted has the same line endings ([more info](https://github.com/diffplug/spotless/pull/1838)). You can easily set the line endings of different files using [a `.gitattributes` file](https://help.github.com/articles/dealing-with-line-endings/). Here's an example `.gitattributes` which sets all files to unix newlines: `* text eol=lf`. From cdc0c7b3c9d85b358d7aba005e284122cbb925b2 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 28 Sep 2023 15:47:43 -0700 Subject: [PATCH 6/7] Update changelogs. --- CHANGES.md | 1 + plugin-gradle/CHANGES.md | 3 +++ plugin-maven/CHANGES.md | 3 +++ 3 files changed, 7 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 87eacf12a5..0fbb14f7ff 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -16,6 +16,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( the version accordingly. ([#1804](https://github.com/diffplug/spotless/issues/1804)). * Support for `google-java-format`'s `skip-javadoc-formatting` option. ([#1793](https://github.com/diffplug/spotless/pull/1793)) * Support configuration of mirrors for P2 repositories in maven DSL ([#1697](https://github.com/diffplug/spotless/issues/1697)). +* New line endings mode `GIT_ATTRIBUTES_FAST_ALLSAME`. ([#1838](https://github.com/diffplug/spotless/pull/1838)) ### Fixed * Fix support for plugins when using Prettier version `3.0.0` and newer. ([#1802](https://github.com/diffplug/spotless/pull/1802)) * Fix configuration cache issue around `external process started '/usr/bin/git --version'`. ([#1806](https://github.com/diffplug/spotless/issues/1806)) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 7a92efc514..536747a4ff 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -13,6 +13,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( * Fixed support for plugins when using Prettier version `3.0.0` and newer. ([#1802](https://github.com/diffplug/spotless/pull/1802)) ### Changes * Bump default `ktlint` version to latest `0.50.0` -> `1.0.0`. ([#1808](https://github.com/diffplug/spotless/pull/1808)) +* **POSSIBLY BREAKING** the default line endings are now `GIT_ATTRIBUTES_FAST_ALLSAME` instead of `GIT_ATTRIBUTES`. ([#1838](https://github.com/diffplug/spotless/pull/1838)) + * If all the files within a format have the same line endings, then there is no change in behavior. + * Fixes large performance regression. ([#1527](https://github.com/diffplug/spotless/issues/1527)) ## [6.21.0] - 2023-08-29 ### Added diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md index 1338d42ddb..a6c98e2c96 100644 --- a/plugin-maven/CHANGES.md +++ b/plugin-maven/CHANGES.md @@ -28,6 +28,9 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Changes * Bump default `flexmark` version to latest `0.64.0` -> `0.64.8`. ([#1801](https://github.com/diffplug/spotless/pull/1801)) * Bump default `ktlint` version to latest `0.50.0` -> `1.0.0`. ([#1808](https://github.com/diffplug/spotless/pull/1808)) +* **POSSIBLY BREAKING** the default line endings are now `GIT_ATTRIBUTES_FAST_ALLSAME` instead of `GIT_ATTRIBUTES`. ([#1838](https://github.com/diffplug/spotless/pull/1838)) + * If all the files within a format have the same line endings, then there is no change in behavior. + * Fixes large performance regression. ([#1527](https://github.com/diffplug/spotless/issues/1527)) ## [2.39.0] - 2023-08-29 ### Added From 1a1fbfef95ff001ad0d4b5d8cbf8a46bc76c8896 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 28 Sep 2023 16:23:55 -0700 Subject: [PATCH 7/7] Remove unused field. --- lib/src/main/java/com/diffplug/spotless/LineEnding.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/LineEnding.java b/lib/src/main/java/com/diffplug/spotless/LineEnding.java index 708699904c..74d9c80618 100644 --- a/lib/src/main/java/com/diffplug/spotless/LineEnding.java +++ b/lib/src/main/java/com/diffplug/spotless/LineEnding.java @@ -19,11 +19,8 @@ import java.io.Serializable; import java.lang.reflect.Method; import java.util.Objects; -import java.util.function.BiFunction; import java.util.function.Supplier; -import javax.annotation.Nullable; - /** * Represents the line endings which should be written by the tool. */ @@ -76,8 +73,6 @@ public Policy createPolicy(File projectDir, Supplier> toFormat) { } } - private static volatile @Nullable BiFunction>, Policy> gitAttributesPolicyCreator; - // @formatter:off /** Should use {@link #createPolicy(File, Supplier)} instead, but this will work iff its a path-independent LineEnding policy. */ public Policy createPolicy() { @@ -85,7 +80,7 @@ public Policy createPolicy() { case PLATFORM_NATIVE: return _platformNativePolicy; case WINDOWS: return WINDOWS_POLICY; case UNIX: return UNIX_POLICY; - case MAC_CLASSIC: return MAC_CLASSIC_POLICY; + case MAC_CLASSIC: return MAC_CLASSIC_POLICY; default: throw new UnsupportedOperationException(this + " is a path-specific line ending."); } } @@ -129,7 +124,7 @@ public String str() { case PLATFORM_NATIVE: return _platformNative; case WINDOWS: return "\r\n"; case UNIX: return "\n"; - case MAC_CLASSIC: return "\r"; + case MAC_CLASSIC: return "\r"; default: throw new UnsupportedOperationException(this + " is a path-specific line ending."); } }