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 + ], + }, + ], + }, +]);