Skip to content

Commit b618560

Browse files
ParkerMphilwebb
authored andcommitted
Detect and preserve line separators
Update `Formatter` so that line endings are now detected based on the contents of the file. This mirrors the behavior of the Eclipse plugin when used in the IDE, specifically the `nextDelimiterInfo` method in `org.eclipse.jface.text.DefaultLineTracker`. See gh-340
1 parent b543266 commit b618560

File tree

12 files changed

+86
-5
lines changed

12 files changed

+86
-5
lines changed

spring-javaformat-eclipse/io.spring.javaformat.eclipse.tests/src/io/spring/javaformat/eclipse/projectsettings/ProjectSettingsFilesTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ void applyToProjectWithFileMergesToDotSettings() throws Exception {
9595
}).given(projectFile).setContents((InputStream) any(), anyInt(), any());
9696
files.applyToProject(project, monitor);
9797
verify(projectFile).setContents((InputStream) any(), eq(1), eq(monitor));
98-
assertThat(out.toString(StandardCharsets.UTF_8)).isEqualTo("a=b\ny=z\n");
98+
assertThat(out.toString(StandardCharsets.UTF_8))
99+
.isEqualToNormalizingNewlines("a=b\ny=z\n");
99100
}
100101

101102
private ProjectSettingsFile createPrefsFile() throws IOException {

spring-javaformat-gradle/spring-javaformat-gradle-plugin/src/test/java/io/spring/javaformat/gradle/CheckTaskTests.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import java.nio.file.StandardCopyOption;
2424
import java.nio.file.StandardOpenOption;
2525
import java.util.Arrays;
26-
import java.util.Collections;
26+
import java.util.List;
2727
import java.util.stream.Stream;
2828

2929
import org.gradle.testkit.runner.BuildResult;
@@ -72,8 +72,8 @@ void whenFirstInvocationSucceedsAndSourceIsModifiedThenSecondInvocationSucceeds(
7272
GradleBuild gradleBuild = this.gradleBuild.source(this.temp);
7373
BuildResult result = gradleBuild.build("check");
7474
assertThat(result.task(":checkFormatMain").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
75-
Files.write(new File(this.temp, "src/main/java/simple/Simple.java").toPath(),
76-
Collections.singletonList("// A change to the file"), StandardOpenOption.APPEND);
75+
appendToFileNormalizingNewlines(new File(this.temp, "src/main/java/simple/Simple.java").toPath(),
76+
"// A change to the file");
7777
result = gradleBuild.build("--debug", "check");
7878
assertThat(result.task(":checkFormatMain").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
7979
}
@@ -146,4 +146,14 @@ private void copyFolder(Path source, Path target) throws IOException {
146146
}
147147
}
148148

149+
/**
150+
* Uses a read/modify/truncate approach to append a line to a file.
151+
* This avoids issues where the standard append option results in mixed line-endings.
152+
*/
153+
private void appendToFileNormalizingNewlines(Path sourceFilePath, String lineToAppend) throws IOException {
154+
List<String> lines = Files.readAllLines(sourceFilePath);
155+
lines.add(lineToAppend);
156+
Files.write(sourceFilePath, lines, StandardOpenOption.TRUNCATE_EXISTING);
157+
}
158+
149159
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Test resources that need a predictable eol
2+
apply*/src/main/java/simple/Simple.java eol=lf

spring-javaformat-maven/spring-javaformat-maven-plugin/src/test/java/io/spring/format/maven/VerifyApply.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
*/
3131
public class VerifyApply {
3232

33-
private static final String LF = System.lineSeparator();
33+
private static final String LF = "\n";
3434

3535
private static final String JAVA_FILE = "src/main/java/simple/Simple.java";
3636

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Test resources that need specific eol
2+
**/correct-crlf.txt eol=crlf
3+
**/correct-cr.txt eol=cr
4+
**/correct-lf.txt eol=lf

spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/expected/correct-cr.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package correct;public class CorrectCr { public static void main(String[] args) throws Exception { // FIXME }}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package correct;
2+
3+
public class CorrectCrlf {
4+
5+
public static void main(String[] args) throws Exception {
6+
// FIXME
7+
}
8+
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package correct;
2+
3+
public class CorrectLf {
4+
5+
public static void main(String[] args) throws Exception {
6+
// FIXME
7+
}
8+
9+
}

spring-javaformat/spring-javaformat-formatter-tests/src/test/resources/source/correct-cr.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package correct;public class CorrectCr { public static void main(String[] args) throws Exception { // FIXME }}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package correct;
2+
3+
public class CorrectCrlf {
4+
5+
public static void main(String[] args) throws Exception {
6+
// FIXME
7+
}
8+
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package correct;
2+
3+
public class CorrectLf {
4+
5+
public static void main(String[] args) throws Exception {
6+
// FIXME
7+
}
8+
9+
}

spring-javaformat/spring-javaformat-formatter/src/main/java/io/spring/javaformat/formatter/Formatter.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package io.spring.javaformat.formatter;
1818

1919
import java.util.Map;
20+
import java.util.regex.Matcher;
21+
import java.util.regex.Pattern;
2022

2123
import org.eclipse.jface.text.IRegion;
2224
import org.eclipse.text.edits.TextEdit;
@@ -56,6 +58,11 @@ public class Formatter {
5658
*/
5759
private static final int DEFAULT_INDENTATION_LEVEL = 0;
5860

61+
/**
62+
* Pattern that matches all line separators into named-capturing group "sep".
63+
*/
64+
private static final Pattern LINE_SEPARATOR_PATTERN = Pattern.compile("(?<sep>(\r\n|\r|\n))");
65+
5966
/**
6067
* The default line separator.
6168
*/
@@ -123,6 +130,9 @@ public TextEdit format(String source, int offset, int length, String lineSeparat
123130

124131
public TextEdit format(int kind, String source, int offset, int length, int indentationLevel,
125132
String lineSeparator) {
133+
if (lineSeparator == null) {
134+
lineSeparator = detectLineSeparator(source);
135+
}
126136
return this.delegate.format(kind, source, offset, length, indentationLevel, lineSeparator);
127137
}
128138

@@ -148,6 +158,9 @@ public TextEdit format(String source, IRegion[] regions, String lineSeparator) {
148158
}
149159

150160
public TextEdit format(int kind, String source, IRegion[] regions, int indentationLevel, String lineSeparator) {
161+
if (lineSeparator == null) {
162+
lineSeparator = detectLineSeparator(source);
163+
}
151164
return this.delegate.format(kind, source, regions, indentationLevel, lineSeparator);
152165
}
153166

@@ -159,4 +172,17 @@ public void setOptions(Map<String, String> options) {
159172
this.delegate.setOptions(options);
160173
}
161174

175+
private String detectLineSeparator(String contents) {
176+
Matcher matcher = LINE_SEPARATOR_PATTERN.matcher(contents);
177+
if (!matcher.find()) {
178+
return DEFAULT_LINE_SEPARATOR;
179+
}
180+
String firstMatch = matcher.group("sep");
181+
while (matcher.find()) {
182+
if (!matcher.group("sep").equals(firstMatch)) {
183+
return DEFAULT_LINE_SEPARATOR;
184+
}
185+
}
186+
return firstMatch;
187+
}
162188
}

0 commit comments

Comments
 (0)