parse method

List<InputEvent> parse(
  1. List<int> chunk
)

Parses a chunk of bytes and returns all decoded InputEvents.

Implementation

List<InputEvent> parse(List<int> chunk) {
  _buffer.addAll(chunk);
  final events = <InputEvent>[];

  while (_buffer.isNotEmpty) {
    if (_isPasting) {
      // Look for end of bracketed paste: \x1b[201~ (bytes: 27, 91, 50, 48, 49, 126)
      int endIdx = -1;
      for (var i = 0; i <= _buffer.length - 6; i++) {
        if (_buffer[i] == 27 &&
            _buffer[i + 1] == 91 &&
            _buffer[i + 2] == 50 &&
            _buffer[i + 3] == 48 &&
            _buffer[i + 4] == 49 &&
            _buffer[i + 5] == 126) {
          endIdx = i;
          break;
        }
      }

      if (endIdx == -1) {
        final safeLength = _buffer.length >= 6 ? _buffer.length - 5 : 0;
        if (safeLength > 0) {
          _pasteBuffer.addAll(_buffer.sublist(0, safeLength));
          _buffer.removeRange(0, safeLength);
        }
        break;
      } else {
        _pasteBuffer.addAll(_buffer.sublist(0, endIdx));
        events.add(PasteEvent(String.fromCharCodes(_pasteBuffer)));
        _pasteBuffer.clear();
        _isPasting = false;
        _buffer.removeRange(0, endIdx + 6);
      }
      continue;
    }

    if (_buffer[0] != 27) {
      final b = _buffer.removeAt(0);

      if (b == 8 || b == 127) {
        if (!_backspaceDetected) {
          _backspaceIs8 = (b == 8);
          _backspaceDetected = true;
        }

        if (b == (_backspaceIs8 ? 8 : 127)) {
          events.add(const KeyEvent('backspace', KeyType.backspace));
        } else {
          events.add(
            const KeyEvent(
              'backspace',
              KeyType.backspace,
              modifiers: {Modifier.control},
            ),
          );
        }
      } else if (b == 9) {
        events.add(const KeyEvent('\t', KeyType.tab));
      } else if (b == 10 || b == 13) {
        events.add(const KeyEvent('\n', KeyType.enter));
      } else if (b >= 1 && b <= 26) {
        final char = String.fromCharCode(b + 96);
        events.add(
          KeyEvent(
            char,
            KeyType.character,
            modifiers: const {Modifier.control},
          ),
        );
      } else {
        final char = String.fromCharCode(b);
        events.add(KeyEvent(char, KeyType.character));
      }
    } else {
      if (_buffer.length == 1) {
        // Since TTY delivers full sequences together, a single ESC here means standalone ESC
        _buffer.removeAt(0);
        events.add(const KeyEvent('escape', KeyType.escape));
        break;
      }

      final second = _buffer[1];
      if (second == 91) {
        int termIdx = -1;
        for (var i = 2; i < _buffer.length; i++) {
          final b = _buffer[i];
          if (b >= 0x40 && b <= 0x7E) {
            termIdx = i;
            break;
          }
        }

        if (termIdx == -1) {
          break;
        }

        final seqBytes = _buffer.sublist(0, termIdx + 1);
        final termByte = seqBytes.last;
        final seqStr = String.fromCharCodes(seqBytes);

        if (seqStr.startsWith('\x1b[<')) {
          _buffer.removeRange(0, termIdx + 1);
          final mouseEvent = _parseSgrMouse(seqStr, termByte);
          if (mouseEvent != null) {
            events.add(mouseEvent);
          }
        } else if (seqStr.startsWith('\x1b[M')) {
          if (_buffer.length < 6) {
            break;
          }
          final x10Bytes = _buffer.sublist(3, 6);
          _buffer.removeRange(0, 6);
          events.add(_parseX10Mouse(x10Bytes));
        } else {
          _buffer.removeRange(0, termIdx + 1);
          final csiEvent = _parseCsiKey(seqStr, termByte);
          if (csiEvent != null) {
            events.add(csiEvent);
          }
        }
      } else if (second == 79) {
        if (_buffer.length < 3) {
          break;
        }
        final term = _buffer[2];
        _buffer.removeRange(0, 3);
        events.add(_parseSs3Key(term));
      } else {
        _buffer.removeRange(0, 2);
        final charStr = String.fromCharCode(second);
        events.add(
          KeyEvent(
            charStr,
            KeyType.character,
            modifiers: const {Modifier.alt},
          ),
        );
      }
    }
  }

  return events;
}