use alacritty_terminal::term::TermMode;
use gpui::Keystroke;
pub fn keystroke_to_bytes(keystroke: &Keystroke, mode: TermMode) -> Option<Vec<u8>> {
match keystroke.key.as_str() {
"space" => {
if keystroke.modifiers.control {
return Some(b"\x00".to_vec());
}
return Some(b" ".to_vec());
}
"enter" => return Some(b"\r".to_vec()),
"escape" => return Some(b"\x1b".to_vec()),
"backspace" => return Some(b"\x7f".to_vec()),
"tab" => {
if keystroke.modifiers.shift {
return Some(b"\x1b[Z".to_vec());
}
return Some(b"\t".to_vec());
}
"up" => {
if mode.contains(TermMode::APP_CURSOR) {
return Some(b"\x1bOA".to_vec());
}
return Some(b"\x1b[A".to_vec());
}
"down" => {
if mode.contains(TermMode::APP_CURSOR) {
return Some(b"\x1bOB".to_vec());
}
return Some(b"\x1b[B".to_vec());
}
"right" => {
if mode.contains(TermMode::APP_CURSOR) {
return Some(b"\x1bOC".to_vec());
}
return Some(b"\x1b[C".to_vec());
}
"left" => {
if mode.contains(TermMode::APP_CURSOR) {
return Some(b"\x1bOD".to_vec());
}
return Some(b"\x1b[D".to_vec());
}
"home" => return Some(b"\x1b[H".to_vec()),
"end" => return Some(b"\x1b[F".to_vec()),
"pageup" => return Some(b"\x1b[5~".to_vec()),
"pagedown" => return Some(b"\x1b[6~".to_vec()),
"insert" => return Some(b"\x1b[2~".to_vec()),
"delete" => return Some(b"\x1b[3~".to_vec()),
"f1" => return Some(b"\x1bOP".to_vec()),
"f2" => return Some(b"\x1bOQ".to_vec()),
"f3" => return Some(b"\x1bOR".to_vec()),
"f4" => return Some(b"\x1bOS".to_vec()),
"f5" => return Some(b"\x1b[15~".to_vec()),
"f6" => return Some(b"\x1b[17~".to_vec()),
"f7" => return Some(b"\x1b[18~".to_vec()),
"f8" => return Some(b"\x1b[19~".to_vec()),
"f9" => return Some(b"\x1b[20~".to_vec()),
"f10" => return Some(b"\x1b[21~".to_vec()),
"f11" => return Some(b"\x1b[23~".to_vec()),
"f12" => return Some(b"\x1b[24~".to_vec()),
_ => {}
}
if keystroke.modifiers.control {
let key = keystroke.key.as_str();
if key.len() == 1 {
let ch = key.chars().next().unwrap();
if ch.is_ascii_alphabetic() {
let upper = ch.to_ascii_uppercase();
let ctrl_char = (upper as u8) - b'@';
return Some(vec![ctrl_char]);
}
match ch {
'[' => return Some(b"\x1b".to_vec()),
'\\' => return Some(b"\x1c".to_vec()),
']' => return Some(b"\x1d".to_vec()),
'^' => return Some(b"\x1e".to_vec()),
'_' => return Some(b"\x1f".to_vec()),
'?' => return Some(b"\x7f".to_vec()),
_ => {}
}
}
}
if keystroke.modifiers.alt {
let key = keystroke.key.as_str();
if key.len() == 1 {
let ch = key.chars().next().unwrap();
if ch.is_ascii() {
let mut bytes = vec![b'\x1b'];
bytes.push(ch as u8);
return Some(bytes);
}
}
}
if let Some(key_char) = &keystroke.key_char
&& !keystroke.modifiers.control
&& !keystroke.modifiers.alt
{
return Some(key_char.as_bytes().to_vec());
}
let key = keystroke.key.as_str();
if key.len() == 1 {
let ch = key.chars().next().unwrap();
if ch.is_ascii() && !keystroke.modifiers.control {
let ch = if keystroke.modifiers.shift {
ch.to_ascii_uppercase()
} else {
ch
};
return Some(vec![ch as u8]);
}
if !keystroke.modifiers.control && !keystroke.modifiers.alt {
return Some(key.as_bytes().to_vec());
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_enter_key() {
let keystroke = Keystroke::parse("enter").unwrap();
let bytes = keystroke_to_bytes(&keystroke, TermMode::empty());
assert_eq!(bytes, Some(b"\r".to_vec()));
}
#[test]
fn test_escape_key() {
let keystroke = Keystroke::parse("escape").unwrap();
let bytes = keystroke_to_bytes(&keystroke, TermMode::empty());
assert_eq!(bytes, Some(b"\x1b".to_vec()));
}
#[test]
fn test_backspace_key() {
let keystroke = Keystroke::parse("backspace").unwrap();
let bytes = keystroke_to_bytes(&keystroke, TermMode::empty());
assert_eq!(bytes, Some(b"\x7f".to_vec()));
}
#[test]
fn test_tab_key() {
let keystroke = Keystroke::parse("tab").unwrap();
let bytes = keystroke_to_bytes(&keystroke, TermMode::empty());
assert_eq!(bytes, Some(b"\t".to_vec()));
}
#[test]
fn test_shift_tab() {
let keystroke = Keystroke::parse("shift-tab").unwrap();
let bytes = keystroke_to_bytes(&keystroke, TermMode::empty());
assert_eq!(bytes, Some(b"\x1b[Z".to_vec()));
}
#[test]
fn test_arrow_keys_normal_mode() {
let mode = TermMode::empty();
let up = Keystroke::parse("up").unwrap();
assert_eq!(keystroke_to_bytes(&up, mode), Some(b"\x1b[A".to_vec()));
let down = Keystroke::parse("down").unwrap();
assert_eq!(keystroke_to_bytes(&down, mode), Some(b"\x1b[B".to_vec()));
let right = Keystroke::parse("right").unwrap();
assert_eq!(keystroke_to_bytes(&right, mode), Some(b"\x1b[C".to_vec()));
let left = Keystroke::parse("left").unwrap();
assert_eq!(keystroke_to_bytes(&left, mode), Some(b"\x1b[D".to_vec()));
}
#[test]
fn test_arrow_keys_app_cursor_mode() {
let mode = TermMode::APP_CURSOR;
let up = Keystroke::parse("up").unwrap();
assert_eq!(keystroke_to_bytes(&up, mode), Some(b"\x1bOA".to_vec()));
let down = Keystroke::parse("down").unwrap();
assert_eq!(keystroke_to_bytes(&down, mode), Some(b"\x1bOB".to_vec()));
let right = Keystroke::parse("right").unwrap();
assert_eq!(keystroke_to_bytes(&right, mode), Some(b"\x1bOC".to_vec()));
let left = Keystroke::parse("left").unwrap();
assert_eq!(keystroke_to_bytes(&left, mode), Some(b"\x1bOD".to_vec()));
}
#[test]
fn test_navigation_keys() {
let mode = TermMode::empty();
let home = Keystroke::parse("home").unwrap();
assert_eq!(keystroke_to_bytes(&home, mode), Some(b"\x1b[H".to_vec()));
let end = Keystroke::parse("end").unwrap();
assert_eq!(keystroke_to_bytes(&end, mode), Some(b"\x1b[F".to_vec()));
let pageup = Keystroke::parse("pageup").unwrap();
assert_eq!(keystroke_to_bytes(&pageup, mode), Some(b"\x1b[5~".to_vec()));
let pagedown = Keystroke::parse("pagedown").unwrap();
assert_eq!(
keystroke_to_bytes(&pagedown, mode),
Some(b"\x1b[6~".to_vec())
);
let insert = Keystroke::parse("insert").unwrap();
assert_eq!(keystroke_to_bytes(&insert, mode), Some(b"\x1b[2~".to_vec()));
let delete = Keystroke::parse("delete").unwrap();
assert_eq!(keystroke_to_bytes(&delete, mode), Some(b"\x1b[3~".to_vec()));
}
#[test]
fn test_function_keys() {
let mode = TermMode::empty();
let f1 = Keystroke::parse("f1").unwrap();
assert_eq!(keystroke_to_bytes(&f1, mode), Some(b"\x1bOP".to_vec()));
let f2 = Keystroke::parse("f2").unwrap();
assert_eq!(keystroke_to_bytes(&f2, mode), Some(b"\x1bOQ".to_vec()));
let f5 = Keystroke::parse("f5").unwrap();
assert_eq!(keystroke_to_bytes(&f5, mode), Some(b"\x1b[15~".to_vec()));
let f12 = Keystroke::parse("f12").unwrap();
assert_eq!(keystroke_to_bytes(&f12, mode), Some(b"\x1b[24~".to_vec()));
}
#[test]
fn test_ctrl_combinations() {
let mode = TermMode::empty();
let ctrl_a = Keystroke::parse("ctrl-a").unwrap();
assert_eq!(keystroke_to_bytes(&ctrl_a, mode), Some(vec![0x01]));
let ctrl_c = Keystroke::parse("ctrl-c").unwrap();
assert_eq!(keystroke_to_bytes(&ctrl_c, mode), Some(vec![0x03]));
let ctrl_z = Keystroke::parse("ctrl-z").unwrap();
assert_eq!(keystroke_to_bytes(&ctrl_z, mode), Some(vec![0x1a]));
let ctrl_space = Keystroke::parse("ctrl-space").unwrap();
assert_eq!(keystroke_to_bytes(&ctrl_space, mode), Some(vec![0x00]));
}
#[test]
fn test_alt_combinations() {
let mode = TermMode::empty();
let alt_a = Keystroke::parse("alt-a").unwrap();
assert_eq!(keystroke_to_bytes(&alt_a, mode), Some(b"\x1ba".to_vec()));
let alt_x = Keystroke::parse("alt-x").unwrap();
assert_eq!(keystroke_to_bytes(&alt_x, mode), Some(b"\x1bx".to_vec()));
}
#[test]
fn test_regular_characters() {
let mode = TermMode::empty();
let a = Keystroke::parse("a").unwrap();
assert_eq!(keystroke_to_bytes(&a, mode), Some(b"a".to_vec()));
let z = Keystroke::parse("z").unwrap();
assert_eq!(keystroke_to_bytes(&z, mode), Some(b"z".to_vec()));
let zero = Keystroke::parse("0").unwrap();
assert_eq!(keystroke_to_bytes(&zero, mode), Some(b"0".to_vec()));
}
#[test]
fn test_space_key() {
let mode = TermMode::empty();
let space = Keystroke::parse("space").unwrap();
assert_eq!(keystroke_to_bytes(&space, mode), Some(b" ".to_vec()));
}
}