Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` |
Expand Down Expand Up @@ -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 (
<ReactDiffViewer
oldValue={oldCode}
newValue={newCode}
compareMethod={DiffMethod.WORDS}
splitView={true}
renderContent={this.highlightSyntax}
/>
)
}
}
```


## Overriding Styles

Expand Down
52 changes: 42 additions & 10 deletions examples/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface ExampleState {
highlightLine?: string[];
language?: string;
enableSyntaxHighlighting?: boolean;
compareMethod?: string;
}

const P = (window as any).Prism;
Expand All @@ -31,6 +32,7 @@ class Example extends React.Component<{}, ExampleState> {
highlightLine: [],
language: 'javascript',
enableSyntaxHighlighting: true,
compareMethod: 'diffChars'
};
}

Expand All @@ -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<HTMLTableCellElement>,
Expand Down Expand Up @@ -155,16 +160,41 @@ class Example extends React.Component<{}, ExampleState> {
</div>
</div>
<div className="controls">
<select
name="language"
id="language"
onChange={this.onLanguageChange}
value={this.state.language}
>
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="javascript">Javascript</option>
</select>
<span>
<label htmlFor="js_diff_compare_method">JsDiff Compare Method</label>
{' '}
(<a href="https://github.com/kpdecker/jsdiff/tree/v4.0.1#api">Learn More</a>)
{' '}
<select
name="js_diff_compare_method"
id="js_diff_compare_method"
onChange={this.onCompareMethodChange}
value={this.state.compareMethod}
>
<option value="disabled">DISABLE</option>
<option value="diffChars">diffChars</option>
<option value="diffWords">diffWords</option>
<option value="diffWordsWithSpace">diffWordsWithSpace</option>
<option value="diffLines">diffLines</option>
<option value="diffTrimmedLines">diffTrimmedLines</option>
<option value="diffCss">diffCss</option>
<option value="diffSentences">diffSentences</option>
</select>
</span>
<span>
<label htmlFor="language">Language</label>
{' '}
<select
name="language"
id="language"
onChange={this.onLanguageChange}
value={this.state.language}
>
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="javascript">Javascript</option>
</select>
</span>
<span>
<label>
<input
Expand All @@ -190,6 +220,8 @@ class Example extends React.Component<{}, ExampleState> {
</div>
<div className="diff-viewer">
<ReactDiff
disableWordDiff={ this.state.compareMethod === 'disabled' }
compareMethod={ this.state.compareMethod }
highlightLines={this.state.highlightLine}
onLineNumberClick={this.onLineNumberClick}
oldValue={oldValue}
Expand Down
51 changes: 37 additions & 14 deletions src/compute-lines.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
import * as diff from 'diff';

const jsDiff: { [key: string]: any } = diff;

export enum DiffType {
DEFAULT = 0,
ADDED = 1,
REMOVED = 2,
}

// See https://github.com/kpdecker/jsdiff/tree/v4.0.1#api for more info on the below JsDiff methods
export enum DiffMethod {
CHARS = 'diffChars',
WORDS = 'diffWords',
WORDS_WITH_SPACE = 'diffWordsWithSpace',
LINES = 'diffLines',
TRIMMED_LINES = 'diffTrimmedLines',
SENTENCES = 'diffSentences',
CSS = 'diffCss',
}

export interface DiffInformation {
value?: string | DiffInformation[];
lineNumber?: number;
Expand All @@ -22,11 +35,18 @@ export interface ComputedLineInformation {
diffLines: number[];
}

export interface WordDiffInformation {
export interface ComputedDiffInformation {
left?: DiffInformation[];
right?: DiffInformation[];
}

// See https://github.com/kpdecker/jsdiff/tree/v4.0.1#change-objects for more info on JsDiff Change Objects
export interface JsDiffChangeObject {
added?: boolean;
removed?: boolean;
value?: string;
}

/**
* Splits diff text by new line and computes final list of diff lines based on
* conditions.
Expand Down Expand Up @@ -60,14 +80,15 @@ const constructLines = (value: string): string[] => {

/**
* 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<JsDiffChangeObject> = jsDiff[compareMethod](oldValue, newValue);
const computedDiff: ComputedDiffInformation = {
left: [],
right: [],
};
Expand All @@ -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;
};

/**
Expand All @@ -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(),
Expand Down Expand Up @@ -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 || !(<any>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;
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
LineInformation,
DiffInformation,
DiffType,
DiffMethod,
} from './compute-lines';
import computeStyles, { ReactDiffViewerStylesOverride, ReactDiffViewerStyles } from './styles';

Expand All @@ -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.
Expand Down Expand Up @@ -68,6 +71,7 @@ class DiffViewer extends React.Component<ReactDiffViewerProps, ReactDiffViewerSt
splitView: true,
highlightLines: [],
disableWordDiff: false,
compareMethod: DiffMethod.CHARS,
styles: {},
hideLineNumbers: false,
extraLinesSurroundingDiff: 3,
Expand All @@ -79,6 +83,7 @@ class DiffViewer extends React.Component<ReactDiffViewerProps, ReactDiffViewerSt
newValue: PropTypes.string.isRequired,
splitView: PropTypes.bool,
disableWordDiff: PropTypes.bool,
compareMethod: PropTypes.oneOf(Object.values(DiffMethod)),
renderContent: PropTypes.func,
onLineNumberClick: PropTypes.func,
extraLinesSurroundingDiff: PropTypes.number,
Expand Down Expand Up @@ -420,11 +425,12 @@ class DiffViewer extends React.Component<ReactDiffViewerProps, ReactDiffViewerSt
* Generates the entire diff view.
*/
private renderDiff = (): JSX.Element[] => {
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
Expand Down Expand Up @@ -501,4 +507,4 @@ class DiffViewer extends React.Component<ReactDiffViewerProps, ReactDiffViewerSt
}

export default DiffViewer;
export { ReactDiffViewerStylesOverride };
export { ReactDiffViewerStylesOverride, DiffMethod };
Loading