Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/util/dmp.ts
2208 views
1
import { DiffMatchPatch, type PatchObject } from "@cocalc/diff-match-patch";
2
export { DiffMatchPatch };
3
4
export type CompressedPatch = [
5
[-1 | 0 | 1, string][],
6
number,
7
number,
8
number,
9
number,
10
][];
11
12
const dmp = new DiffMatchPatch();
13
// computing a diff shouldn't block longer than about 0.2s, though
14
// due to the structure of the algorithms it can be a little worse.
15
dmp.diffTimeout = 0.2;
16
17
// Here's what a diff-match-patch patch looks like
18
//
19
// [{"diffs":[[1,"{\"x\":5,\"y\":3}"]],"start1":0,"start2":0,"length1":0,"length2":13},...]
20
//
21
22
export const diff_main = dmp.diff_main.bind(dmp);
23
export const patch_make = dmp.patch_make.bind(dmp);
24
25
function compress_patch(patch: PatchObject[]): CompressedPatch {
26
return patch.map((p) => [p.diffs, p.start1, p.start2, p.length1, p.length2]);
27
}
28
29
function decompress_patch(patch: CompressedPatch): PatchObject[] {
30
return patch.map((p) => ({
31
diffs: p[0],
32
start1: p[1],
33
start2: p[2],
34
length1: p[3],
35
length2: p[4],
36
}));
37
}
38
39
// return *a* compressed patch that transforms string s0 into string s1.
40
export function make_patch(s0: string, s1: string): CompressedPatch {
41
// @ts-ignore
42
return compress_patch(dmp.patch_make(s0, s1));
43
}
44
45
// apply a compressed patch to a string.
46
// Returns the result *and* whether or not the patch applied cleanly.
47
export function apply_patch(
48
patch: CompressedPatch,
49
s: string,
50
): [string, boolean] {
51
let x;
52
try {
53
x = dmp.patch_apply(decompress_patch(patch), s);
54
//console.log('patch_apply ', misc.to_json(decompress_patch(patch)), x)
55
} catch (err) {
56
// If a patch is so corrupted it can't be parsed -- e.g., due to a bug in SMC -- we at least
57
// want to make application the identity map (i.e., "best effort"), so
58
// the document isn't completely unreadable!
59
console.warn(`apply_patch -- ${err}, ${JSON.stringify(patch)}`);
60
return [s, false];
61
}
62
let clean = true;
63
for (const a of x[1]) {
64
if (!a) {
65
clean = false;
66
break;
67
}
68
}
69
return [x[0], clean];
70
}
71
72
// Do a 3-way **string** merge by computing patch that transforms
73
// base to remote, then applying that patch to local.
74
export function three_way_merge(opts: {
75
base: string;
76
local: string;
77
remote: string;
78
}): string {
79
if (opts.base === opts.remote) {
80
// trivial special case...
81
return opts.local;
82
}
83
// @ts-ignore
84
return dmp.patch_apply(dmp.patch_make(opts.base, opts.remote), opts.local)[0];
85
}
86
87