diff --git a/README.md b/README.md
index 3fb7c91b..5a48b11d 100644
--- a/README.md
+++ b/README.md
@@ -76,6 +76,7 @@ class Diff extends PureComponent {
|newValue |`string` |`''` |New value as string. |
|splitView |`boolean` |`true` |Switch between `unified` and `split` view. |
|disableWordDiff |`boolean` |`false` |Show and hide word diff in a diff line. |
+|compareMethod |`string` |`'diffChars'` |JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api |
|hideLineNumbers |`boolean` |`false` |Show and hide line numbers. |
|renderContent |`function` |`undefined` |Render Prop API to render code in the diff viewer. Helpful for [syntax highlighting](#syntax-highlighting) |
|onLineNumberClick |`function` |`undefined` |Event handler for line number click. `(lineId: string) => void` |
@@ -146,6 +147,43 @@ class Diff extends PureComponent {
}
```
+## Text block diff comparison
+
+Different styles of text block diffing are possible by using the enums corresponding to various v4.0.1 JsDiff text block method names ([learn more](https://github.com/kpdecker/jsdiff/tree/v4.0.1#api)).
+
+```javascript
+import React, { PureComponent } from 'react'
+import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer'
+
+const oldCode = `
+{
+ "name": "Original name",
+ "description": null
+}
+`
+const newCode = `
+{
+ "name": "My updated name",
+ "description": "Brand new description",
+ "status": "running"
+}
+`
+
+class Diff extends PureComponent {
+ render = () => {
+ return (
+
+ )
+ }
+}
+```
+
## Overriding Styles
diff --git a/examples/src/index.tsx b/examples/src/index.tsx
index 56ae650e..a4022db2 100644
--- a/examples/src/index.tsx
+++ b/examples/src/index.tsx
@@ -19,6 +19,7 @@ interface ExampleState {
highlightLine?: string[];
language?: string;
enableSyntaxHighlighting?: boolean;
+ compareMethod?: string;
}
const P = (window as any).Prism;
@@ -31,6 +32,7 @@ class Example extends React.Component<{}, ExampleState> {
highlightLine: [],
language: 'javascript',
enableSyntaxHighlighting: true,
+ compareMethod: 'diffChars'
};
}
@@ -45,6 +47,9 @@ class Example extends React.Component<{}, ExampleState> {
private onLanguageChange = (e: any): void =>
this.setState({ language: e.target.value, highlightLine: [] });
+ private onCompareMethodChange = (e: any): void =>
+ this.setState({ compareMethod: e.target.value });
+
private onLineNumberClick = (
id: string,
e: React.MouseEvent,
@@ -155,16 +160,41 @@ class Example extends React.Component<{}, ExampleState> {
-
+
+
+ {' '}
+ (Learn More)
+ {' '}
+
+
+
+
+ {' '}
+
+
{
/**
* 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 computeWordDiff = (oldValue: string, newValue: string): WordDiffInformation => {
- const diffArray = diff
- .diffChars(oldValue, newValue);
- const wordDiff: WordDiffInformation = {
+const computeDiff = (oldValue: string, newValue: string, compareMethod: string): ComputedDiffInformation => {
+ const diffArray: Array = jsDiff[compareMethod](oldValue, newValue);
+ const computedDiff: ComputedDiffInformation = {
left: [],
right: [],
};
@@ -77,22 +98,22 @@ const computeWordDiff = (oldValue: string, newValue: string): WordDiffInformatio
if (added) {
diffInformation.type = DiffType.ADDED;
diffInformation.value = value;
- wordDiff.right.push(diffInformation);
+ computedDiff.right.push(diffInformation);
}
if (removed) {
diffInformation.type = DiffType.REMOVED;
diffInformation.value = value;
- wordDiff.left.push(diffInformation);
+ computedDiff.left.push(diffInformation);
}
if (!removed && !added) {
diffInformation.type = DiffType.DEFAULT;
diffInformation.value = value;
- wordDiff.right.push(diffInformation);
- wordDiff.left.push(diffInformation);
+ computedDiff.right.push(diffInformation);
+ computedDiff.left.push(diffInformation);
}
return diffInformation;
});
- return wordDiff;
+ return computedDiff;
};
/**
@@ -106,11 +127,13 @@ const computeWordDiff = (oldValue: string, newValue: string): WordDiffInformatio
* @param oldString Old string to compare.
* @param newString New string to compare with old string.
* @param disableWordDiff Flag to enable/disable word diff.
+ * @param compareMethod JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api
*/
const computeLineInformation = (
oldString: string,
newString: string,
disableWordDiff: boolean = false,
+ compareMethod: string = DiffMethod.CHARS,
): ComputedLineInformation => {
const diffArray = diff.diffLines(
oldString.trimRight(),
@@ -173,12 +196,12 @@ const computeLineInformation = (
right.type = type;
// Do word level diff and assign the corresponding values to the
// left and right diff information object.
- if (disableWordDiff) {
+ if (disableWordDiff || !(Object).values(DiffMethod).includes(compareMethod)) {
right.value = rightValue;
} else {
- const wordDiff = computeWordDiff(line, rightValue as string);
- right.value = wordDiff.right;
- left.value = wordDiff.left;
+ const computedDiff = computeDiff(line, rightValue as string, compareMethod);
+ right.value = computedDiff.right;
+ left.value = computedDiff.left;
}
}
}
diff --git a/src/index.tsx b/src/index.tsx
index 716990c3..2568090a 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -7,6 +7,7 @@ import {
LineInformation,
DiffInformation,
DiffType,
+ DiffMethod,
} from './compute-lines';
import computeStyles, { ReactDiffViewerStylesOverride, ReactDiffViewerStyles } from './styles';
@@ -29,6 +30,8 @@ export interface ReactDiffViewerProps {
splitView?: boolean;
// Enable/Disable word diff.
disableWordDiff?: boolean;
+ // JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api
+ compareMethod?: string;
// Number of unmodified lines surrounding each line diff.
extraLinesSurroundingDiff?: number;
// Show/hide line number.
@@ -68,6 +71,7 @@ class DiffViewer extends React.Component {
- const { oldValue, newValue, splitView } = this.props;
+ const { oldValue, newValue, splitView, disableWordDiff, compareMethod } = this.props;
const { lineInformation, diffLines } = computeLineInformation(
oldValue,
newValue,
- this.props.disableWordDiff,
+ disableWordDiff,
+ compareMethod,
);
const extraLines = this.props.extraLinesSurroundingDiff < 0
? 0
@@ -501,4 +507,4 @@ class DiffViewer extends React.Component {
it('Should it avoid trailing spaces', (): void => {
@@ -198,4 +198,216 @@ describe('Testing compute lines utils', (): void => {
diffLines: [1],
});
});
+
+ it('Should call "diffChars" jsDiff method when compareMethod is not provided', (): void => {
+ const oldCode = `Hello World`;
+ const newCode = `My Updated Name
+Also this info`;
+
+ expect(computeLineInformation(oldCode, newCode))
+ .toMatchObject({
+ lineInformation: [
+ {
+ "right": {
+ "lineNumber": 1,
+ "type": 1,
+ "value": [
+ {
+ "type": 1,
+ "value": "My Updat"
+ },
+ {
+ "type": 0,
+ "value": "e"
+ },
+ {
+ "type": 1,
+ "value": "d"
+ },
+ {
+ "type": 0,
+ "value": " "
+ },
+ {
+ "type": 1,
+ "value": "Name"
+ }
+ ]
+ },
+ "left": {
+ "lineNumber": 1,
+ "type": 2,
+ "value": [
+ {
+ "type": 2,
+ "value": "H"
+ },
+ {
+ "type": 0,
+ "value": "e"
+ },
+ {
+ "type": 2,
+ "value": "llo"
+ },
+ {
+ "type": 0,
+ "value": " "
+ },
+ {
+ "type": 2,
+ "value": "World"
+ }
+ ]
+ }
+ },
+ {
+ "right": {
+ "lineNumber": 2,
+ "type": 1,
+ "value": "Also this info"
+ },
+ "left": {}
+ }
+ ],
+ diffLines: [
+ 0,
+ 2,
+ ],
+ });
+ });
+
+ it('Should call "diffWords" jsDiff method when a compareMethod IS provided', (): void => {
+ const oldCode = `Hello World`;
+ const newCode = `My Updated Name
+Also this info`;
+
+ expect(computeLineInformation(oldCode, newCode, false, DiffMethod.WORDS))
+ .toMatchObject({
+ lineInformation: [
+ {
+ "right": {
+ "lineNumber": 1,
+ "type": 1,
+ "value": [
+ {
+ "type": 1,
+ "value": "My"
+ },
+ {
+ "type": 0,
+ "value": " "
+ },
+ {
+ "type": 1,
+ "value": "Updated Name"
+ }
+ ]
+ },
+ "left": {
+ "lineNumber": 1,
+ "type": 2,
+ "value": [
+ {
+ "type": 2,
+ "value": "Hello"
+ },
+ {
+ "type": 0,
+ "value": " "
+ },
+ {
+ "type": 2,
+ "value": "World"
+ }
+ ]
+ }
+ },
+ {
+ "right": {
+ "lineNumber": 2,
+ "type": 1,
+ "value": "Also this info"
+ },
+ "left": {}
+ }
+ ],
+ diffLines: [
+ 0,
+ 2
+ ],
+ });
+ });
+
+ it('Should not call jsDiff method and not diff text when unknown JsDiffMethod is passed', (): void => {
+ const oldCode = `Hello World`;
+ const newCode = `My Updated Name
+Also this info`;
+
+ expect(computeLineInformation(oldCode, newCode, false, 'unknownMethod'))
+ .toMatchObject({
+ lineInformation: [
+ {
+ "right": {
+ "lineNumber": 1,
+ "type": 1,
+ "value": "My Updated Name"
+ },
+ "left": {
+ "lineNumber": 1,
+ "type": 2,
+ "value": "Hello World"
+ }
+ },
+ {
+ "right": {
+ "lineNumber": 2,
+ "type": 1,
+ "value": "Also this info"
+ },
+ "left": {}
+ }
+ ],
+ diffLines: [
+ 0,
+ 2
+ ],
+ });
+ });
+
+ it('Should not call jsDiff method and not diff text when disableWordDiff is true', (): void => {
+ const oldCode = `Hello World`;
+ const newCode = `My Updated Name
+Also this info`;
+
+ expect(computeLineInformation(oldCode, newCode, true))
+ .toMatchObject({
+ lineInformation: [
+ {
+ "right": {
+ "lineNumber": 1,
+ "type": 1,
+ "value": "My Updated Name"
+ },
+ "left": {
+ "lineNumber": 1,
+ "type": 2,
+ "value": "Hello World"
+ }
+ },
+ {
+ "right": {
+ "lineNumber": 2,
+ "type": 1,
+ "value": "Also this info"
+ },
+ "left": {}
+ }
+ ],
+ diffLines: [
+ 0,
+ 2
+ ],
+ });
+ });
});