value property

  1. @override
TextEditingValue get value
inherited

The current value stored in this notifier.

When the value is replaced with something that is not equal to the old value as evaluated by the equality operator ==, this class notifies its listeners.

Implementation

@override
T get value => _value;
  1. @override
set value (TextEditingValue newValue)
override

Implementation

@override
set value(TextEditingValue newValue) {
  final TextEditingValue oldValue = super.value;
  const pairs = {'(': ')', '{': '}', '[': ']', '"': '"', "'": "'"};
  final openers = pairs.keys.toSet();
  final closers = pairs.values.toSet();

  if (newValue.text.length == oldValue.text.length + 1 &&
      newValue.selection.baseOffset > 0) {
    final cursorPos = newValue.selection.baseOffset;
    final inserted = newValue.text[cursorPos - 1];

    if (openers.contains(inserted)) {
      final closing = pairs[inserted]!;
      final before = newValue.text.substring(0, cursorPos);
      final after = newValue.text.substring(cursorPos);

      super.value = TextEditingValue(
        text: '$before$closing$after',
        selection: TextSelection.collapsed(offset: cursorPos),
      );
      return;
    }

    if (closers.contains(inserted)) {
      final oldCursorPos = oldValue.selection.baseOffset;
      if (oldCursorPos < oldValue.text.length) {
        final charAfterCursor = oldValue.text[oldCursorPos];
        if (charAfterCursor == inserted) {
          super.value = TextEditingValue(
            text: oldValue.text,
            selection: TextSelection.collapsed(offset: oldCursorPos + 1),
          );
          return;
        }
      }
    }
  }

  if (newValue.text.length > oldValue.text.length &&
      newValue.selection.baseOffset > 0 &&
      newValue.text
          .substring(0, newValue.selection.baseOffset)
          .endsWith('\n')) {
    final cursorPosition = newValue.selection.baseOffset;
    final textBeforeCursor = newValue.text.substring(0, cursorPosition);
    final textAfterCursor = newValue.text.substring(cursorPosition);

    final lines = textBeforeCursor.split('\n');
    if (lines.length < 2) {
      super.value = newValue;
      return;
    }
    final prevLine = lines[lines.length - 2];
    final indentMatch = RegExp(r'^\s*').firstMatch(prevLine);
    final prevIndent = indentMatch?.group(0) ?? '';
    final shouldIndent = RegExp(r'[:{[(]\s*$').hasMatch(prevLine);
    final extraIndent = shouldIndent ? ' ' * Shared().tabSize : '';
    final indent = prevIndent + extraIndent;
    final openToClose = {'{': '}', '(': ')', '[': ']'};
    final trimmedPrev = prevLine.trimRight();
    final lastChar = trimmedPrev.isNotEmpty
        ? trimmedPrev[trimmedPrev.length - 1]
        : null;
    final trimmedNext = textAfterCursor.trimLeft();
    final nextChar = trimmedNext.isNotEmpty ? trimmedNext[0] : null;
    final isBracketOpen = openToClose.containsKey(lastChar);
    final isNextClosing = isBracketOpen && openToClose[lastChar] == nextChar;
    String newText;
    int newOffset;
    if (isBracketOpen && isNextClosing) {
      newText = '$textBeforeCursor$indent\n$prevIndent$textAfterCursor';
      newOffset = cursorPosition + indent.length;
    } else {
      newText = '$textBeforeCursor$indent$textAfterCursor';
      newOffset = cursorPosition + indent.length;
    }

    super.value = TextEditingValue(
      text: newText,
      selection: TextSelection.collapsed(offset: newOffset),
    );
    return;
  }

  super.value = newValue;
}