use super::key::{Key, KeyEvent, Modifiers};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseResult {
Key(KeyEvent, usize),
NeedMore,
Passthrough(u8),
}
pub struct KeyParser {
buffer: Vec<u8>,
}
impl KeyParser {
pub fn new() -> Self {
Self { buffer: Vec::new() }
}
pub fn push(&mut self, bytes: &[u8]) {
self.buffer.extend_from_slice(bytes);
}
pub fn has_pending(&self) -> bool {
!self.buffer.is_empty()
}
#[allow(dead_code)]
pub fn take_pending(&mut self) -> Vec<u8> {
std::mem::take(&mut self.buffer)
}
pub fn parse_next(&mut self) -> ParseResult {
if self.buffer.is_empty() {
return ParseResult::NeedMore;
}
let result = parse_bytes(&self.buffer);
match result {
ParseResult::Key(key, consumed) => {
self.buffer.drain(..consumed);
ParseResult::Key(key, consumed)
}
ParseResult::Passthrough(b) => {
self.buffer.remove(0);
ParseResult::Passthrough(b)
}
ParseResult::NeedMore => ParseResult::NeedMore,
}
}
pub fn force_parse_first(&mut self) -> Option<ParseResult> {
if self.buffer.is_empty() {
return None;
}
let byte = self.buffer[0];
if byte == 0x1b {
self.buffer.remove(0);
return Some(ParseResult::Key(
KeyEvent::new(Key::Escape, Modifiers::none()),
1,
));
}
Some(self.parse_next())
}
}
impl Default for KeyParser {
fn default() -> Self {
Self::new()
}
}
fn parse_bytes(bytes: &[u8]) -> ParseResult {
if bytes.is_empty() {
return ParseResult::NeedMore;
}
let first = bytes[0];
if first == 0x1b {
return parse_escape_sequence(bytes);
}
if (0x01..=0x1a).contains(&first) {
return match first {
0x09 => ParseResult::Key(KeyEvent::new(Key::Tab, Modifiers::none()), 1),
0x0d => ParseResult::Key(KeyEvent::new(Key::Enter, Modifiers::none()), 1),
_ => {
let c = (first + b'a' - 1) as char;
ParseResult::Key(KeyEvent::new(Key::Char(c), Modifiers::ctrl()), 1)
}
};
}
if first == 0x7f {
return ParseResult::Key(KeyEvent::new(Key::Backspace, Modifiers::none()), 1);
}
if (0x20..0x7f).contains(&first) {
return ParseResult::Key(
KeyEvent::new(Key::Char(first as char), Modifiers::none()),
1,
);
}
ParseResult::Passthrough(first)
}
fn parse_escape_sequence(bytes: &[u8]) -> ParseResult {
if bytes.len() < 2 {
return ParseResult::NeedMore;
}
let second = bytes[1];
if second == b'[' {
return parse_csi_sequence(bytes);
}
if second == b'O' {
return parse_ss3_sequence(bytes);
}
if (0x20..0x7f).contains(&second) {
let key = Key::Char(second as char);
return ParseResult::Key(KeyEvent::new(key, Modifiers::alt()), 2);
}
if (0x01..=0x1a).contains(&second) && second != 0x1b {
let c = (second + b'a' - 1) as char;
let mut mods = Modifiers::ctrl();
mods.alt = true;
return ParseResult::Key(KeyEvent::new(Key::Char(c), mods), 2);
}
ParseResult::Key(KeyEvent::new(Key::Escape, Modifiers::none()), 1)
}
fn parse_csi_sequence(bytes: &[u8]) -> ParseResult {
if bytes.len() < 3 {
return ParseResult::NeedMore;
}
let mut i = 2;
while i < bytes.len() {
let b = bytes[i];
if (0x40..=0x7e).contains(&b) {
let params = &bytes[2..i];
let final_byte = b;
return interpret_csi(params, final_byte, i + 1);
}
if !(0x20..=0x3f).contains(&b) {
return ParseResult::Key(KeyEvent::new(Key::Escape, Modifiers::none()), 1);
}
i += 1;
}
ParseResult::NeedMore
}
fn interpret_csi(params: &[u8], final_byte: u8, consumed: usize) -> ParseResult {
let params_str = std::str::from_utf8(params).unwrap_or("");
let parts: Vec<&str> = params_str.split(';').collect();
let modifier = if parts.len() >= 2 {
parse_modifier_param(parts[1])
} else {
Modifiers::none()
};
let key = match final_byte {
b'A' => Some(Key::Up),
b'B' => Some(Key::Down),
b'C' => Some(Key::Right),
b'D' => Some(Key::Left),
b'H' => Some(Key::Home),
b'F' => Some(Key::End),
b'~' => {
match parts.first().and_then(|s| s.parse::<u8>().ok()) {
Some(1) => Some(Key::Home),
Some(2) => Some(Key::Insert),
Some(3) => Some(Key::Delete),
Some(4) => Some(Key::End),
Some(5) => Some(Key::PageUp),
Some(6) => Some(Key::PageDown),
Some(15) => Some(Key::F(5)),
Some(17) => Some(Key::F(6)),
Some(18) => Some(Key::F(7)),
Some(19) => Some(Key::F(8)),
Some(20) => Some(Key::F(9)),
Some(21) => Some(Key::F(10)),
Some(23) => Some(Key::F(11)),
Some(24) => Some(Key::F(12)),
_ => None,
}
}
_ => None,
};
match key {
Some(k) => ParseResult::Key(KeyEvent::new(k, modifier), consumed),
None => ParseResult::Key(KeyEvent::new(Key::Escape, Modifiers::none()), 1),
}
}
fn parse_ss3_sequence(bytes: &[u8]) -> ParseResult {
if bytes.len() < 3 {
return ParseResult::NeedMore;
}
let key = match bytes[2] {
b'P' => Some(Key::F(1)),
b'Q' => Some(Key::F(2)),
b'R' => Some(Key::F(3)),
b'S' => Some(Key::F(4)),
b'H' => Some(Key::Home),
b'F' => Some(Key::End),
_ => None,
};
match key {
Some(k) => ParseResult::Key(KeyEvent::new(k, Modifiers::none()), 3),
None => ParseResult::Key(KeyEvent::new(Key::Escape, Modifiers::none()), 1),
}
}
fn parse_modifier_param(s: &str) -> Modifiers {
let n: u8 = s.parse().unwrap_or(1);
let mut m = Modifiers::none();
let bits = n.saturating_sub(1);
m.shift = (bits & 1) != 0;
m.alt = (bits & 2) != 0;
m.ctrl = (bits & 4) != 0;
m
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_regular_char() {
let mut parser = KeyParser::new();
parser.push(b"a");
assert_eq!(
parser.parse_next(),
ParseResult::Key(KeyEvent::char('a'), 1)
);
}
#[test]
fn test_parse_ctrl_a() {
let mut parser = KeyParser::new();
parser.push(&[0x01]);
assert_eq!(
parser.parse_next(),
ParseResult::Key(KeyEvent::ctrl_char('a'), 1)
);
}
#[test]
fn test_parse_arrow_up() {
let mut parser = KeyParser::new();
parser.push(b"\x1b[A");
assert_eq!(
parser.parse_next(),
ParseResult::Key(KeyEvent::new(Key::Up, Modifiers::none()), 3)
);
}
#[test]
fn test_parse_f1() {
let mut parser = KeyParser::new();
parser.push(b"\x1bOP");
assert_eq!(
parser.parse_next(),
ParseResult::Key(KeyEvent::new(Key::F(1), Modifiers::none()), 3)
);
}
#[test]
fn test_parse_alt_x() {
let mut parser = KeyParser::new();
parser.push(b"\x1bx");
assert_eq!(
parser.parse_next(),
ParseResult::Key(KeyEvent::new(Key::Char('x'), Modifiers::alt()), 2)
);
}
#[test]
fn test_parse_escape_need_more() {
let mut parser = KeyParser::new();
parser.push(b"\x1b");
assert_eq!(parser.parse_next(), ParseResult::NeedMore);
}
#[test]
fn test_force_parse_escape() {
let mut parser = KeyParser::new();
parser.push(b"\x1b");
assert_eq!(
parser.force_parse_first(),
Some(ParseResult::Key(
KeyEvent::new(Key::Escape, Modifiers::none()),
1
))
);
}
}