codeToggleBlockComments function

TextCommandResult codeToggleBlockComments({
  1. required TextDocument document,
  2. required TextOffsetStateSnapshot state,
  3. required CodeLanguageProfile profile,
})

Implementation

TextCommandResult codeToggleBlockComments({
  required TextDocument document,
  required TextOffsetStateSnapshot state,
  required CodeLanguageProfile profile,
}) {
  final delimiters = profile.blockCommentDelimiters;
  if (delimiters == null || document.length == 0) {
    return _unchangedCodeResultFromDocument(document, state);
  }

  final selection = normalizedSelectionRange(
    state.selectionBaseOffset,
    state.selectionExtentOffset,
  );
  final hasSelection = selection != null && selection.start != selection.end;
  final cursor = document.positionForOffset(state.cursorOffset);
  final line = cursor.line.clamp(0, document.lineCount - 1);
  final start = hasSelection
      ? selection.start
      : document.offsetForPosition(TextPosition(line: line, column: 0));
  final end = hasSelection
      ? selection.end
      : document.offsetForPosition(
          TextPosition(line: line, column: document.lineLength(line)),
        );
  if (start >= end) {
    return _unchangedCodeResultFromDocument(document, state);
  }

  final clampedStart = start < 0
      ? 0
      : (start > document.length ? document.length : start);
  final clampedEnd = end < clampedStart
      ? clampedStart
      : (end > document.length ? document.length : end);
  if (clampedStart >= clampedEnd) {
    return _unchangedCodeResultFromDocument(document, state);
  }

  final startGraphemes = delimiters.start.characters.toList(growable: false);
  final endGraphemes = delimiters.end.characters.toList(growable: false);
  final segment = document.graphemesInRange(
    startOffset: clampedStart,
    endOffset: 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 _unchangedCodeResultFromDocument(document, state);
  }

  final working = document.copy();
  final result = edit_ops.replaceDocumentTextRange(
    working,
    start: clampedStart,
    end: clampedEnd,
    replacement: replacement.join(),
  );
  final nextSelectionStart = clampedStart + selectionStart;
  final nextSelectionEnd = clampedStart + selectionEnd;

  if (hasSelection) {
    return _codeResultFromDocument(
      working,
      cursorOffset: nextSelectionEnd,
      selectionBaseOffset: nextSelectionStart,
      selectionExtentOffset: nextSelectionEnd,
      documentChange: result.change,
      changed: result.changed,
    );
  }

  return _codeResultFromDocument(
    working,
    cursorOffset: nextSelectionEnd,
    documentChange: result.change,
    changed: result.changed,
  );
}