toggleHeadingPrefix function

TextLineCommandResult toggleHeadingPrefix(
  1. List<String> lines, {
  2. required TextPosition cursor,
  3. TextPosition? selectionBase,
  4. TextPosition? selectionExtent,
  5. int level = 1,
})

Implementation

TextLineCommandResult toggleHeadingPrefix(
  List<String> lines, {
  required TextPosition cursor,
  TextPosition? selectionBase,
  TextPosition? selectionExtent,
  int level = 1,
}) {
  final lineTexts = lines.isEmpty ? <String>[''] : List<String>.from(lines);
  final clampedCursor = _clampPositionToLines(cursor, lineTexts);
  final selection = selectionBase != null && selectionExtent != null
      ? TextSelection(base: selectionBase, extent: selectionExtent)
      : null;
  final hasSelection = selection != null && !selection.isCollapsed;
  final startLine = hasSelection ? selection.start.line : clampedCursor.line;
  final endLine = hasSelection ? selection.end.line : clampedCursor.line;
  final targetLevel = level.clamp(1, 6);
  final targetPrefix = '${'#' * targetLevel} ';

  var hasRelevantLine = false;
  var allAtTargetLevel = true;
  for (var lineIndex = startLine; lineIndex <= endLine; lineIndex++) {
    final line = lineTexts[lineIndex];
    final leadingWhitespace = line.length - line.trimLeft().length;
    final body = line.substring(leadingWhitespace);
    final headingPrefix = _leadingHeadingPrefix(body);
    if (body.trim().isEmpty && headingPrefix == null) {
      continue;
    }
    hasRelevantLine = true;
    if (headingPrefix == null || headingPrefix.level != targetLevel) {
      allAtTargetLevel = false;
      break;
    }
  }
  if (!hasRelevantLine) {
    return _unchangedLineResult(
      lineTexts,
      cursor: clampedCursor,
      selectionBase: selectionBase,
      selectionExtent: selectionExtent,
    );
  }

  var adjustedBase = selectionBase;
  var adjustedExtent = selectionExtent;
  var nextCursor = clampedCursor;

  for (var lineIndex = startLine; lineIndex <= endLine; lineIndex++) {
    final line = lineTexts[lineIndex];
    final leadingWhitespace = line.length - line.trimLeft().length;
    final indent = line.substring(0, leadingWhitespace);
    final body = line.substring(leadingWhitespace);
    final headingPrefix = _leadingHeadingPrefix(body);
    if (body.trim().isEmpty && headingPrefix == null) {
      continue;
    }

    final removeCount = headingPrefix?.length ?? 0;
    final nextBody = removeCount > 0 ? body.substring(removeCount) : body;
    final delta = allAtTargetLevel
        ? -removeCount
        : targetPrefix.length - removeCount;
    lineTexts[lineIndex] = allAtTargetLevel
        ? '$indent$nextBody'
        : '$indent$targetPrefix$nextBody';

    if (delta == 0) {
      continue;
    }

    if (lineIndex == nextCursor.line) {
      nextCursor = TextPosition(
        line: nextCursor.line,
        column: _adjustLinePrefixColumnDelta(
          column: nextCursor.column,
          leadingWhitespace: leadingWhitespace,
          delta: delta,
        ),
      );
    }
    adjustedBase = _adjustLinePrefixPositionDelta(
      adjustedBase,
      line: lineIndex,
      leadingWhitespace: leadingWhitespace,
      delta: delta,
    );
    adjustedExtent = _adjustLinePrefixPositionDelta(
      adjustedExtent,
      line: lineIndex,
      leadingWhitespace: leadingWhitespace,
      delta: delta,
    );
  }

  if (_listStringEquals(lineTexts, lines)) {
    return _unchangedLineResult(
      lineTexts,
      cursor: clampedCursor,
      selectionBase: selectionBase,
      selectionExtent: selectionExtent,
    );
  }

  if (hasSelection && adjustedBase != null && adjustedExtent != null) {
    final clampedBase = _clampPositionToLines(adjustedBase, lineTexts);
    final clampedExtent = _clampPositionToLines(adjustedExtent, lineTexts);
    return TextLineCommandResult(
      lines: lineTexts,
      cursor: clampedExtent,
      selectionBase: clampedBase,
      selectionExtent: clampedExtent,
    );
  }

  return TextLineCommandResult(
    lines: lineTexts,
    cursor: _clampPositionToLines(nextCursor, lineTexts),
  );
}