backspaceAtAllCursors method

void backspaceAtAllCursors()

Performs backspace at all cursor positions (primary + secondary).

Implementation

void backspaceAtAllCursors() {
  if (readOnly || _multiCursors.isEmpty) {
    return;
  }

  _flushBuffer();

  final selectionBefore = _selection;
  final primaryOffset = selection.extentOffset.clamp(0, _rope.length);
  final offsets = <int>[primaryOffset];
  for (final c in _multiCursors) {
    offsets.add(_multiCursorToOffset(c).clamp(0, _rope.length));
  }

  // Sort descending so we delete from bottom to top
  final uniqueOffsets = offsets.toSet().toList()
    ..sort((a, b) => b.compareTo(a));

  // Begin compound undo operation
  final compound = _undoController?.beginCompoundOperation();

  // Running shift tracks how much earlier offsets shifted due to deletions
  // processed so far (descending order means no shift needed per-deletion,
  // but we need the deleted chars for undo recording).
  for (final offset in uniqueOffsets) {
    if (offset > 0) {
      final deleteStart = (offset - 1).clamp(0, _rope.length);
      final deletedChar = _rope.substring(deleteStart, offset);
      _rope.delete(deleteStart, offset);
      _currentVersion++;

      _recordDeletion(
        deleteStart,
        deletedChar,
        selectionBefore,
        TextSelection.collapsed(offset: deleteStart),
      );
    }
  }

  // End compound so all deletions are one undo unit
  compound?.end();

  int primaryShift = 0;
  for (final o in uniqueOffsets) {
    if (o <= primaryOffset && o > 0) {
      primaryShift += 1;
    }
  }
  _selection = TextSelection.collapsed(
    offset: (primaryOffset - primaryShift).clamp(0, _rope.length),
  );

  _multiCursors.clear();
  final sortedAsc = uniqueOffsets.reversed.toList();
  for (int i = 0; i < sortedAsc.length; i++) {
    final origOffset = sortedAsc[i];
    if (origOffset <= 0) continue;
    int shift = 0;
    for (int j = 0; j <= i; j++) {
      if (sortedAsc[j] > 0) shift++;
    }
    final newOffset = (origOffset - shift).clamp(0, _rope.length);
    final primaryNewOffset = (primaryOffset - primaryShift).clamp(
      0,
      _rope.length,
    );
    if (newOffset == primaryNewOffset) continue;
    final newLine = _rope.getLineAtOffset(newOffset);
    final newLineStart = _rope.getLineStartOffset(newLine);
    final newChar = newOffset - newLineStart;
    _multiCursors.add((line: newLine, character: newChar));
  }

  multiCursorsChanged = true;
  dirtyRegion = TextRange(start: 0, end: _rope.length);
  _syncToConnection();
  notifyListeners();
}