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 diff tool #89

Merged
merged 9 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ ext {
projects.snapshotTestsXml,
projects.snapshotTestsJaxbJakarta,
projects.snapshotTestsNormalize,
projects.snapshotTestsDirectoryParams
projects.snapshotTestsDirectoryParams,
projects.diffTool
);
}

96 changes: 96 additions & 0 deletions diff-tool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Diff-Tool

This is a thin wrapper around `java-diff-utils` which provides the convenience of rendering diffs to a nice human
readable String.

## Usage

```java
final String expected = "...";
final String actual = "...";

// Create a diff using default configuration
final StringDiff diff1 = StringDiff.simple(expected, actual);

// Render as unified diff
final String unifiedDiff = diff1.toString();
// Render as split diff
final String splitDiff = diff1.toString(SplitDiffRenderer.INSTANCE);

// Test whether compared Strings differed in line separators
final boolean lineSeparatorDifference = diff1.hasLineSeparatorDifference()
// Tests whether compared Strings differed in actual text
final boolean textDifference = diff1.hasTextDifference()

// Create a diff by configuring the java-diff-utils _DiffRowGenerator_
final DiffAlgorithm diffAlgorithm = DiffUtilsDiffAlgorithm.create(builder -> builder
.showInlineDiffs(true)
.inlineDiffByWord(true)
.ignoreWhiteSpaces(true));

final StringDiff diff2 = StringDiff.using(diffAlgorithm, expected, actual);
```

## Sample output

Unified Diff:
```
[...]
6 6 Some unchanged lines6
7 7 Some unchanged lines7
8 8 Some unchanged lines8
9 - This is a test <<senctence>>.
9 + This is a test <<for diffutils>>.
10 10 This is the second line.
11 11 Some unchanged lines9
12 12 Some unchanged lines10
13 13 Some unchanged lines11
14 14 Some unchanged lines12
15 - And here is the finish with way more than 80 characters and I'm very curious how this is going to be displayed in split view diff.
16 15 This line is unchanged
16 + This line has been added
17 17 Some unchanged lines13
18 18 Some unchanged lines14
19 19 Some unchanged lines15
[...]
[...]
22 22 Some unchanged lines18
23 23 Some unchanged lines19
24 24 Some unchanged lines20
25 - <<Another>> <<difference>>
25 + <<This>> <<has changed>>
26 26 Some unchanged lines21
27 27 Some unchanged lines22
28 28 Some unchanged lines23
[...]
```

Split Diff
```
[...]
6 Some unchanged lines6 | 6 Some unchanged lines6
7 Some unchanged lines7 | 7 Some unchanged lines7
8 Some unchanged lines8 | 8 Some unchanged lines8
9 ! This is a test <<senctence>>. | 9 ! This is a test <<for diffutils>>.
10 This is the second line. | 10 This is the second line.
11 Some unchanged lines9 | 11 Some unchanged lines9
12 Some unchanged lines10 | 12 Some unchanged lines10
13 Some unchanged lines11 | 13 Some unchanged lines11
14 Some unchanged lines12 | 14 Some unchanged lines12
15 - And here is the finish with way more than 80 characters and I'm very curious how this is going to be displayed in split view diff. |
16 This line is unchanged | 15 This line is unchanged
| 16 + This line has been added
17 Some unchanged lines13 | 17 Some unchanged lines13
18 Some unchanged lines14 | 18 Some unchanged lines14
19 Some unchanged lines15 | 19 Some unchanged lines15
[...]
[...]
22 Some unchanged lines18 | 22 Some unchanged lines18
23 Some unchanged lines19 | 23 Some unchanged lines19
24 Some unchanged lines20 | 24 Some unchanged lines20
25 ! <<Another>> <<difference>> | 25 ! <<This>> <<has changed>>
26 Some unchanged lines21 | 26 Some unchanged lines21
27 Some unchanged lines22 | 27 Some unchanged lines22
28 Some unchanged lines23 | 28 Some unchanged lines23
[...]
```
9 changes: 9 additions & 0 deletions diff-tool/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
plugins {
id("snapshot-tests.published-java-component")
}
description = "Diff Tool"
ext.automaticModuleName = "de.skuzzle.test.snapshots.difftool"

