moveLinesAtSelections method

bool moveLinesAtSelections({
  1. required bool down,
})

Moves every touched line up or down by one row, keeping contiguous runs together.

Implementation

bool moveLinesAtSelections({required bool down}) {
  return _runEditFrame(() {
    _beginHistoryAction(_TextAreaHistoryAction.transform, breakChain: true);
    _refreshDocumentSnapshot();
    final indexes = _selectedLineIndexes().toList()..sort();
    if (indexes.isEmpty) return false;
    final runs = _contiguousLineRuns(indexes);
    final lines = [for (var i = 0; i < lineCount; i++) _document.lineAt(i)];
    var changed = false;
    final ordered = down ? runs.reversed : runs;
    for (final run in ordered) {
      if (down) {
        if (run.end >= lines.length - 1) continue;
        final block = lines.sublist(run.start, run.end + 1);
        lines.removeRange(run.start, run.end + 1);
        lines.insertAll(run.start + 1, block);
        changed = true;
      } else {
        if (run.start <= 0) continue;
        final block = lines.sublist(run.start, run.end + 1);
        lines.removeRange(run.start, run.end + 1);
        lines.insertAll(run.start - 1, block);
        changed = true;
      }
    }
    if (!changed) return false;
    final delta = down ? 1 : -1;
    final mapped = [
      for (final range in selections.ranges)
        (
          start: _document.positionForOffset(range.startOffset),
          end: _document.positionForOffset(range.endOffset),
          isReversed: range.isReversed,
        ),
    ];
    final primaryIndex = selections.ranges.indexOf(selections.primary!);
    _recordUndoSnapshot();
    // A trailing newline is represented by the final empty document line,
    // so joining all logical lines already preserves it.
    _replaceText(lines.join('\n'));
    TextSelectionRange relocate(
      ({TextPosition start, TextPosition end, bool isReversed}) positions,
    ) {
      final startLine = (positions.start.line + delta).clamp(
        0,
        lineCount - 1,
      );
      final endLine = (positions.end.line + delta).clamp(0, lineCount - 1);
      return TextSelectionRange(
        startOffset: _document.offsetForPosition(
          TextPosition(
            line: startLine,
            column: positions.start.column.clamp(
              0,
              _document.lineLength(startLine),
            ),
          ),
        ),
        endOffset: _document.offsetForPosition(
          TextPosition(
            line: endLine,
            column: positions.end.column.clamp(
              0,
              _document.lineLength(endLine),
            ),
          ),
        ),
        isReversed: positions.start != positions.end && positions.isReversed,
      );
    }

    final relocated = [for (final positions in mapped) relocate(positions)];
    setSelections(
      TextSelectionSet(
        relocated,
        primaryOffset: relocated.isEmpty
            ? 0
            : relocated[primaryIndex].activeOffset,
      ),
    );
    return true;
  });
}