otakustay / react-diff-view

A git diff component
MIT License
824 stars 78 forks source link

Using the library with the react-diff-viewer sandbox <https://8rhv2q.csb.app/> found different behaviour #220

Open giampietrozambelli opened 2 weeks ago

giampietrozambelli commented 2 weeks ago

@otakustay I try to diff the 2 following sample json in the react-diff-viewer sandbox https://8rhv2q.csb.app/ .

As in the attached picture:

image

I would expect to identify the pattern: { "firstname": "Mario", "lastname": "Rossi" } as repeated and identical, while the library compare line per line in this case.

Note that just adding the following new element in the array: _{ "newtag": "new"; }

image

the library identifies the correct repeated and identical item.

Any suggestions? Did I miss some configuration parameters to let the repeated element properly identified as identical in the 2 input variables? { "firstname": "Mario", "lastname": "Rossi" }

Regards, Giampietro

The source code from the sandbox is:

import React, {useState, useCallback, useMemo} from 'react';
import ReactDOM from 'react-dom';
import {Input, Button} from 'antd';
import {diffLines, formatLines} from 'unidiff';
import {parseDiff, Diff, Hunk} from 'react-diff-view';
import {useInput} from './hooks';
import 'antd/dist/antd.min.css';
import 'react-diff-view/style/index.css';
import './styles.css';
import tokenize from './tokenize';
const EMPTY_HUNKS = [];
const renderToken = (token, defaultRender, i) => {
    switch (token.type) {
        case 'space':
            console.log(token);
            return (
                <span key={i} className="space">
                    {token.children && token.children.map((token, i) => renderToken(token, defaultRender, i))}
                </span>
            );
        default:
            return defaultRender(token, i);
    }
};
function App() {
    const oldText = useInput('');
    const newText = useInput('');
    const [{type, hunks}, setDiff] = useState('');
    const updateDiffText = useCallback(() => {
        const diffText = formatLines(diffLines(oldText.value, newText.value), {context: 0});
        const [diff] = parseDiff(diffText, {nearbySequences: 'zip'});
        //const [diff] = parseDiff(diffText);
        setDiff(diff);
    }, [oldText.value, newText.value, setDiff]);
    const tokens = useMemo(() => tokenize(hunks), [hunks]);
    return (
        <div>
            <header className="header">
                <div className="input">
                    <Input.TextArea className="text" rows={15} placeholder="old text..." {...oldText} />
                    <Input.TextArea className="text" rows={15} placeholder="new text..." {...newText} />
                </div>
                <Button className="submit" type="primary" onClick={updateDiffText}>
                    GENERATE DIFF
                </Button>
            </header>
            <main>
                <Diff
                    viewType="split"
                    diffType={type}
                    hunks={hunks || EMPTY_HUNKS}
                    tokens={tokens}
                    renderToken={renderToken}
                >
                    {(hunks) => hunks.map((hunk) => <Hunk key={hunk.content} hunk={hunk} />)}
                </Diff>
            </main>
        </div>
    );
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
otakustay commented 2 weeks ago

Could please paste your old and new code directly here, so I can have more debugging work to reproduce and identify the problem

giampietrozambelli commented 2 weeks ago
// Sample 1 - OLD
{
"prop_a":[
  {
    "firstname": "Andrea",
    "lastname": "Chieppa"
  },
  {
    "firstname": "Mario",
    "lastname": "Rossi"
  }
 ]
}
// Sample 1 - NEW
{
"prop_a":[
  {
    "firstname": "Mario",
    "lastname": "Rossi"
  },
  {
    "firstname": "Guido",
    "lastname": "Bianchi"
  }
 ]
}

// Sample 2 - OLD
{
"prop_a":[
  {
    "firstname": "Andrea",
    "lastname": "Chieppa"
  },
  {
    "firstname": "Mario",
    "lastname": "Rossi"
  },
  {
    "new_tag": "new"
  }
 ]
}
// Sample 2 - NEW
{
"prop_a":[
  {
    "firstname": "Mario",
    "lastname": "Rossi"
  },
  {
    "firstname": "Guido",
    "lastname": "Bianchi"
  },
  {
    "new_tag": "new"
  }
 ]
}
giampietrozambelli commented 2 weeks ago

The code of the sandbox can be found here: Sanbox URL

giampietrozambelli commented 1 week ago

@otakustay Did you have a chance to reproduce the issue? Could you share more hints?

giampietrozambelli commented 3 days ago

@otakustay Any news on the ability to reproduce the issue? Or you don't recognize it as an issue?

otakustay commented 3 days ago

Sorry for the sever delay, this issue is related to how we compute diff from source text using unidiff library, by default I didn't pass any options to diffLines function in my demo, this results a unwanted diff in your case.

I've changed the sandbox, adding an option object to diffLines call:

const diffText = formatLines(
    diffLines(oldText.value, newText.value, {newlineIsToken: true}),
    {context: 3},
);

Note the {newlineIsToken: true} object which helps produce a diff result similar to our expectation, however this introduce another problem that we can see extra empty lines between properties.

CleanShot 2024-07-05 at 21 34 43@2x

This for sure is not correct, and I believe the problem lies inside the formatLines function from unidiff package, I'd again search for information about this.

This is not a render issue for react-diff-view, but a problem with how we generate the diff text itself in demo, maybe I can have a look at how react-diff-viewer computes the diff text and find some inspiration.

otakustay commented 3 days ago

It's also weird to me that the Diff.createPatch function from diff package can produce a diff text exactly the same as the image I posted above:

const Diff = require('diff');

const a = `{
"prop_a":[
  {
    "firstname": "Andrea",
    "lastname": "Chieppa"
  },
  {
    "firstname": "Mario",
    "lastname": "Rossi"
  },
  {
    "new_tag": "new"
  }
 ]
}`;

const b = `{
"prop_a":[
  {
    "firstname": "Mario",
    "lastname": "Rossi"
  },
  {
    "firstname": "Guido",
    "lastname": "Bianchi"
  },
  {
    "new_tag": "new"
  }
 ]
}`;

const patch = Diff.createPatch(
    'a.txt',
    a,
    b,
    undefined,
    undefined,
    {
        newlineIsToken: true
    }
);

This results:

Index: a.txt
===================================================================
--- a.txt
+++ a.txt
@@ -1,16 +1,16 @@
 {
 "prop_a":[
   {
-    "firstname": "Andrea",
+    "firstname": "Mario",

-    "lastname": "Chieppa"
+    "lastname": "Rossi"

   },
   {
-    "firstname": "Mario",
+    "firstname": "Guido",

-    "lastname": "Rossi"
+    "lastname": "Bianchi"

   },
   {
     "new_tag": "new"

Don't know where the issue is currently