UNPKG

react-diff-viewer-continued

Version:

Continuation of a simple and beautiful text diff viewer component made with diff and React

229 lines (228 loc) 9.96 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.computeLineInformation = exports.DiffMethod = exports.DiffType = void 0; const diff = __importStar(require("diff")); const jsDiff = diff; var DiffType; (function (DiffType) { DiffType[DiffType["DEFAULT"] = 0] = "DEFAULT"; DiffType[DiffType["ADDED"] = 1] = "ADDED"; DiffType[DiffType["REMOVED"] = 2] = "REMOVED"; DiffType[DiffType["CHANGED"] = 3] = "CHANGED"; })(DiffType || (exports.DiffType = DiffType = {})); // See https://github.com/kpdecker/jsdiff/tree/v4.0.1#api for more info on the below JsDiff methods var DiffMethod; (function (DiffMethod) { DiffMethod["CHARS"] = "diffChars"; DiffMethod["WORDS"] = "diffWords"; DiffMethod["WORDS_WITH_SPACE"] = "diffWordsWithSpace"; DiffMethod["LINES"] = "diffLines"; DiffMethod["TRIMMED_LINES"] = "diffTrimmedLines"; DiffMethod["SENTENCES"] = "diffSentences"; DiffMethod["CSS"] = "diffCss"; DiffMethod["JSON"] = "diffJson"; })(DiffMethod || (exports.DiffMethod = DiffMethod = {})); /** * Splits diff text by new line and computes final list of diff lines based on * conditions. * * @param value Diff text from the js diff module. */ const constructLines = (value) => { if (value === '') return []; const lines = value.replace(/\n$/, '').split('\n'); return lines; }; /** * Computes word diff information in the line. * [TODO]: Consider adding options argument for JsDiff text block comparison * * @param oldValue Old word in the line. * @param newValue New word in the line. * @param compareMethod JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api */ const computeDiff = (oldValue, newValue, compareMethod = DiffMethod.CHARS) => { const diffArray = jsDiff[compareMethod](oldValue, newValue); const computedDiff = { left: [], right: [], }; diffArray.forEach(({ added, removed, value }) => { const diffInformation = {}; if (added) { diffInformation.type = DiffType.ADDED; diffInformation.value = value; computedDiff.right.push(diffInformation); } if (removed) { diffInformation.type = DiffType.REMOVED; diffInformation.value = value; computedDiff.left.push(diffInformation); } if (!removed && !added) { diffInformation.type = DiffType.DEFAULT; diffInformation.value = value; computedDiff.right.push(diffInformation); computedDiff.left.push(diffInformation); } return diffInformation; }); return computedDiff; }; /** * [TODO]: Think about moving common left and right value assignment to a * common place. Better readability? * * Computes line wise information based in the js diff information passed. Each * line contains information about left and right section. Left side denotes * deletion and right side denotes addition. * * @param oldString Old string to compare. * @param newString New string to compare with old string. * @param disableWordDiff Flag to enable/disable word diff. * @param lineCompareMethod JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api * @param linesOffset line number to start counting from * @param showLines lines that are always shown, regardless of diff */ const computeLineInformation = (oldString, newString, disableWordDiff = false, lineCompareMethod = DiffMethod.CHARS, linesOffset = 0, showLines = []) => { let diffArray = []; // Use diffLines for strings, and diffJson for objects... if (typeof oldString === 'string' && typeof newString === 'string') { diffArray = diff.diffLines(oldString.trimRight(), newString.trimRight(), { newlineIsToken: false, ignoreWhitespace: false, ignoreCase: false, }); } else { diffArray = diff.diffJson(oldString, newString); } let rightLineNumber = linesOffset; let leftLineNumber = linesOffset; let lineInformation = []; let counter = 0; const diffLines = []; const ignoreDiffIndexes = []; const getLineInformation = (value, diffIndex, added, removed, evaluateOnlyFirstLine) => { const lines = constructLines(value); return lines .map((line, lineIndex) => { const left = {}; const right = {}; if (ignoreDiffIndexes.includes(`${diffIndex}-${lineIndex}`) || (evaluateOnlyFirstLine && lineIndex !== 0)) { return undefined; } if (added || removed) { let countAsChange = true; if (removed) { leftLineNumber += 1; left.lineNumber = leftLineNumber; left.type = DiffType.REMOVED; left.value = line || ' '; // When the current line is of type REMOVED, check the next item in // the diff array whether it is of type ADDED. If true, the current // diff will be marked as both REMOVED and ADDED. Meaning, the // current line is a modification. const nextDiff = diffArray[diffIndex + 1]; if (nextDiff && nextDiff.added) { const nextDiffLines = constructLines(nextDiff.value)[lineIndex]; if (nextDiffLines) { const nextDiffLineInfo = getLineInformation(nextDiffLines, diffIndex, true, false, true); const { value: rightValue, lineNumber, type, } = nextDiffLineInfo[0].right; // When identified as modification, push the next diff to ignore // list as the next value will be added in this line computation as // right and left values. ignoreDiffIndexes.push(`${diffIndex + 1}-${lineIndex}`); right.lineNumber = lineNumber; if (left.value === rightValue) { // The new value is exactly the same as the old countAsChange = false; right.type = 0; left.type = 0; right.value = rightValue; } else { right.type = type; // Do char level diff and assign the corresponding values to the // left and right diff information object. if (disableWordDiff) { right.value = rightValue; } else { const computedDiff = computeDiff(line, rightValue, lineCompareMethod); right.value = computedDiff.right; left.value = computedDiff.left; } } } } } else { rightLineNumber += 1; right.lineNumber = rightLineNumber; right.type = DiffType.ADDED; right.value = line; } if (countAsChange && !evaluateOnlyFirstLine) { if (!diffLines.includes(counter)) { diffLines.push(counter); } } } else { leftLineNumber += 1; rightLineNumber += 1; left.lineNumber = leftLineNumber; left.type = DiffType.DEFAULT; left.value = line; right.lineNumber = rightLineNumber; right.type = DiffType.DEFAULT; right.value = line; } if ((showLines === null || showLines === void 0 ? void 0 : showLines.includes(`L-${left.lineNumber}`)) || (showLines === null || showLines === void 0 ? void 0 : showLines.includes(`R-${right.lineNumber}`)) && !diffLines.includes(counter)) { diffLines.push(counter); } if (!evaluateOnlyFirstLine) { counter += 1; } return { right, left }; }) .filter(Boolean); }; diffArray.forEach(({ added, removed, value }, index) => { lineInformation = [ ...lineInformation, ...getLineInformation(value, index, added, removed), ]; }); return { lineInformation, diffLines, }; }; exports.computeLineInformation = computeLineInformation;