dependencies {
api(libs.javadiffutils)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package de.skuzzle.difftool;

import java.util.List;

public interface DiffAlgorithm {

List<DiffLine> diffOf(List<String> left, List<String> right);
}
55 changes: 55 additions & 0 deletions diff-tool/src/main/java/de/skuzzle/difftool/DiffLine.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package de.skuzzle.difftool;

import java.util.Objects;

public final class DiffLine {

private final Type type;
private final String oldLine;
private final String newLine;

public DiffLine(Type type, String oldLine, String newLine) {
this.type = Objects.requireNonNull(type);
this.oldLine = Objects.requireNonNull(oldLine);
this.newLine = Objects.requireNonNull(newLine);
}

public enum Type {
INSERT,
DELETE,
CHANGE,
EQUAL
}

public Type type() {
return type;
}

public String oldLine() {
return oldLine;
}

public String newLine() {
return newLine;
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final DiffLine diffLine = (DiffLine) o;
return type == diffLine.type && oldLine.equals(diffLine.oldLine) && newLine.equals(diffLine.newLine);
}

@Override
public int hashCode() {
return Objects.hash(type, oldLine, newLine);
}

@Override
public String toString() {
return "[" + this.type + "," + this.oldLine + "," + this.newLine + "]";
}
}
18 changes: 18 additions & 0 deletions diff-tool/src/main/java/de/skuzzle/difftool/DiffRenderer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package de.skuzzle.difftool;

import java.util.List;

/**
* Defines how a diff is rendered as a String.
*/
public interface DiffRenderer {

/**
* Creates a String representation of the provided diff.
*
* @param rows The diff.
* @param diffSettings Additional parameters.
* @return The String representation.
*/
String renderDiff(List<DiffLine> rows, DiffSettings diffSettings);
}
82 changes: 82 additions & 0 deletions diff-tool/src/main/java/de/skuzzle/difftool/DiffSettings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package de.skuzzle.difftool;

/**
* Defines parameters that are used when rendering a diff to String using a
* {@link DiffRenderer}.
*/
public final class DiffSettings {

public static final DiffSettings DEFAULT = new DiffSettings(5, 0, DiffSymbols.DEFAULT);

private final int contextLines;
private final int lineNumberOffset;
private final DiffSymbols symbols;

public DiffSettings(int contextLines, int lineNumberOffset, DiffSymbols symbols) {
this.contextLines = contextLines;
this.lineNumberOffset = lineNumberOffset;
this.symbols = symbols;
}

public static DiffSettings withDefaultSymbols(int contextLines, int lineNumberOffset) {
return new DiffSettings(contextLines, lineNumberOffset, DiffSymbols.DEFAULT);
}

public int contextLines() {
return contextLines;
}

public int lineNumberOffset() {
return lineNumberOffset;
}

public DiffSymbols symbols() {
return symbols;
}

public static final class DiffSymbols {

public static DiffSymbols DEFAULT = new DiffSymbols("!", "+", "-", " ", LineSeparator.SYSTEM, "[...]");

private final String changedLine;
private final String addedLine;
private final String deletedLine;
private final String equalLine;
private final LineSeparator newLineCharacter;
private final String continuation;

public DiffSymbols(String changedLine, String addedLine, String deletedLine, String equalLine,
LineSeparator newLineCharacter, String continuation) {
this.changedLine = changedLine;
this.addedLine = addedLine;
this.deletedLine = deletedLine;
this.equalLine = equalLine;
this.newLineCharacter = newLineCharacter;
this.continuation = continuation;
}

public String changedLine() {
return changedLine;
}

public String addedLine() {
return addedLine;
}

public String deletedLine() {
return deletedLine;
}

public String equalLine() {
return equalLine;
}

public LineSeparator newLineCharacter() {
return newLineCharacter;
}

public String continuation() {
return continuation;
}
}
}
67 changes: 67 additions & 0 deletions diff-tool/src/main/java/de/skuzzle/difftool/GitLineSeparator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package de.skuzzle.difftool;

import java.util.Locale;

final class GitLineSeparator {

static final LineSeparator GIT_LINE_SEPARATOR = determineGitLineSeparator(GitConfig.DEFAULT);

static LineSeparator determineGitLineSeparator(GitConfig gitConfig) {
final String autocrlf = gitConfig.autocrlf();
if (autocrlf == null) {
return LineSeparator.SYSTEM;
}
switch (autocrlf.toLowerCase(Locale.ROOT)) {
case "true":
return LineSeparator.CRLF;
case "input":
return LineSeparator.LF;
case "false":
final String eol = gitConfig.eol();
if (eol == null) {
return LineSeparator.SYSTEM;
}
switch (eol.toLowerCase(Locale.ROOT)) {
case "crlf":
return LineSeparator.CRLF;
case "lf":
return LineSeparator.LF;
case "native":
default:
return LineSeparator.SYSTEM;
}
}
return LineSeparator.SYSTEM;
}

static class GitConfig {

static final GitConfig DEFAULT = new GitConfig();

String autocrlf() {
return execute("git config core.autocrlf");
}

String eol() {
return execute("git config core.eol");
}

static String execute(String command) {
try {
final Process exec = Runtime.getRuntime().exec(command);
try (var err = exec.getErrorStream()) {
err.readAllBytes();
}
try (var in = exec.getInputStream()) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11% of developers fix this issue

RESOURCE_LEAK: resource of type java.lang.Process acquired by call to exec(...) at line 51 is not released after line 55.


ℹ️ Expand to see all @sonatype-lift commands

You can reply with the following commands. For example, reply with @sonatype-lift ignoreall to leave out all findings.

Command Usage
@sonatype-lift ignore Leave out the above finding from this PR
@sonatype-lift ignoreall Leave out all the existing findings from this PR
@sonatype-lift exclude <file|issue|path|tool> Exclude specified file|issue|path|tool from Lift findings by updating your config.toml file

Note: When talking to LiftBot, you need to refresh the page to see its response.
Click here to add LiftBot to another repo.


Help us improve LIFT! (Sonatype LiftBot external survey)

Was this a good recommendation for you? Answering this survey will not impact your Lift settings.

[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]

return new String(in.readAllBytes());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

16% of developers fix this issue

DefaultCharset: Implicit use of the platform default charset, which can result in differing behaviour between JVM executions or incorrect behavior if the encoding of the data source doesn't match expectations.


Suggested change
return new String(in.readAllBytes());
return new String(in.readAllBytes(), Charset.defaultCharset());

ℹ️ Expand to see all @sonatype-lift commands

You can reply with the following commands. For example, reply with @sonatype-lift ignoreall to leave out all findings.

Command Usage
@sonatype-lift ignore Leave out the above finding from this PR
@sonatype-lift ignoreall Leave out all the existing findings from this PR
@sonatype-lift exclude <file|issue|path|tool> Exclude specified file|issue|path|tool from Lift findings by updating your config.toml file

Note: When talking to LiftBot, you need to refresh the page to see its response.
Click here to add LiftBot to another repo.


Help us improve LIFT! (Sonatype LiftBot external survey)

Was this a good recommendation for you? Answering this survey will not impact your Lift settings.

[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]

}
} catch (Exception e) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11% of developers fix this issue

RESOURCE_LEAK: resource of type java.lang.Process acquired to exec by call to exec(...) at line 51 is not released after line 58.
Note: potential exception at line 54


ℹ️ Expand to see all @sonatype-lift commands

You can reply with the following commands. For example, reply with @sonatype-lift ignoreall to leave out all findings.

Command Usage
@sonatype-lift ignore Leave out the above finding from this PR
@sonatype-lift ignoreall Leave out all the existing findings from this PR
@sonatype-lift exclude <file|issue|path|tool> Exclude specified file|issue|path|tool from Lift findings by updating your config.toml file

Note: When talking to LiftBot, you need to refresh the page to see its response.
Click here to add LiftBot to another repo.


Help us improve LIFT! (Sonatype LiftBot external survey)

Was this a good recommendation for you? Answering this survey will not impact your Lift settings.

[ 🙁 Not relevant ] - [ 😕 Won't fix ] - [ 😑 Not critical, will fix ] - [ 🙂 Critical, will fix ] - [ 😊 Critical, fixing now ]

return null;
}
}
}

private GitLineSeparator() {
// hidden
}
}
Loading