import * as Y from 'yjs'; import { TextChange, minimalChange } from '../util/textDiff'; const isHigh = (code: number) => code >= 0xd800 && code <= 0xdbff; const isLow = (code: number) => code >= 0xdc00 && code <= 0xdfff; /** * `minimalChange`, widened so it never splits a surrogate pair: Yjs would * replace half a pair with U+FFFD, and every copy would get the broken text. */ export function safeChange(before: string, after: string): TextChange | undefined { const change = minimalChange(before, after); if (!change) return undefined; let { from, to } = change; let endAfter = after.length - (before.length - to); // A common start ending in a high surrogate, or a common end starting with // a low one, would split a pair: take the whole character into the change. if (from > 0 && isHigh(before.charCodeAt(from - 1))) from--; if (to < before.length && isLow(before.charCodeAt(to))) { to++; endAfter++; } return { from, to, insert: after.slice(from, endAfter) }; } /** Makes the Y.Text read `after` with one minimal edit; false when it already does. */ export function applyToYText(text: Y.Text, after: string): boolean { const change = safeChange(text.toString(), after); if (!change) return false; if (change.to > change.from) text.delete(change.from, change.to - change.from); if (change.insert) text.insert(change.from, change.insert); return true; }