Path: blob/master/src/packages/frontend/editors/slate/slate-diff/handle-change-one-node.ts
1698 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { isEqual } from "lodash";6import { copy_without } from "@cocalc/util/misc";7import { Node, Operation } from "slate";8import { slateDiff } from "./diff";910// Replace node at path by nextNode using the first11// strategy that works.12export function handleChangeOneNode(13node: Node,14nextNode: Node,15path: number[]16): Operation[] {17for (const strategy of STRATEGIES) {18const ops = strategy(node, nextNode, path);19if (ops != null) {20return ops;21}22}23throw Error("BUG");24}2526// We try each of the Handler functions listed below until one of them27// matches. When one does, that is used to compute the operations. At28// least one will, since the last one is a fallback that works for any29// input.3031type Handler = (32node: Node,33nextNode: Node,34path: number[]35) => Operation[] | undefined;3637const STRATEGIES: Handler[] = [];3839/*40Common special case -- only the children change:4142If we have two blocks and only the children change,43we recursively call our top level diff algorithm on44those children. */45STRATEGIES.push((node, nextNode, path) => {46if (47node["children"] != null &&48nextNode["children"] != null &&49isEqual(50copy_without(node, ["children"]),51copy_without(nextNode, ["children"])52)53) {54return slateDiff(node["children"], nextNode["children"], path);55}56});5758/* Common special case -- only the value property changes:5960A common special case is that one (or more) properties changes, e.g.,61when editing a fenced code block, checkbox, etc., the value62property changes but nothing else does. Using set_node we can63deal with anything changing except children/text.64*/65STRATEGIES.push((node, nextNode, path) => {66const properties: any = {};67const newProperties: any = {};68for (const key in node) {69if (!isEqual(node[key], nextNode[key])) {70if (key == "children" || key == "text") return; // can't do via set_node71properties[key] = node[key];72newProperties[key] = nextNode[key];73}74}75for (const key in nextNode) {76if (node[key] == undefined) {77if (key == "children" || key == "text") return; // can't do via set_node78newProperties[key] = nextNode[key];79}80}81// set_node can change everything except the children and text fields.82return [83{84type: "set_node",85path,86properties,87newProperties,88},89];90});9192// TODO: we could combine the above two, where children changes *and* any93// property changes (except text).94// I can't think of any case where that actually happens though.9596/*97Generic fallback strategy if nothing else works:9899Just remove and set, since that's the only thing to do generically.100We want to avoid this as much as possible, since it is not efficient101and breaks the cursor selection! This will always work though.102*/103// IMPORTANT: this must be added last!104STRATEGIES.push((node, nextNode, path) => {105return [106{107type: "remove_node",108path,109node,110},111{112type: "insert_node",113path,114node: nextNode,115},116];117});118119120