deleteSelection method

Future<bool> deleteSelection(
  1. Selection selection
)

Deletes the selection.

If the selection is collapsed, this function does nothing.

If the node contains a delta, this function deletes the text in the selection, else the node does not contain a delta, this function deletes the node.

If both the first node and last node contain a delta, this function merges the delta of the last node into the first node, and deletes the nodes in between.

If only the first node contains a delta, this function deletes the text in the first node, and deletes the nodes expect for the first node.

For the other cases, this function just deletes all the nodes.

Implementation

Future<bool> deleteSelection(Selection selection) async {
  // Nothing to do if the selection is collapsed.
  if (selection.isCollapsed) {
    return false;
  }

  // Normalize the selection so that it is never reversed or extended.
  selection = selection.normalized;

  // Start a new transaction.
  final transaction = this.transaction;

  // Get the nodes that are fully or partially selected.
  final nodes = getNodesInSelection(selection);

  // If only one node is selected, then we can just delete the selected text
  // or node.
  if (nodes.length == 1) {
    // If table cell is selected, clear the cell node child.
    final node = nodes.first.type == TableCellBlockKeys.type
        ? nodes.first.children.first
        : nodes.first;
    if (node.delta != null) {
      transaction.deleteText(
        node,
        selection.startIndex,
        selection.length,
      );
    } else if (node.parent?.type != TableCellBlockKeys.type) {
      transaction.deleteNode(node);
    }
  }

  // Otherwise, multiple nodes are selected, so we have to do more work.
  else {
    // The nodes are guaranteed to be in order, so we can determine which
    // nodes are at the beginning, middle, and end of the selection.
    assert(nodes.first.path < nodes.last.path);
    for (var i = 0; i < nodes.length; i++) {
      final node = nodes[i];

      // The first node is at the beginning of the selection.
      // All other nodes can be deleted.
      if (i != 0) {
        // Never delete a table cell node child
        if (node.parent?.type == TableCellBlockKeys.type) {
          if (!nodes.any((n) => n.id == node.parent?.parent?.id)) {
            transaction.deleteText(
              node,
              0,
              selection.end.offset,
            );
          }
        }
        // If first node was inside table cell then it wasn't mergable to last
        // node, So we should not delete the last node. Just delete part of
        // the text inside selection
        else if (node.id == nodes.last.id &&
            nodes.first.parent?.type == TableCellBlockKeys.type) {
          transaction.deleteText(
            node,
            0,
            selection.end.offset,
          );
        } else if (node.type != TableCellBlockKeys.type) {
          transaction.deleteNode(node);
        }
        continue;
      }

      // If the last node is also a text node and not a node inside table cell,
      // and also the current node isn't inside table cell, then we can merge
      // the text between the two nodes.
      if (nodes.last.delta != null &&
          ![node.parent?.type, nodes.last.parent?.type]
              .contains(TableCellBlockKeys.type)) {
        transaction.mergeText(
          node,
          nodes.last,
          leftOffset: selection.startIndex,
          rightOffset: selection.endIndex,
        );

        // combine the children of the last node into the first node.
        final last = nodes.last;

        if (last.children.isNotEmpty) {
          if (indentableBlockTypes.contains(node.type)) {
            transaction.insertNodes(
              node.path + [0],
              last.children,
              deepCopy: true,
            );
          } else {
            transaction.insertNodes(
              node.path.next,
              last.children,
              deepCopy: true,
            );
          }
        }
      }

      // Otherwise, we can just delete the selected text.
      else {
        // If the last or first node is inside table we will only delete
        // selection part of first node.
        if (nodes.last.parent?.type == TableCellBlockKeys.type ||
            node.parent?.type == TableCellBlockKeys.type) {
          transaction.deleteText(
            node,
            selection.startIndex,
            node.delta!.length - selection.startIndex,
          );
        } else {
          transaction.deleteText(
            node,
            selection.startIndex,
            selection.length,
          );
        }
      }
    }
  }

  // After the selection is deleted, we want to move the selection to the
  // beginning of the deleted selection.
  transaction.afterSelection = selection.collapse(atStart: true);
  Log.editor
      .debug(transaction.operations.map((e) => e.toString()).toString());

  // Apply the transaction.
  await apply(transaction);

  return true;
}