|
18 | 18 | import java.io.ByteArrayOutputStream; |
19 | 19 | import java.io.File; |
20 | 20 | import java.io.IOException; |
21 | | -import java.nio.charset.Charset; |
22 | 21 | import java.nio.charset.StandardCharsets; |
23 | 22 | import java.nio.file.Files; |
24 | 23 | import java.util.List; |
25 | 24 | import java.util.ListIterator; |
26 | 25 | import java.util.Objects; |
27 | 26 |
|
| 27 | +import org.eclipse.jgit.diff.DiffFormatter; |
28 | 28 | import org.eclipse.jgit.diff.EditList; |
29 | | -import org.eclipse.jgit.diff.HistogramDiff; |
| 29 | +import org.eclipse.jgit.diff.MyersDiff; |
30 | 30 | import org.eclipse.jgit.diff.RawText; |
31 | 31 | import org.eclipse.jgit.diff.RawTextComparator; |
32 | 32 |
|
| 33 | +import com.diffplug.common.base.CharMatcher; |
33 | 34 | import com.diffplug.common.base.Errors; |
34 | 35 | import com.diffplug.common.base.Preconditions; |
35 | 36 | import com.diffplug.common.base.Splitter; |
@@ -138,10 +139,7 @@ private void addFile(String arg) { |
138 | 139 |
|
139 | 140 | // then we'll print the rest that can fit |
140 | 141 | ListIterator<String> iter = lines.listIterator(Math.min(MIN_LINES_PER_FILE, lines.size())); |
141 | | - // lines.size() - iter.nextIndex() == 1 means "just one line left", and we just print the line |
142 | | - // instead of "1 more lines that didn't fit" |
143 | | - while (iter.hasNext() && |
144 | | - (numLines < MAX_CHECK_MESSAGE_LINES || lines.size() - iter.nextIndex() == 1)) { |
| 142 | + while (iter.hasNext() && numLines < MAX_CHECK_MESSAGE_LINES) { |
145 | 143 | addIntendedLine(DIFF_INDENT, iter.next()); |
146 | 144 | } |
147 | 145 |
|
@@ -170,30 +168,70 @@ private void addIntendedLine(String indent, String line) { |
170 | 168 | * sequence (\n, \r, \r\n). |
171 | 169 | */ |
172 | 170 | private static String diff(Builder builder, File file) throws IOException { |
173 | | - Charset encoding = builder.formatter.getEncoding(); |
174 | | - String raw = new String(Files.readAllBytes(file.toPath()), encoding); |
| 171 | + String raw = new String(Files.readAllBytes(file.toPath()), builder.formatter.getEncoding()); |
175 | 172 | String rawUnix = LineEnding.toUnix(raw); |
176 | 173 | String formattedUnix; |
177 | 174 | if (builder.isPaddedCell) { |
178 | 175 | formattedUnix = PaddedCell.check(builder.formatter, file, rawUnix).canonical(); |
179 | 176 | } else { |
180 | 177 | formattedUnix = builder.formatter.compute(rawUnix, file); |
181 | 178 | } |
182 | | - String formatted = builder.formatter.computeLineEndings(formattedUnix, file); |
183 | | - return visualizeDiff(raw, formatted); |
| 179 | + |
| 180 | + if (rawUnix.equals(formattedUnix)) { |
| 181 | + // the formatting is fine, so it's a line-ending issue |
| 182 | + String formatted = builder.formatter.computeLineEndings(formattedUnix, file); |
| 183 | + return diffWhitespaceLineEndings(raw, formatted, false, true); |
| 184 | + } else { |
| 185 | + return diffWhitespaceLineEndings(rawUnix, formattedUnix, true, false); |
| 186 | + } |
184 | 187 | } |
185 | 188 |
|
186 | | - private static String visualizeDiff(String raw, String formattedBytes) throws IOException { |
187 | | - RawText a = new RawText(raw.getBytes(StandardCharsets.UTF_8)); |
188 | | - RawText b = new RawText(formattedBytes.getBytes(StandardCharsets.UTF_8)); |
189 | | - EditList edits = new HistogramDiff().diff(RawTextComparator.DEFAULT, a, b); |
| 189 | + /** |
| 190 | + * Returns a git-style diff between the two unix strings. |
| 191 | + * |
| 192 | + * Output has no trailing newlines. |
| 193 | + * |
| 194 | + * Boolean args determine whether whitespace or line endings will be visible. |
| 195 | + */ |
| 196 | + private static String diffWhitespaceLineEndings(String dirty, String clean, boolean whitespace, boolean lineEndings) throws IOException { |
| 197 | + dirty = visibleWhitespaceLineEndings(dirty, whitespace, lineEndings); |
| 198 | + clean = visibleWhitespaceLineEndings(clean, whitespace, lineEndings); |
| 199 | + |
| 200 | + RawText a = new RawText(dirty.getBytes(StandardCharsets.UTF_8)); |
| 201 | + RawText b = new RawText(clean.getBytes(StandardCharsets.UTF_8)); |
| 202 | + EditList edits = new EditList(); |
| 203 | + edits.addAll(MyersDiff.INSTANCE.diff(RawTextComparator.DEFAULT, a, b)); |
| 204 | + |
190 | 205 | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
191 | | - // defaultCharset is here so the formatter could select "fancy" or "simple" |
192 | | - // characters for whitespace visualization based on the capabilities of the console |
193 | | - // For instance, if the app is running with file.encoding=ISO-8859-1, then |
194 | | - // the console can't encode fancy whitespace characters, and the formatter would |
195 | | - // resort to simple "\\r", "\\n", and so on |
196 | | - new WriteSpaceAwareDiffFormatter(out, Charset.defaultCharset()).format(edits, a, b); |
197 | | - return new String(out.toByteArray(), StandardCharsets.UTF_8); |
| 206 | + try (DiffFormatter formatter = new DiffFormatter(out)) { |
| 207 | + formatter.format(edits, a, b); |
| 208 | + } |
| 209 | + String formatted = out.toString(StandardCharsets.UTF_8.name()); |
| 210 | + |
| 211 | + // we don't need the diff to show this, since we display newlines ourselves |
| 212 | + formatted = formatted.replace("\\ No newline at end of file\n", ""); |
| 213 | + return NEWLINE_MATCHER.trimTrailingFrom(formatted); |
198 | 214 | } |
| 215 | + |
| 216 | + private static final CharMatcher NEWLINE_MATCHER = CharMatcher.is('\n'); |
| 217 | + |
| 218 | + /** |
| 219 | + * Makes the whitespace and/or the lineEndings visible. |
| 220 | + * |
| 221 | + * MyersDiff wants inputs with only unix line endings. So this ensures that that is the case. |
| 222 | + */ |
| 223 | + private static String visibleWhitespaceLineEndings(String input, boolean whitespace, boolean lineEndings) { |
| 224 | + if (whitespace) { |
| 225 | + input = input.replace(' ', MIDDLE_DOT).replace("\t", "\\t"); |
| 226 | + } |
| 227 | + if (lineEndings) { |
| 228 | + input = input.replace("\n", "\\n\n").replace("\r", "\\r"); |
| 229 | + } else { |
| 230 | + // we want only \n, so if we didn't replace them above, we'll replace them here. |
| 231 | + input = input.replace("\r", ""); |
| 232 | + } |
| 233 | + return input; |
| 234 | + } |
| 235 | + |
| 236 | + private static final char MIDDLE_DOT = '\u00b7'; |
199 | 237 | } |
0 commit comments