toggleDelimitedSegment function

TextCommandResult toggleDelimitedSegment(
  1. List<String> graphemes, {
  2. required int cursorOffset,
  3. int? selectionBaseOffset,
  4. int? selectionExtentOffset,
  5. required int rangeStartOffset,
  6. required int rangeEndOffset,
  7. required String startDelimiter,
  8. required String endDelimiter,
})

Implementation

TextCommandResult toggleDelimitedSegment(
  List<String> graphemes, {
  required int cursorOffset,
  int? selectionBaseOffset,
  int? selectionExtentOffset,
  required int rangeStartOffset,
  required int rangeEndOffset,
  required String startDelimiter,
  required String endDelimiter,
}) {
  final selection = normalizedSelectionRange(
    selectionBaseOffset,
    selectionExtentOffset,
  );
  final hasSelection = selection != null && selection.start != selection.end;
  final start = hasSelection ? selection.start : rangeStartOffset;
  final end = hasSelection ? selection.end : rangeEndOffset;
  if (start >= end) {
    return _unchangedResult(
      graphemes,
      cursorOffset: cursorOffset,
      selectionBaseOffset: selectionBaseOffset,
      selectionExtentOffset: selectionExtentOffset,
    );
  }

  final clampedStart = start.clamp(0, graphemes.length);
  final clampedEnd = end.clamp(0, graphemes.length);
  if (clampedStart >= clampedEnd) {
    return _unchangedResult(
      graphemes,
      cursorOffset: cursorOffset,
      selectionBaseOffset: selectionBaseOffset,
      selectionExtentOffset: selectionExtentOffset,
    );
  }

  final startGraphemes = startDelimiter.characters.toList(growable: false);
  final endGraphemes = endDelimiter.characters.toList(growable: false);
  final segment = graphemes.sublist(clampedStart, clampedEnd);
  final leadingWhitespace = _leadingWhitespaceCount(segment);
  final trailingWhitespace = _trailingWhitespaceCount(segment);
  final coreEnd = segment.length - trailingWhitespace;
  final core = segment.sublist(leadingWhitespace, coreEnd);

  List<String> replacement;
  var selectionStart = leadingWhitespace;
  late int selectionEnd;

  if (_startsWithGraphemeSequence(core, startGraphemes) &&
      _endsWithGraphemeSequence(core, endGraphemes)) {
    var innerStart = leadingWhitespace + startGraphemes.length;
    var innerEnd = coreEnd - endGraphemes.length;
    if (innerStart < innerEnd && segment[innerStart] == ' ') {
      innerStart++;
    }
    if (innerStart < innerEnd && segment[innerEnd - 1] == ' ') {
      innerEnd--;
    }

    replacement = <String>[
      ...segment.sublist(0, leadingWhitespace),
      ...segment.sublist(innerStart, innerEnd),
      ...segment.sublist(coreEnd),
    ];
    selectionEnd = selectionStart + (innerEnd - innerStart);
  } else {
    final separator = core.isEmpty ? const <String>[] : const <String>[' '];
    replacement = <String>[
      ...segment.sublist(0, leadingWhitespace),
      ...startGraphemes,
      ...separator,
      ...core,
      ...separator,
      ...endGraphemes,
      ...segment.sublist(coreEnd),
    ];
    selectionEnd =
        selectionStart +
        startGraphemes.length +
        separator.length +
        core.length +
        separator.length +
        endGraphemes.length;
  }

  if (_listStringEquals(replacement, segment)) {
    return _unchangedResult(
      graphemes,
      cursorOffset: cursorOffset,
      selectionBaseOffset: selectionBaseOffset,
      selectionExtentOffset: selectionExtentOffset,
    );
  }

  final result = edit_ops.replaceRange(
    graphemes,
    start: clampedStart,
    end: clampedEnd,
    replacement: replacement,
  );
  final nextSelectionStart = clampedStart + selectionStart;
  final nextSelectionEnd = clampedStart + selectionEnd;

  if (hasSelection) {
    return TextCommandResult(
      graphemes: result.graphemes,
      cursorOffset: nextSelectionEnd,
      selectionBaseOffset: nextSelectionStart,
      selectionExtentOffset: nextSelectionEnd,
    );
  }

  return TextCommandResult(
    graphemes: result.graphemes,
    cursorOffset: nextSelectionEnd,
  );
}