insertAtAllCursors method
Inserts textToInsert at all cursor positions (primary + secondary).
Insertions are performed from the last (highest-offset) cursor to the first so that earlier offsets are not invalidated by later insertions. After insertion, all cursor positions are updated to sit after the inserted text.
Implementation
void insertAtAllCursors(String textToInsert) {
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 insert from bottom to top
final uniqueOffsets = offsets.toSet().toList()
..sort((a, b) => b.compareTo(a));
// Begin compound undo operation
final compound = _undoController?.beginCompoundOperation();
for (final offset in uniqueOffsets) {
final safeOffset = offset.clamp(0, _rope.length);
_rope.insert(safeOffset, textToInsert);
_currentVersion++;
_recordInsertion(
safeOffset,
textToInsert,
selectionBefore,
TextSelection.collapsed(offset: safeOffset + textToInsert.length),
);
}
// End compound so all insertions are one undo unit
compound?.end();
int primaryShift = 0;
for (final o in uniqueOffsets) {
if (o <= primaryOffset) {
primaryShift += textToInsert.length;
}
}
_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 newOffset = sortedAsc[i] + (i + 1) * textToInsert.length;
final safeNewOffset = newOffset.clamp(0, _rope.length);
final newLine = _rope.getLineAtOffset(safeNewOffset);
final newLineStart = _rope.getLineStartOffset(newLine);
final newChar = safeNewOffset - newLineStart;
final primaryNewOffset = primaryOffset + primaryShift;
if (safeNewOffset == primaryNewOffset) continue;
_multiCursors.add((line: newLine, character: newChar));
}
multiCursorsChanged = true;
dirtyRegion = TextRange(start: 0, end: _rope.length);
_syncToConnection();
notifyListeners();
}