parse method
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;
}