Skip to content

Commit e53a882

Browse files
author
Derek Hammond
committed
fixes #31
1 parent 8034565 commit e53a882

File tree

5 files changed

+329
-28
lines changed

5 files changed

+329
-28
lines changed

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class Diff extends PureComponent {
7676
|newValue |`string` |`''` |New value as string. |
7777
|splitView |`boolean` |`true` |Switch between `unified` and `split` view. |
7878
|disableWordDiff |`boolean` |`false` |Show and hide word diff in a diff line. |
79+
|jsDiffCompareMethod |`string` |`'diffChars'` |JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api |
7980
|hideLineNumbers |`boolean` |`false` |Show and hide line numbers. |
8081
|renderContent |`function` |`undefined` |Render Prop API to render code in the diff viewer. Helpful for [syntax highlighting](#syntax-highlighting) |
8182
|onLineNumberClick |`function` |`undefined` |Event handler for line number click. `(lineId: string) => void` |
@@ -146,6 +147,43 @@ class Diff extends PureComponent {
146147
}
147148
```
148149

150+
## Text block diff comparison
151+
152+
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)).
153+
154+
```javascript
155+
import React, { PureComponent } from 'react'
156+
import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer'
157+
158+
const oldCode = `
159+
{
160+
"name": "Original name",
161+
"description": null
162+
}
163+
`
164+
const newCode = `
165+
{
166+
"name": "My updated name",
167+
"description": "Brand new description",
168+
"status": "running"
169+
}
170+
`
171+
172+
class Diff extends PureComponent {
173+
render = () => {
174+
return (
175+
<ReactDiffViewer
176+
oldValue={oldCode}
177+
newValue={newCode}
178+
jsDiffCompareMethod={DiffMethod.WORDS}
179+
splitView={true}
180+
renderContent={this.highlightSyntax}
181+
/>
182+
)
183+
}
184+
}
185+
```
186+
149187

150188
## Overriding Styles
151189

examples/src/index.tsx

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ interface ExampleState {
1919
highlightLine?: string[];
2020
language?: string;
2121
enableSyntaxHighlighting?: boolean;
22+
jsDiffCompareMethod?: string;
2223
}
2324

2425
const P = (window as any).Prism;
@@ -31,6 +32,7 @@ class Example extends React.Component<{}, ExampleState> {
3132
highlightLine: [],
3233
language: 'javascript',
3334
enableSyntaxHighlighting: true,
35+
jsDiffCompareMethod: 'diffChars'
3436
};
3537
}
3638

@@ -45,6 +47,9 @@ class Example extends React.Component<{}, ExampleState> {
4547
private onLanguageChange = (e: any): void =>
4648
this.setState({ language: e.target.value, highlightLine: [] });
4749

50+
private onJsDiffCompareMethodChange = (e: any): void =>
51+
this.setState({ jsDiffCompareMethod: e.target.value });
52+
4853
private onLineNumberClick = (
4954
id: string,
5055
e: React.MouseEvent<HTMLTableCellElement>,
@@ -155,16 +160,41 @@ class Example extends React.Component<{}, ExampleState> {
155160
</div>
156161
</div>
157162
<div className="controls">
158-
<select
159-
name="language"
160-
id="language"
161-
onChange={this.onLanguageChange}
162-
value={this.state.language}
163-
>
164-
<option value="json">JSON</option>
165-
<option value="xml">XML</option>
166-
<option value="javascript">Javascript</option>
167-
</select>
163+
<span>
164+
<label htmlFor="js_diff_compare_method">JsDiff Compare Method</label>
165+
{' '}
166+
(<a href="https://github.com/kpdecker/jsdiff/tree/v4.0.1#api">Learn More</a>)
167+
{' '}
168+
<select
169+
name="js_diff_compare_method"
170+
id="js_diff_compare_method"
171+
onChange={this.onJsDiffCompareMethodChange}
172+
value={this.state.jsDiffCompareMethod}
173+
>
174+
<option value="disabled">DISABLE</option>
175+
<option value="diffChars">diffChars</option>
176+
<option value="diffWords">diffWords</option>
177+
<option value="diffWordsWithSpace">diffWordsWithSpace</option>
178+
<option value="diffLines">diffLines</option>
179+
<option value="diffTrimmedLines">diffTrimmedLines</option>
180+
<option value="diffCss">diffCss</option>
181+
<option value="diffSentences">diffSentences</option>
182+
</select>
183+
</span>
184+
<span>
185+
<label htmlFor="language">Language</label>
186+
{' '}
187+
<select
188+
name="language"
189+
id="language"
190+
onChange={this.onLanguageChange}
191+
value={this.state.language}
192+
>
193+
<option value="json">JSON</option>
194+
<option value="xml">XML</option>
195+
<option value="javascript">Javascript</option>
196+
</select>
197+
</span>
168198
<span>
169199
<label>
170200
<input
@@ -190,6 +220,8 @@ class Example extends React.Component<{}, ExampleState> {
190220
</div>
191221
<div className="diff-viewer">
192222
<ReactDiff
223+
disableWordDiff={ this.state.jsDiffCompareMethod === 'disabled' }
224+
jsDiffCompareMethod={ this.state.jsDiffCompareMethod }
193225
highlightLines={this.state.highlightLine}
194226
onLineNumberClick={this.onLineNumberClick}
195227
oldValue={oldValue}

src/compute-lines.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ export enum DiffType {
66
REMOVED = 2,
77
}
88

9+
export enum DiffMethod {
10+
CHARS = 'diffChars',
11+
WORDS = 'diffWords',
12+
WORDS_WITH_SPACE = 'diffWordsWithSpace',
13+
LINES = 'diffLines',
14+
TRIMMED_LINES = 'diffTrimmedLines',
15+
SENTENCES = 'diffSentences',
16+
CSS = 'diffCss',
17+
}
18+
919
export interface DiffInformation {
1020
value?: string | DiffInformation[];
1121
lineNumber?: number;
@@ -22,7 +32,7 @@ export interface ComputedLineInformation {
2232
diffLines: number[];
2333
}
2434

25-
export interface WordDiffInformation {
35+
export interface ComputedDiffInformation {
2636
left?: DiffInformation[];
2737
right?: DiffInformation[];
2838
}
@@ -60,14 +70,15 @@ const constructLines = (value: string): string[] => {
6070

6171
/**
6272
* Computes word diff information in the line.
73+
* [TODO]: Consider adding options argument for JsDiff text block comparison
6374
*
6475
* @param oldValue Old word in the line.
6576
* @param newValue New word in the line.
77+
* @param jsDiffCompareMethod JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api
6678
*/
67-
const computeWordDiff = (oldValue: string, newValue: string): WordDiffInformation => {
68-
const diffArray = diff
69-
.diffChars(oldValue, newValue);
70-
const wordDiff: WordDiffInformation = {
79+
const computeDiff = (oldValue: string, newValue: string, jsDiffCompareMethod: string): ComputedDiffInformation => {
80+
const diffArray = diff[jsDiffCompareMethod](oldValue, newValue);
81+
const computedDiff: ComputedDiffInformation = {
7182
left: [],
7283
right: [],
7384
};
@@ -77,22 +88,22 @@ const computeWordDiff = (oldValue: string, newValue: string): WordDiffInformatio
7788
if (added) {
7889
diffInformation.type = DiffType.ADDED;
7990
diffInformation.value = value;
80-
wordDiff.right.push(diffInformation);
91+
computedDiff.right.push(diffInformation);
8192
}
8293
if (removed) {
8394
diffInformation.type = DiffType.REMOVED;
8495
diffInformation.value = value;
85-
wordDiff.left.push(diffInformation);
96+
computedDiff.left.push(diffInformation);
8697
}
8798
if (!removed && !added) {
8899
diffInformation.type = DiffType.DEFAULT;
89100
diffInformation.value = value;
90-
wordDiff.right.push(diffInformation);
91-
wordDiff.left.push(diffInformation);
101+
computedDiff.right.push(diffInformation);
102+
computedDiff.left.push(diffInformation);
92103
}
93104
return diffInformation;
94105
});
95-
return wordDiff;
106+
return computedDiff;
96107
};
97108

98109
/**
@@ -106,11 +117,13 @@ const computeWordDiff = (oldValue: string, newValue: string): WordDiffInformatio
106117
* @param oldString Old string to compare.
107118
* @param newString New string to compare with old string.
108119
* @param disableWordDiff Flag to enable/disable word diff.
120+
* @param jsDiffCompareMethod JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api
109121
*/
110122
const computeLineInformation = (
111123
oldString: string,
112124
newString: string,
113125
disableWordDiff: boolean = false,
126+
jsDiffCompareMethod: string = DiffMethod.CHARS,
114127
): ComputedLineInformation => {
115128
const diffArray = diff.diffLines(
116129
oldString.trimRight(),
@@ -173,12 +186,12 @@ const computeLineInformation = (
173186
right.type = type;
174187
// Do word level diff and assign the corresponding values to the
175188
// left and right diff information object.
176-
if (disableWordDiff) {
189+
if (disableWordDiff || !(<any>Object).values(DiffMethod).includes(jsDiffCompareMethod)) {
177190
right.value = rightValue;
178191
} else {
179-
const wordDiff = computeWordDiff(line, rightValue as string);
180-
right.value = wordDiff.right;
181-
left.value = wordDiff.left;
192+
const computedDiff = computeDiff(line, rightValue as string, jsDiffCompareMethod);
193+
right.value = computedDiff.right;
194+
left.value = computedDiff.left;
182195
}
183196
}
184197
}

src/index.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
LineInformation,
88
DiffInformation,
99
DiffType,
10+
DiffMethod,
1011
} from './compute-lines';
1112
import computeStyles, { ReactDiffViewerStylesOverride, ReactDiffViewerStyles } from './styles';
1213

@@ -29,6 +30,8 @@ export interface ReactDiffViewerProps {
2930
splitView?: boolean;
3031
// Enable/Disable word diff.
3132
disableWordDiff?: boolean;
33+
// JsDiff text diff method from https://github.com/kpdecker/jsdiff/tree/v4.0.1#api
34+
jsDiffCompareMethod?: string;
3235
// Number of unmodified lines surrounding each line diff.
3336
extraLinesSurroundingDiff?: number;
3437
// Show/hide line number.
@@ -68,6 +71,7 @@ class DiffViewer extends React.Component<ReactDiffViewerProps, ReactDiffViewerSt
6871
splitView: true,
6972
highlightLines: [],
7073
disableWordDiff: false,
74+
jsDiffCompareMethod: DiffMethod.CHARS,
7175
styles: {},
7276
hideLineNumbers: false,
7377
extraLinesSurroundingDiff: 3,
@@ -79,6 +83,7 @@ class DiffViewer extends React.Component<ReactDiffViewerProps, ReactDiffViewerSt
7983
newValue: PropTypes.string.isRequired,
8084
splitView: PropTypes.bool,
8185
disableWordDiff: PropTypes.bool,
86+
jsDiffCompareMethod: PropTypes.oneOf(Object.values(DiffMethod)),
8287
renderContent: PropTypes.func,
8388
onLineNumberClick: PropTypes.func,
8489
extraLinesSurroundingDiff: PropTypes.number,
@@ -420,11 +425,12 @@ class DiffViewer extends React.Component<ReactDiffViewerProps, ReactDiffViewerSt
420425
* Generates the entire diff view.
421426
*/
422427
private renderDiff = (): JSX.Element[] => {
423-
const { oldValue, newValue, splitView } = this.props;
428+
const { oldValue, newValue, splitView, disableWordDiff, jsDiffCompareMethod } = this.props;
424429
const { lineInformation, diffLines } = computeLineInformation(
425430
oldValue,
426431
newValue,
427-
this.props.disableWordDiff,
432+
disableWordDiff,
433+
jsDiffCompareMethod,
428434
);
429435
const extraLines = this.props.extraLinesSurroundingDiff < 0
430436
? 0
@@ -501,4 +507,4 @@ class DiffViewer extends React.Component<ReactDiffViewerProps, ReactDiffViewerSt
501507
}
502508

503509
export default DiffViewer;
504-
export { ReactDiffViewerStylesOverride };
510+
export { ReactDiffViewerStylesOverride, DiffMethod };

0 commit comments

Comments
 (0)