slate#MergeNodeOperation TypeScript Examples

The following examples show how to use slate#MergeNodeOperation. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From slate-ot with MIT License 6 votes vote down vote up
makeOp = {
  insertText: (
    path: Path,
    offset: number,
    text: string
  ): InsertTextOperation => {
    return {
      type: 'insert_text',
      path,
      offset,
      text,
    };
  },

  removeText: (
    path: Path,
    offset: number,
    text: string
  ): RemoveTextOperation => {
    return {
      type: 'remove_text',
      path,
      offset,
      text,
    };
  },

  insertNode: (path: Path, node: Node): InsertNodeOperation => {
    return {
      type: 'insert_node',
      path,
      node,
    };
  },

  removeNode: (path: Path, node: Node): RemoveNodeOperation => {
    return {
      type: 'remove_node',
      path,
      node,
    };
  },

  splitNode: (path: Path, position: number): SplitNodeOperation => {
    return {
      type: 'split_node',
      path,
      position,
      target: null,
      properties: {},
    };
  },

  mergeNode: (path: Path, position: number): MergeNodeOperation => {
    return {
      type: 'merge_node',
      path,
      position,
      target: null,
      properties: {},
    };
  },

  moveNode: (path: Path, newPath: Path): MoveNodeOperation => {
    return {
      type: 'move_node',
      path,
      newPath,
    };
  },

  setNode: (path: Path, newProperties: Partial<Node>): SetNodeOperation => {
    return {
      type: 'set_node',
      path,
      properties: {},
      newProperties,
    };
  },
}
Example #2
Source File: transInsertNode.ts    From slate-ot with MIT License 4 votes vote down vote up
transInsertNode = (
  leftOp: InsertNodeOperation,
  rightOp: Operation,
  side: 'left' | 'right'
): (InsertNodeOperation | SplitNodeOperation | MergeNodeOperation)[] => {
  switch (rightOp.type) {
    case 'insert_node': {
      if (Path.equals(leftOp.path, rightOp.path) && side === 'left') {
        return [leftOp];
      }

      return <InsertNodeOperation[]>pathTransform(leftOp, rightOp);
    }

    case 'remove_node': {
      if (Path.equals(leftOp.path, rightOp.path)) {
        return [leftOp];
      }

      return <InsertNodeOperation[]>pathTransform(leftOp, rightOp);
    }

    case 'split_node': {
      if (Path.equals(leftOp.path, rightOp.path)) {
        return [leftOp];
      }

      return <InsertNodeOperation[]>pathTransform(leftOp, rightOp);
    }

    case 'merge_node': {
      if (Path.equals(leftOp.path, rightOp.path)) {
        const offset = Text.isText(leftOp.node)
          ? leftOp.node.text.length
          : leftOp.node.children.length;
        return [
          {
            ...rightOp,
            type: 'split_node',
            path: Path.previous(rightOp.path),
          },
          leftOp,
          rightOp,
          {
            ...rightOp,
            position: rightOp.position + offset,
          },
        ];
      }

      return <InsertNodeOperation[]>pathTransform(leftOp, rightOp);
    }

    case 'move_node': {
      if (Path.equals(rightOp.path, rightOp.newPath)) {
        return [leftOp];
      }

      // the anchor node is moved, but we don't want to move with the anchor
      // hence the next of anchor is chosen as the new anchor
      if (Path.equals(leftOp.path, rightOp.path)) {
        return [
          {
            ...leftOp,
            path: Path.transform(Path.next(leftOp.path), rightOp)!,
          },
        ];
      }

      const [rr, ri] = decomposeMove(rightOp);
      const [l] = xTransformMxN([leftOp], [rr, ri], side);

      if (l.length == 1) return <InsertNodeOperation[]>l;

      // l.length == 0, the parent node is moved
      // in this case rr and ri will not be transformed by leftOp
      return [
        {
          ...leftOp,
          path: ri.path.concat(leftOp.path.slice(rr.path.length)),
        },
      ];
    }

    // insert_text
    // remove_text
    // set_node
    default:
      return <InsertNodeOperation[]>pathTransform(leftOp, rightOp);
  }
}
Example #3
Source File: transMergeNode.ts    From slate-ot with MIT License 4 votes vote down vote up
transMergeNode = (
  leftOp: MergeNodeOperation,
  rightOp: Operation,
  _side: 'left' | 'right'
): (MergeNodeOperation | MoveNodeOperation | RemoveNodeOperation)[] => {
  switch (rightOp.type) {
    case 'insert_text': {
      if (Path.equals(leftOp.path, Path.next(rightOp.path))) {
        return [
          {
            ...leftOp,
            position: leftOp.position + rightOp.text.length,
          },
        ];
      }

      return [leftOp];
    }

    case 'remove_text': {
      if (Path.equals(leftOp.path, Path.next(rightOp.path))) {
        return [
          {
            ...leftOp,
            position: leftOp.position - rightOp.text.length,
          },
        ];
      }

      return [leftOp];
    }

    case 'insert_node': {
      if (Path.equals(leftOp.path, rightOp.path)) {
        const offset = Text.isText(rightOp.node)
          ? rightOp.node.text.length
          : rightOp.node.children.length;

        return [
          leftOp, // merge the inserted node
          {
            ...leftOp, // merge the original node
            position: leftOp.position + offset,
          },
        ];
      }

      if (Path.isParent(Path.previous(leftOp.path), rightOp.path)) {
        return [
          {
            ...leftOp,
            position: leftOp.position + 1,
          },
        ];
      }

      return <MergeNodeOperation[]>pathTransform(leftOp, rightOp);
    }

    case 'remove_node': {
      if (Path.isParent(Path.previous(leftOp.path), rightOp.path)) {
        return [
          {
            ...leftOp,
            position: leftOp.position - 1,
          },
        ];
      }

      const path = Path.transform(leftOp.path, rightOp);
      const prevPath = Path.transform(Path.previous(leftOp.path), rightOp);

      if (path && prevPath) {
        return [
          {
            ...leftOp,
            path,
          },
        ];
      }

      // conflicting ops, we have to discard merge
      // for now we simply remove the merged node
      else if (!path && prevPath) {
        return [
          {
            ...rightOp,
            path: prevPath,
          },
        ];
      } else if (path && !prevPath) {
        return [
          {
            ...rightOp,
            path,
          },
        ];
      }

      // both to-merge nodes are removed
      else {
        return [];
      }
    }

    case 'split_node': {
      if (Path.equals(leftOp.path, rightOp.path)) {
        return [
          leftOp,
          {
            ...leftOp,
            position: leftOp.position + rightOp.position,
          },
        ];
      }

      if (Path.equals(Path.previous(leftOp.path), rightOp.path)) {
        return [
          {
            ...leftOp,
            path: Path.next(leftOp.path),
            position: leftOp.position - rightOp.position,
          },
        ];
      }

      if (Path.isParent(Path.previous(leftOp.path), rightOp.path)) {
        return [
          {
            ...leftOp,
            position: leftOp.position + 1,
          },
        ];
      }

      const path = Path.transform(leftOp.path, rightOp)!;

      // conflicting ops, we choose to discard split
      if (path[path.length - 1] === 0) {
        return [
          {
            ...rightOp,
            type: 'merge_node',
            path: Path.next(rightOp.path),
          },
          leftOp,
        ];
      }

      return <MergeNodeOperation[]>pathTransform(leftOp, rightOp);
    }

    case 'merge_node': {
      if (Path.equals(leftOp.path, rightOp.path)) {
        return [];
      }

      if (Path.equals(Path.previous(leftOp.path), rightOp.path)) {
        return [
          {
            ...leftOp,
            path: Path.previous(leftOp.path),
            position: leftOp.position + rightOp.position,
          },
        ];
      }

      if (Path.isParent(Path.previous(leftOp.path), rightOp.path)) {
        return [
          {
            ...leftOp,
            position: leftOp.position - 1,
          },
        ];
      }

      return <MergeNodeOperation[]>pathTransform(leftOp, rightOp);
    }

    case 'move_node': {
      if (Path.equals(rightOp.path, rightOp.newPath)) {
        return [leftOp];
      }

      const path = leftOp.path;
      const prevPath = Path.previous(path);

      const newPath = Path.transform(path, rightOp)!;
      const newPrevPath = Path.transform(prevPath, rightOp)!;

      // Ops conflict with each other, discard merge.
      //   Note that the merge-and-split node cannot keep properties,
      //   so we have to remove it.
      if (!Path.equals(newPath, Path.next(newPrevPath))) {
        return [
          {
            type: 'remove_node',
            path: newPath,
            node: { text: '' },
          },
        ];
      }

      let position = leftOp.position;

      // only src path is leftOp's child
      if (Path.isParent(prevPath, rightOp.path)) {
        position--;
      }

      // only dst path is leftOp's child
      if (Path.isParent(prevPath, rightOp.newPath)) {
        position++;
      }

      return [
        {
          ...leftOp,
          path: newPath,
          position,
        },
      ];
    }

    // set_node
    default:
      return <MergeNodeOperation[]>pathTransform(leftOp, rightOp);
  }
}