Skip to content

Commit

Permalink
improved comparison algorithm, improved highlight logic
Browse files Browse the repository at this point in the history
  • Loading branch information
GrayHat12 committed Oct 11, 2023
1 parent 8929177 commit bf436c9
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 26 deletions.
113 changes: 92 additions & 21 deletions src/pages/Compare.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Loading from "./Loading";
import { BiSolidUpArrow, BiSolidDownArrow } from "react-icons/bi";
import { sortObj, cleanJSON } from "jsonabc";
import styles from "./compare.module.css";
import { JSONDiff, Difference, difference } from "../utils";
import { JSONDiff, Difference, differenceV2 } from "../utils";
import { parseJSONPath } from "../utils/butils";
import * as utils from "../utils";
import { useWorker, WORKER_STATUS } from "../worker";
Expand Down Expand Up @@ -49,40 +49,109 @@ function uniqueDifferenceFunction(differenceObject: Difference) {
// }
// })];
left.different.filter((x, index) => !left.different.find((y, _index) => y.startsWith(x) && index != _index)).forEach((path) => {
if (right.different.includes(path)) {
uniques.push({ pathLeft: path, pathRight: path });
}
uniques.push({ pathLeft: path, pathRight: path });
});
// tasks = [...tasks, ...left.extra.map(async (path) => {
// if (!right.extra.includes(path)) {
// uniques.push({ pathLeft: path });
// }
// })];
left.extra.forEach((path) => {
if (!right.extra.includes(path)) {
uniques.push({ pathLeft: path });
}
left.extra.filter((x, index) => !left.extra.find((y, _index) => y.startsWith(x) && index != _index)).forEach((path) => {
uniques.push({ pathLeft: path });
});
// tasks = [...tasks, ...right.extra.map(async (path) => {
// if (!left.extra.includes(path)) {
// uniques.push({ pathRight: path });
// }
// })];
right.extra.forEach((path) => {
if (!left.extra.includes(path)) {
uniques.push({ pathRight: path });
}
right.extra.filter((x, index) => !right.extra.find((y, _index) => y.startsWith(x) && index != _index)).forEach((path) => {
uniques.push({ pathRight: path });
});
// await Promise.all(tasks);
// if (uniqueDifferenceInterval) clearInterval(uniqueDifferenceInterval);
uniqueDifferenceInterval = undefined;
// uniqueDifferenceInterval = undefined;
// setUniqueDiff(uniques);
// setCurrentDifferenceIndex(uniques.length === 0 ? 0 : 1);
return { uniques, index: uniques.length === 0 ? 0 : 1 };
}

function uniqueDifferenceFunctionV2(differenceObject: Difference) {
let left = differenceObject.left;
let right = differenceObject.right;
let uniques: { pathLeft?: string; pathRight?: string; }[] = [];
// let tasks: Promise<void>[] = [];
// console.log("Finding unique difference", left, right);
// let a = left.different.filter(async (x, index) => !left.different.find((y, _index) => y.startsWith(x) && index != _index));
// tasks = [...tasks, ...left.different.filter((x, index) => !left.different.find((y, _index) => y.startsWith(x) && index != _index)).map(async (path) => {
// if (right.different.includes(path)) {
// uniques.push({ pathLeft: path, pathRight: path });
// }
// })];
left.different.forEach((path) => {
uniques.push({ pathLeft: path, pathRight: path });
});
// tasks = [...tasks, ...left.extra.map(async (path) => {
// if (!right.extra.includes(path)) {
// uniques.push({ pathLeft: path });
// }
// })];
left.missing.forEach((path) => {
uniques.push({ pathLeft: path });
});
// tasks = [...tasks, ...right.extra.map(async (path) => {
// if (!left.extra.includes(path)) {
// uniques.push({ pathRight: path });
// }
// })];
right.extra.filter((x, index) => !right.extra.find((y, _index) => y.startsWith(x) && index != _index)).forEach((path) => {
uniques.push({ pathRight: path });
});
// await Promise.all(tasks);
// if (uniqueDifferenceInterval) clearInterval(uniqueDifferenceInterval);
// uniqueDifferenceInterval = undefined;
// setUniqueDiff(uniques);
// setCurrentDifferenceIndex(uniques.length === 0 ? 0 : 1);
return { uniques, index: uniques.length === 0 ? 0 : 1 };
}

const STYLE_ID = "jsoneditor-styles-custom";

function generateStylesV2(editorid: string, classNames: {[classname: string]: string[]}) {
let style: string[] = [];
Object.keys(classNames).forEach(className => {
classNames[className].forEach(datapath => {
let selector = `#${editorid} div[data-path="${datapath}"] > div`;
let rules: string[] = [
"color:#292D1C!important",
"--jse-key-color:#292D1C!important",
"background-color:var(--background-color-custom)!important",
"--jse-selection-background-inactive-color:var(--background-color-custom)!important",
"--jse-value-color-string:#292D1C"
];
switch (className) {
case styles.different: {
rules.push('--jse-contents-background-color: #f6d283;');
break;
};
case styles.missing: {
rules.push('--jse-contents-background-color: #f69283;');
break;
};
case styles.extra: {
rules.push('--jse-contents-background-color: #c5da8b;');
break;
};
default: {
console.error("Should never happen", editorid, classNames, className);
break;
}
}
style.push(`${selector}{${rules.join(";")}}`);
});
});
return style.join('');
}

function generateStyles(editorid: string, classNames: { [classname: string]: string[] }) {
let style2 = `color:#292D1C!important;--jse-key-color:#292D1C!important;background-color:var(--background-color-custom)!important`;
let style3 = `color:#292D1C!important;--jse-selection-background-inactive-color:var(--background-color-custom)!important`;
Expand All @@ -107,7 +176,7 @@ function generateStyles(editorid: string, classNames: { [classname: string]: str
let val = `--background-color-custom:${className == styles.different ? "#F6D283" : className == styles.extra ? "#C5DA8B" : "#ED8373"}`
return selectors.size > 0 ? `${Array.from(selectors.values()).join(',')}{${val}}` : "";
});
return style1s.join('') + style2 + style3
return style1s.join('') + style2 + style3;
// let style = `
// #${editorid} div[data-path="${datapath}"] {
// --background-color-custom: ${className == styles.different ? "#F6D283" : className == styles.extra ? "#C5DA8B" : "#ED8373"};
Expand All @@ -133,8 +202,8 @@ export default function Compare() {
const [rightMode, setRightMode] = useState<Mode>(Mode.tree);
const [currentDifferenceIndex, setCurrentDifferenceIndex] = useState<number>(0);
const [loadingState, setLoadingState] = useState<boolean>(false);
const [uniqueDifferenceWorker, { status: differenceWorkerStatus, kill: killDifferenceWorker }] = useWorker(uniqueDifferenceFunction);
const [differenceFinderWorker, { status: differenceFinderWorkerStatus, kill: killDifferenceFinderWorker }] = useWorker(difference, {
const [uniqueDifferenceWorker, { status: differenceWorkerStatus, kill: killDifferenceWorker }] = useWorker(uniqueDifferenceFunctionV2);
const [differenceFinderWorker, { status: differenceFinderWorkerStatus, kill: killDifferenceFinderWorker }] = useWorker(differenceV2, {
localDependencies() {
return { ...utils, cache: {} }
},
Expand Down Expand Up @@ -320,7 +389,7 @@ export default function Compare() {
}

useEffect(() => {
// console.log("Running useEffect");
console.log("Running useEffect", differenceObject);
if (highlightJobInterval) clearInterval(highlightJobInterval);
highlightJobInterval = setInterval(regularHighlightJob, 100);
return () => {
Expand All @@ -337,11 +406,13 @@ export default function Compare() {
killDifferenceWorker();
console.log('Starting unique difference worker');
uniqueDifferenceWorker(differenceObject).then(result => {
console.log('uniques', result);
setUniqueDiff(result.uniques);
setCurrentDifferenceIndex(result.index);
}).catch(console.error).finally(() => {
if (uniqueDifferenceInterval) clearInterval(uniqueDifferenceInterval);
console.log("FINISHED");
});
if (uniqueDifferenceInterval) clearInterval(uniqueDifferenceInterval);
}

useEffect(() => {
Expand All @@ -352,7 +423,7 @@ export default function Compare() {
if (uniqueDifferenceInterval) {
clearInterval(uniqueDifferenceInterval);
}
uniqueDifferenceInterval = setInterval(uniqueDifferenceFunctionRunner, 20);
uniqueDifferenceInterval = setInterval(uniqueDifferenceFunctionRunner, 100);
return () => {
if (uniqueDifferenceInterval) clearInterval(uniqueDifferenceInterval);
};
Expand Down Expand Up @@ -386,12 +457,12 @@ export default function Compare() {
styleElement.id = STYLE_ID;
let [leftStyle, rightStyle] = await Promise.all(tasks);
let returnables = [leftStyle.returnable, rightStyle.returnable];
let leftcss = generateStyles(leftId, {
let leftcss = generateStylesV2(leftId, {
[styles.different]: leftStyle.result.find((x) => x.style === styles.different)?.paths || [],
[styles.extra]: leftStyle.result.find((x) => x.style === styles.extra)?.paths || [],
[styles.missing]: leftStyle.result.find((x) => x.style === styles.missing)?.paths || [],
});
let rightcss = generateStyles(rightId, {
let rightcss = generateStylesV2(rightId, {
[styles.different]: rightStyle.result.find((x) => x.style === styles.different)?.paths || [],
[styles.extra]: rightStyle.result.find((x) => x.style === styles.extra)?.paths || [],
[styles.missing]: rightStyle.result.find((x) => x.style === styles.missing)?.paths || [],
Expand Down
2 changes: 1 addition & 1 deletion src/utils/butils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function parseJSONPath(path: string): JSONPath {
index += 1;
start_index = index;
} else {
console.log('skipping', char);
// console.log('skipping', path, char);
index += 1;
}
} break;
Expand Down
97 changes: 93 additions & 4 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function cyrb53(str: string, seed: number = 0) {
};

export function hashCode(s: string) {
for(var i = 0, h = 0; i < s.length; i++)
for (var i = 0, h = 0; i < s.length; i++)
h = Math.imul(31, h) + s.charCodeAt(i) | 0;
return h;
}
Expand Down Expand Up @@ -97,17 +97,17 @@ export function normalizeArray(objects: any[], baseKey = "$") {
// normalized[`${baseKey}[${index}]`] = object;
// }
// }));
objects.forEach(async (object: any, index: number) => {
objects.forEach((object: any, index: number) => {
if (isPlainObject(object)) {
for (let [key, value] of Object.entries(await normalizeObject(object, `${baseKey}[${index}]`))) {
for (let [key, value] of Object.entries(normalizeObject(object, `${baseKey}[${index}]`))) {
normalized[key] = value;
}
// normalized = {
// ...normalized,
// ...await normalizeObject(object, `${baseKey}[${index}]`),
// };
} else if (isArray(object)) {
for (let [key, value] of Object.entries(await normalizeArray(object, `${baseKey}[${index}]`))) {
for (let [key, value] of Object.entries(normalizeArray(object, `${baseKey}[${index}]`))) {
normalized[key] = value;
}
// normalized = {
Expand Down Expand Up @@ -258,3 +258,92 @@ export function difference(jsona: any, jsonb: any) {
return null;
}
}

export function differenceV2(jsona: any, jsonb: any, baseKey: string = "$") {
if (!baseKey.endsWith(".")) {
baseKey += ".";
}
let diff: Difference = {
left: {
different: [],
extra: [],
missing: [],
},
right: {
different: [],
extra: [],
missing: []
},
};

if (isPlainObject(jsona) && isPlainObject(jsonb)) {
Object.keys(jsona).forEach(keya => {
let newkey = keya.replace("'", "\\'").replace('"', '\\"');
if (newkey.includes(".")) {
newkey = `"${newkey}"`;
}
let path = `${baseKey}${newkey}`;
if (typeof jsonb[keya] !== "undefined") {
let vala = jsona[keya];
let valb = jsonb[keya];
if (typeof vala !== typeof valb) {
diff.left.different.push(path);
diff.right.different.push(path);
} else if ((isPlainObject(vala) && isPlainObject(valb)) || (isArray(vala) && isArray(valb))) {
let _diff = differenceV2(vala, valb, path);
diff.left.different = diff.left.different.concat(_diff.left.different);
diff.right.different = diff.right.different.concat(_diff.right.different);
diff.left.extra = diff.left.extra.concat(_diff.left.extra);
diff.right.extra = diff.right.extra.concat(_diff.right.extra);
diff.left.missing = diff.left.missing.concat(_diff.left.missing);
diff.right.missing = diff.right.missing.concat(_diff.right.missing);
} else if (vala !== valb) {
diff.left.different.push(path);
diff.right.different.push(path);
}
}
else {
diff.left.missing.push(path);
// diff.right.extra.push(path);
}
});
Object.keys(jsonb).forEach(keyb => {
let newkey = keyb.replace("'", "\\'").replace('"', '\\"');
if (newkey.includes(".")) {
newkey = `"${newkey}"`;
}
let path = `${baseKey}${newkey}`;
if (typeof jsona[keyb] === "undefined") {
diff.right.extra.push(path);
}
});
} else if (isArray(jsona) && isArray(jsonb)) {
for (let i = 0; i < jsona.length; i++) {
let path = `${baseKey}.[${i}]`;
let vala = jsona[i];
let valb = jsonb[i];
if (typeof vala !== typeof valb) {
diff.left.different.push(path);
diff.right.different.push(path);
} else if ((isPlainObject(vala) && isPlainObject(valb)) || (isArray(vala) && isArray(valb))) {
let _diff = differenceV2(vala, valb, path);
diff.left.different = diff.left.different.concat(_diff.left.different);
diff.right.different = diff.right.different.concat(_diff.right.different);
diff.left.extra = diff.left.extra.concat(_diff.left.extra);
diff.right.extra = diff.right.extra.concat(_diff.right.extra);
diff.left.missing = diff.left.missing.concat(_diff.left.missing);
diff.right.missing = diff.right.missing.concat(_diff.right.missing);
} else if (vala !== valb) {
diff.left.different.push(path);
diff.right.different.push(path);
}
}
for (let i = jsona.length; i < jsonb.length; i++) {
let path = `${baseKey}.[${i}]`;
diff.right.extra.push(path);
}
} else {
throw Error("Invalid input JSON");
}
return diff;
}

0 comments on commit bf436c9

Please sign in to comment.