moveCursor method

void moveCursor(
  1. SelectionMoveDirection direction, [
  2. SelectionMoveRange range = SelectionMoveRange.character
])

Implementation

void moveCursor(
  SelectionMoveDirection direction, [
  SelectionMoveRange range = SelectionMoveRange.character,
]) {
  final selection = this.selection?.normalized;
  if (selection == null) {
    return;
  }

  // If the selection is not collapsed, then we want to collapse the selection
  if (!selection.isCollapsed && range != SelectionMoveRange.line) {
    // move the cursor to the start or end of the selection
    this.selection = selection.collapse(
      atStart: direction == SelectionMoveDirection.forward,
    );
    return;
  }

  final node = getNodeAtPath(selection.start.path);
  if (node == null) {
    return;
  }

  // Originally, I want to make this function as pure as possible,
  //  but I have to import the selectable here to compute the selection.
  final start = node.selectable?.start();
  final end = node.selectable?.end();
  final offset = direction == SelectionMoveDirection.forward
      ? selection.startIndex
      : selection.endIndex;
  {
    // the cursor is at the start of the node
    // move the cursor to the end of the previous node
    if (direction == SelectionMoveDirection.forward &&
        start != null &&
        start.offset >= offset) {
      final previousEnd = node
          .previousNodeWhere((element) => element.selectable != null)
          ?.selectable
          ?.end();
      if (previousEnd != null) {
        updateSelectionWithReason(
          Selection.collapsed(previousEnd),
          reason: SelectionUpdateReason.uiEvent,
        );
      }
      return;
    }
    // the cursor is at the end of the node
    // move the cursor to the start of the next node
    else if (direction == SelectionMoveDirection.backward &&
        end != null &&
        end.offset <= offset) {
      final nextStart = node.next?.selectable?.start();
      if (nextStart != null) {
        updateSelectionWithReason(
          Selection.collapsed(nextStart),
          reason: SelectionUpdateReason.uiEvent,
        );
      }
      return;
    }
  }

  final delta = node.delta;
  switch (range) {
    case SelectionMoveRange.character:
      if (delta != null) {
        // move the cursor to the left or right by one character
        updateSelectionWithReason(
          Selection.collapsed(
            selection.start.copyWith(
              offset: direction == SelectionMoveDirection.forward
                  ? delta.prevRunePosition(offset)
                  : delta.nextRunePosition(offset),
            ),
          ),
          reason: SelectionUpdateReason.uiEvent,
        );
      } else {
        throw UnimplementedError();
      }
      break;
    case SelectionMoveRange.word:
      final delta = node.delta;
      if (delta != null) {
        final position = direction == SelectionMoveDirection.forward
            ? Position(
                path: node.path,
                offset: delta.prevRunePosition(offset),
              )
            : selection.start;
        // move the cursor to the left or right by one line
        final wordSelection =
            node.selectable?.getWordBoundaryInPosition(position);
        if (wordSelection != null) {
          updateSelectionWithReason(
            Selection.collapsed(
              direction == SelectionMoveDirection.forward
                  ? wordSelection.start
                  : wordSelection.end,
            ),
            reason: SelectionUpdateReason.uiEvent,
          );
        }
      } else {
        throw UnimplementedError();
      }

      break;
    case SelectionMoveRange.line:
      if (delta != null) {
        // move the cursor to the left or right by one line
        updateSelectionWithReason(
          Selection.collapsed(
            selection.start.copyWith(
              offset: direction == SelectionMoveDirection.forward
                  ? 0
                  : delta.length,
            ),
          ),
          reason: SelectionUpdateReason.uiEvent,
        );
      } else {
        throw UnimplementedError();
      }
      break;
    default:
      throw UnimplementedError();
  }
}