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 LineEnding.PRESERVE policy #2304

Merged
merged 5 commits into from
Oct 20, 2024
Merged
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
* Support for line ending policy `PRESERVE` which just takes the first line ending of every given file as setting (no matter if `\n`, `\r\n` or `\r`) ([#2304](https://github.com/diffplug/spotless/pull/2304))
### Fixed
* `ktlint` steps now read from the `string` instead of the `file` so they don't clobber earlier steps. (fixes [#1599](https://github.com/diffplug/spotless/issues/1599))

Expand Down
52 changes: 50 additions & 2 deletions lib/src/main/java/com/diffplug/spotless/LineEnding.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 DiffPlug
* Copyright 2016-2024 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,8 +16,12 @@
package com.diffplug.spotless;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.function.Supplier;

Expand Down Expand Up @@ -49,7 +53,9 @@ public Policy createPolicy() {
/** {@code \n} */
UNIX,
/** {@code \r} */
MAC_CLASSIC;
MAC_CLASSIC,
/** preserve the line ending of the first line (no matter which format) */
PRESERVE;
// @formatter:on

/** Returns a {@link Policy} appropriate for files which are contained within the given rootFolder. */
Expand Down Expand Up @@ -81,6 +87,7 @@ public Policy createPolicy() {
case WINDOWS: return WINDOWS_POLICY;
case UNIX: return UNIX_POLICY;
case MAC_CLASSIC: return MAC_CLASSIC_POLICY;
case PRESERVE: return PRESERVE_POLICY;
default: throw new UnsupportedOperationException(this + " is a path-specific line ending.");
}
}
Expand All @@ -100,9 +107,50 @@ public String getEndingFor(File file) {
}
}

static class PreserveLineEndingPolicy extends NoLambda.EqualityBasedOnSerialization implements Policy {
private static final long serialVersionUID = 2L;

@Override
public String getEndingFor(File file) {
// assume US-ASCII encoding (only line ending characters need to be decoded anyways)
try (Reader reader = new FileReader(file, StandardCharsets.US_ASCII)) {
return getEndingFor(reader);
} catch (IOException e) {
throw new IllegalArgumentException("Could not determine line ending of file: " + file, e);
}
}

static String getEndingFor(Reader reader) throws IOException {
char previousCharacter = 0;
char currentCharacter = 0;
int readResult;
while ((readResult = reader.read()) != -1) {
currentCharacter = (char)readResult;
if (currentCharacter == '\n') {
if (previousCharacter == '\r') {
return WINDOWS.str();
} else {
return UNIX.str();
}
} else {
if (previousCharacter == '\r') {
return MAC_CLASSIC.str();
}
}
previousCharacter = currentCharacter;
}
if (previousCharacter == '\r') {
return MAC_CLASSIC.str();
}
// assume UNIX line endings if no line ending was found
return UNIX.str();
}
}

private static final Policy WINDOWS_POLICY = new ConstantLineEndingPolicy(WINDOWS.str());
private static final Policy UNIX_POLICY = new ConstantLineEndingPolicy(UNIX.str());
private static final Policy MAC_CLASSIC_POLICY = new ConstantLineEndingPolicy(MAC_CLASSIC.str());
private static final Policy PRESERVE_POLICY = new PreserveLineEndingPolicy();
private static final String _platformNative = System.getProperty("line.separator");
private static final Policy _platformNativePolicy = new ConstantLineEndingPolicy(_platformNative);
private static final boolean nativeIsWin = _platformNative.equals(WINDOWS.str());
Expand Down
50 changes: 50 additions & 0 deletions lib/src/test/java/com/diffplug/spotless/LineEndingTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2024 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;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class LineEndingTest {

@Test
void testGetEndingFor() throws IOException {
assertLineEnding("\r", "\r");
assertLineEnding("\r", "Test\r");
assertLineEnding("\r", "Test\rTest2\n");

assertLineEnding("\n", "Test");

assertLineEnding("\r\n", "\r\n");
assertLineEnding("\r\n", "Test\r\n");
assertLineEnding("\r\n", "Test\r\nTest2\n");

assertLineEnding("\n", "\n");
assertLineEnding("\n", "Test\n");
assertLineEnding("\n", "Test\nTest2\r");
assertLineEnding("\n", "\n\t");
}

static void assertLineEnding(String ending, String input) throws IOException {
try (Reader reader = new StringReader(input)) {
Assertions.assertEquals(ending, LineEnding.PreserveLineEndingPolicy.getEndingFor(reader));
}
}
}
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
* Support for line ending policy `PRESERVE` which just takes the first line ending of every given file as setting (no matter if `\n`, `\r\n` or `\r`) ([#2304](https://github.com/diffplug/spotless/pull/2304))
### Fixed
* `ktlint` steps now read from the `string` instead of the `file` so they don't clobber earlier steps. (fixes [#1599](https://github.com/diffplug/spotless/issues/1599))

Expand Down
8 changes: 7 additions & 1 deletion plugin-gradle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1577,7 +1577,13 @@ 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`, `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)).
Line endings can also be set globally or per-format using the `lineEndings` property. Spotless supports

- constant modes (`UNIX`, `WINDOWS`, `MAC_CLASSIC`)
- simple modes (`PLATFORM_NATIVE`, `PRESERVE`)
- and git-aware modes (`GIT_ATTRIBUTES`, `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`.

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
* Support for line ending policy `PRESERVE` which just takes the first line ending of every given file as setting (no matter if `\n`, `\r\n` or `\r`) ([#2304](https://github.com/diffplug/spotless/pull/2304))
### Fixed
* `ktlint` steps now read from the `string` instead of the `file` so they don't clobber earlier steps. (fixes [#1599](https://github.com/diffplug/spotless/issues/1599))

Expand Down
10 changes: 8 additions & 2 deletions plugin-maven/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1784,9 +1784,15 @@ Spotless uses UTF-8 by default, but you can use [any encoding which Java support
</configuration>
```

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)).
Line endings can also be set globally or per-format using the `lineEndings` property. Spotless supports

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`.
- constant modes (`UNIX`, `WINDOWS`, `MAC_CLASSIC`)
- simple modes (`PLATFORM_NATIVE`, `PRESERVE`)
- and git-aware modes (`GIT_ATTRIBUTES`, `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`.

<a name="enforceCheck"></a>

Expand Down
Loading