Skip to content

Commit b307c97

Browse files
authored
Add LineEnding.PRESERVE policy (#2304)
2 parents 4d35c32 + 71c61e0 commit b307c97

File tree

7 files changed

+121
-5
lines changed

7 files changed

+121
-5
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ This document is intended for Spotless developers.
1010
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
1111

1212
## [Unreleased]
13+
### Added
14+
* 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))
1315
### Fixed
1416
* `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))
1517

lib/src/main/java/com/diffplug/spotless/LineEnding.java

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2023 DiffPlug
2+
* Copyright 2016-2024 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,8 +16,12 @@
1616
package com.diffplug.spotless;
1717

1818
import java.io.File;
19+
import java.io.FileReader;
20+
import java.io.IOException;
21+
import java.io.Reader;
1922
import java.io.Serializable;
2023
import java.lang.reflect.Method;
24+
import java.nio.charset.StandardCharsets;
2125
import java.util.Objects;
2226
import java.util.function.Supplier;
2327

@@ -49,7 +53,9 @@ public Policy createPolicy() {
4953
/** {@code \n} */
5054
UNIX,
5155
/** {@code \r} */
52-
MAC_CLASSIC;
56+
MAC_CLASSIC,
57+
/** preserve the line ending of the first line (no matter which format) */
58+
PRESERVE;
5359
// @formatter:on
5460

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

110+
static class PreserveLineEndingPolicy extends NoLambda.EqualityBasedOnSerialization implements Policy {
111+
private static final long serialVersionUID = 2L;
112+
113+
@Override
114+
public String getEndingFor(File file) {
115+
// assume US-ASCII encoding (only line ending characters need to be decoded anyways)
116+
try (Reader reader = new FileReader(file, StandardCharsets.US_ASCII)) {
117+
return getEndingFor(reader);
118+
} catch (IOException e) {
119+
throw new IllegalArgumentException("Could not determine line ending of file: " + file, e);
120+
}
121+
}
122+
123+
static String getEndingFor(Reader reader) throws IOException {
124+
char previousCharacter = 0;
125+
char currentCharacter = 0;
126+
int readResult;
127+
while ((readResult = reader.read()) != -1) {
128+
currentCharacter = (char)readResult;
129+
if (currentCharacter == '\n') {
130+
if (previousCharacter == '\r') {
131+
return WINDOWS.str();
132+
} else {
133+
return UNIX.str();
134+
}
135+
} else {
136+
if (previousCharacter == '\r') {
137+
return MAC_CLASSIC.str();
138+
}
139+
}
140+
previousCharacter = currentCharacter;
141+
}
142+
if (previousCharacter == '\r') {
143+
return MAC_CLASSIC.str();
144+
}
145+
// assume UNIX line endings if no line ending was found
146+
return UNIX.str();
147+
}
148+
}
149+
103150
private static final Policy WINDOWS_POLICY = new ConstantLineEndingPolicy(WINDOWS.str());
104151
private static final Policy UNIX_POLICY = new ConstantLineEndingPolicy(UNIX.str());
105152
private static final Policy MAC_CLASSIC_POLICY = new ConstantLineEndingPolicy(MAC_CLASSIC.str());
153+
private static final Policy PRESERVE_POLICY = new PreserveLineEndingPolicy();
106154
private static final String _platformNative = System.getProperty("line.separator");
107155
private static final Policy _platformNativePolicy = new ConstantLineEndingPolicy(_platformNative);
108156
private static final boolean nativeIsWin = _platformNative.equals(WINDOWS.str());
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2024 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;
17+
18+
import java.io.IOException;
19+
import java.io.Reader;
20+
import java.io.StringReader;
21+
22+
import org.junit.jupiter.api.Assertions;
23+
import org.junit.jupiter.api.Test;
24+
25+
class LineEndingTest {
26+
27+
@Test
28+
void testGetEndingFor() throws IOException {
29+
assertLineEnding("\r", "\r");
30+
assertLineEnding("\r", "Test\r");
31+
assertLineEnding("\r", "Test\rTest2\n");
32+
33+
assertLineEnding("\n", "Test");
34+
35+
assertLineEnding("\r\n", "\r\n");
36+
assertLineEnding("\r\n", "Test\r\n");
37+
assertLineEnding("\r\n", "Test\r\nTest2\n");
38+
39+
assertLineEnding("\n", "\n");
40+
assertLineEnding("\n", "Test\n");
41+
assertLineEnding("\n", "Test\nTest2\r");
42+
assertLineEnding("\n", "\n\t");
43+
}
44+
45+
static void assertLineEnding(String ending, String input) throws IOException {
46+
try (Reader reader = new StringReader(input)) {
47+
Assertions.assertEquals(ending, LineEnding.PreserveLineEndingPolicy.getEndingFor(reader));
48+
}
49+
}
50+
}

plugin-gradle/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`).
44

55
## [Unreleased]
6+
### Added
7+
* 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))
68
### Fixed
79
* `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))
810

plugin-gradle/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1577,7 +1577,13 @@ spotless {
15771577
encoding 'Cp1252' // except java, which will be Cp1252
15781578
```
15791579

1580-
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)).
1580+
Line endings can also be set globally or per-format using the `lineEndings` property. Spotless supports
1581+
1582+
- constant modes (`UNIX`, `WINDOWS`, `MAC_CLASSIC`)
1583+
- simple modes (`PLATFORM_NATIVE`, `PRESERVE`)
1584+
- and git-aware modes (`GIT_ATTRIBUTES`, `GIT_ATTRIBUTES_FAST_ALLSAME`)
1585+
1586+
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)).
15811587
15821588
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`.
15831589

plugin-maven/CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
44

55
## [Unreleased]
6+
### Added
7+
* 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))
68
### Fixed
79
* `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))
810

plugin-maven/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,9 +1784,15 @@ Spotless uses UTF-8 by default, but you can use [any encoding which Java support
17841784
</configuration>
17851785
```
17861786

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

1789-
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`.
1789+
- constant modes (`UNIX`, `WINDOWS`, `MAC_CLASSIC`)
1790+
- simple modes (`PLATFORM_NATIVE`, `PRESERVE`)
1791+
- and git-aware modes (`GIT_ATTRIBUTES`, `GIT_ATTRIBUTES_FAST_ALLSAME`)
1792+
1793+
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)).
1794+
1795+
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`.
17901796

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

0 commit comments

Comments
 (0)