diff --git a/src/core/operations/Diff.mjs b/src/core/operations/Diff.mjs
index 9f180f86..dd05f45b 100644
--- a/src/core/operations/Diff.mjs
+++ b/src/core/operations/Diff.mjs
@@ -57,6 +57,12 @@ class Diff extends Operation {
"type": "boolean",
"value": false,
"hint": "Relevant for word and line"
+ },
+ {
+ "name": "Show +/- notation",
+ "hint": "Works only in Line diff mode.",
+ "type": "boolean",
+ "value": false
}
];
}
@@ -73,7 +79,8 @@ class Diff extends Operation {
showAdded,
showRemoved,
showSubtraction,
- ignoreWhitespace
+ ignoreWhitespace,
+ showNotation
] = args,
samples = input.split(sampleDelim);
let output = "",
@@ -86,6 +93,34 @@ class Diff extends Operation {
throw new OperationError("Incorrect number of samples, perhaps you need to modify the sample delimiter or add more samples?");
}
+ // +/- notation only works properly with Line diff mode
+ // For other modes, it falls back to HTML format and this option is ignored (see hint).
+ const useNotation = showNotation && diffBy === "Line";
+
+ if (useNotation) {
+ const patch = jsdiff.createTwoFilesPatch("original", "modified", samples[0], samples[1], "", "", {
+ context: Infinity,
+ ignoreWhitespace: ignoreWhitespace,
+ });
+
+ const lines = patch.split("\n");
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+ if (line.startsWith("@@")) {
+ if (showSubtraction) output += line + "\n";
+ } else if (line.startsWith("+") && !line.startsWith("+++")) {
+ if (showAdded) output += line + "\n";
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
+ if (showRemoved) output += line + "\n";
+ } else if (line.startsWith("===") || line.startsWith("---") || line.startsWith("+++") || line.startsWith("\\")) {
+ continue;
+ } else if (line.length > 0 && !showSubtraction) {
+ output += line.substring(1) + "\n";
+ }
+ }
+ return output.slice(0, -1);
+ }
+
switch (diffBy) {
case "Character":
diff = jsdiff.diffChars(samples[0], samples[1]);
diff --git a/tests/operations/tests/Diff.mjs b/tests/operations/tests/Diff.mjs
new file mode 100644
index 00000000..e2d08875
--- /dev/null
+++ b/tests/operations/tests/Diff.mjs
@@ -0,0 +1,313 @@
+/**
+ * @author mikecat
+ * @copyright Crown Copyright 2023
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ "name": "Diff: Show +/- notation - true (Line mode)",
+ "input": "line1\nline2\nline3\n\nline1\nline2modified\nline3",
+ "expectedOutput": "line1\n-line2\n+line2modified\nline3",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n\\n",
+ "Line",
+ true,
+ true,
+ false,
+ false,
+ true
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Show +/- notation - false (Line mode)",
+ "input": "line1\nline2\nline3\n\nline1\nline2modified\nline3",
+ "expectedOutput": "line1\nline2\nline2modified\nline3",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n\\n",
+ "Line",
+ true,
+ true,
+ false,
+ false,
+ false
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Show +/- notation - with showAdded false (Line mode)",
+ "input": "line1\nline2\nline3\n\nline1\nline2modified\nline3",
+ "expectedOutput": "line1\n-line2\nline3",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n\\n",
+ "Line",
+ false,
+ true,
+ false,
+ false,
+ true
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Show +/- notation - with showRemoved false (Line mode)",
+ "input": "line1\nline2\nline3\n\nline1\nline2modified\nline3",
+ "expectedOutput": "line1\n+line2modified\nline3",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n\\n",
+ "Line",
+ true,
+ false,
+ false,
+ false,
+ true
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Show +/- notation - with showSubtraction true (Line mode)",
+ "input": "line1\nline2\nline3\n\nline1\nline2modified\nline3",
+ "expectedOutput": "@@ -1,3 +1,3 @@\n-line2\n+line2modified",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n\\n",
+ "Line",
+ true,
+ true,
+ true,
+ false,
+ true
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Show +/- notation ignored for Character mode",
+ "input": "abc\ndef",
+ "expectedOutput": "abcdef",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n",
+ "Character",
+ true,
+ true,
+ false,
+ false,
+ true
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Show +/- notation ignored for Word mode",
+ "input": "hello world\nhello cruel world",
+ "expectedOutput": "hello cruel world",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n",
+ "Word",
+ true,
+ true,
+ false,
+ false,
+ true
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Show +/- notation ignored for Sentence mode",
+ "input": "Hello world.\nHello there.",
+ "expectedOutput": "Hello world.Hello there.",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n",
+ "Sentence",
+ true,
+ true,
+ false,
+ false,
+ true
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Character mode basic",
+ "input": "abc\nabc",
+ "expectedOutput": "abc",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n",
+ "Character",
+ true,
+ true,
+ false,
+ false,
+ false
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Character mode with changes",
+ "input": "abc\nabcdef",
+ "expectedOutput": "abcdef",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n",
+ "Character",
+ true,
+ true,
+ false,
+ false,
+ false
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Word mode basic",
+ "input": "hello world\nhello world",
+ "expectedOutput": "hello world",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n",
+ "Word",
+ true,
+ true,
+ false,
+ false,
+ false
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Word mode with changes",
+ "input": "hello world\nhello cruel world",
+ "expectedOutput": "hello cruel world",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n",
+ "Word",
+ true,
+ true,
+ false,
+ false,
+ false
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Line mode basic",
+ "input": "line1\nline2\nline3\n\nline1\nline2\nline3",
+ "expectedOutput": "line1\nline2\nline3",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n\\n",
+ "Line",
+ true,
+ true,
+ false,
+ false,
+ false
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Line mode with changes",
+ "input": "line1\nline2\nline3\n\nline1\nline4\nline3",
+ "expectedOutput": "line1\nline2\nline4\nline3",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n\\n",
+ "Line",
+ true,
+ true,
+ false,
+ false,
+ false
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Sentence mode basic",
+ "input": "Hello world.\nHello world.",
+ "expectedOutput": "Hello world.",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n",
+ "Sentence",
+ true,
+ true,
+ false,
+ false,
+ false
+ ],
+ },
+ ],
+ },
+ {
+ "name": "Diff: Sentence mode with changes",
+ "input": "Hello world.\nHello there.",
+ "expectedOutput": "Hello world.Hello there.",
+ "recipeConfig": [
+ {
+ "op": "Diff",
+ "args": [
+ "\\n",
+ "Sentence",
+ true,
+ true,
+ false,
+ false,
+ false
+ ],
+ },
+ ],
+ },
+]);