Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/sync/editor/generic/util.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { CompressedPatch, Patch } from "./types";6import { diff_match_patch } from "@cocalc/util/dmp";78const dmp = new diff_match_patch();9dmp.Diff_Timeout = 0.2; // computing a diff won't block longer than about 0.2s1011// Here's what a diff-match-patch patch looks like12//13// [{"diffs":[[1,"{\"x\":5,\"y\":3}"]],"start1":0,"start2":0,"length1":0,"length2":13},...]14//1516// TODO: we must explicitly type these as "Function" or typescript gives errors.17// We should of course explicitly type the inputs and outputs of each, which18// will make other code more robust. See above and look at the source...19export const diff_main: Function = dmp.diff_main.bind(dmp);20export const patch_make: Function = dmp.patch_make.bind(dmp);2122// The diff-match-patch library changed the format, but we must keep it the same23// for backward compat and two stay JSON friendly.2425const Diff = diff_match_patch.Diff;2627function diffs_to_arrays(diffs: any[]): any[] {28const v: any[] = [];29for (const d of diffs) {30v.push([d[0], d[1]]);31}32return v;33}3435function arrays_to_diffs(arrays: any[]): any[] {36const v: any[] = [];37for (const x of arrays) {38v.push(new Diff(x[0], x[1]));39}40return v;41}4243export function compress_patch(patch: CompressedPatch): CompressedPatch {44return patch.map((p) => [45diffs_to_arrays(p.diffs),46p.start1,47p.start2,48p.length1,49p.length2,50]);51}5253export function decompress_patch(patch: CompressedPatch): CompressedPatch {54return patch.map((p) => ({55diffs: arrays_to_diffs(p[0]),56start1: p[1],57start2: p[2],58length1: p[3],59length2: p[4],60}));61}6263// return *a* compressed patch that transforms string s0 into string s1.64export function make_patch(s0: string, s1: string): CompressedPatch {65// @ts-ignore66return compress_patch(dmp.patch_make(s0, s1));67}6869// apply a compressed patch to a string.70export function apply_patch(71patch: CompressedPatch,72s: string,73): [string, boolean] {74let x;75try {76x = dmp.patch_apply(decompress_patch(patch), s);77//console.log('patch_apply ', misc.to_json(decompress_patch(patch)), x)78} catch (err) {79// If a patch is so corrupted it can't be parsed -- e.g., due to a bug in SMC -- we at least80// want to make application the identity map (i.e., "best effort"), so81// the document isn't completely unreadable!82console.warn(`apply_patch -- ${err}, ${JSON.stringify(patch)}`);83return [s, false];84}85let clean = true;86for (const a of x[1]) {87if (!a) {88clean = false;89break;90}91}92return [x[0], clean];93}9495import { cmp_array } from "@cocalc/util/misc";9697export function patch_cmp(a: Patch, b: Patch): number {98return cmp_array(99[a.time.valueOf(), a.user_id],100[b.time.valueOf(), b.user_id],101);102}103104export function time_cmp(a: Date, b: Date): number {105const t = a.valueOf() - b.valueOf();106if (t < 0) {107return -1;108} else if (t > 0) {109return 1;110} else {111return 0;112}113}114115// Do a 3-way **string** merge by computing patch that transforms116// base to remote, then applying that patch to local.117export function three_way_merge(opts: {118base: string;119local: string;120remote: string;121}): string {122if (opts.base === opts.remote) {123// trivial special case...124return opts.local;125}126// @ts-ignore127return dmp.patch_apply(dmp.patch_make(opts.base, opts.remote), opts.local)[0];128}129130131