const MOD_MASK_SHIFT: i32 = 0x02;
const MOD_MASK_CTRL: i32 = 0x04;
pub fn name_to_mod_mask(c: u8) -> i32 {
match c.to_ascii_uppercase() {
b'C' => MOD_MASK_CTRL,
b'S' => MOD_MASK_SHIFT,
_ => 0,
}
}
pub fn get_special_key_code(name: &str) -> Option<char> {
Some(match name.to_ascii_lowercase().as_str() {
"tab" => '\t',
"nl" | "newline" | "linefeed" => '\n',
"cr" | "return" | "enter" => '\r',
"esc" => '\x1b',
"space" => ' ',
"lt" => '<',
"bslash" => '\\',
"bar" => '|',
_ => return None,
})
}
fn extract_modifiers(mut key: char, modifiers: i32) -> Option<char> {
let mut modifiers = modifiers;
if modifiers & MOD_MASK_SHIFT != 0 && key.is_ascii_alphabetic() {
key = key.to_ascii_uppercase();
if modifiers & MOD_MASK_CTRL == 0 {
modifiers &= !MOD_MASK_SHIFT;
}
}
if modifiers & MOD_MASK_CTRL != 0 && ((('?'..='_').contains(&key)) || key.is_ascii_alphabetic())
{
let code = (key.to_ascii_uppercase() as u32) ^ 0x40;
if code == 0 {
return None;
}
key = char::from_u32(code)?;
modifiers &= !MOD_MASK_CTRL;
}
(modifiers == 0).then_some(key)
}
pub fn find_special_key(src: &str) -> Option<(char, usize)> {
let bytes = src.as_bytes();
if bytes.first() != Some(&b'<') {
return None;
}
let close = src.find('>')?;
let body = &src[1..close];
if body.is_empty() {
return None;
}
let consumed = close + 1;
if let Some(num) = body
.strip_prefix("Char-")
.or_else(|| body.strip_prefix("char-"))
.or_else(|| body.strip_prefix("CHAR-"))
{
let n = if let Some(hex) = num.strip_prefix("0x").or_else(|| num.strip_prefix("0X")) {
u32::from_str_radix(hex, 16).ok()?
} else {
num.parse::<u32>().ok()?
};
return char::from_u32(n).map(|c| (c, consumed));
}
let mut modifiers = 0;
let (mods, name) = match body.rfind('-') {
Some(last_dash) => (&body[..last_dash], &body[last_dash + 1..]),
None => ("", body),
};
for m in mods.bytes() {
if m == b'-' {
continue;
}
let bit = name_to_mod_mask(m);
if bit == 0 {
return None;
}
modifiers |= bit;
}
if name.is_empty() {
return None;
}
let mut chars = name.chars();
let first = chars.next()?;
let key = if modifiers != 0 && chars.next().is_none() {
first
} else {
get_special_key_code(name)?
};
extract_modifiers(key, modifiers).map(|k| (k, consumed))
}
pub fn trans_special(src: &str) -> Option<(char, usize)> {
find_special_key(src)
}
pub fn get_special_key_name(c: char) -> String {
match c {
' ' => "<Space>".into(),
'<' => "<lt>".into(),
'\t' => "<Tab>".into(),
'\n' => "<NL>".into(),
'\r' => "<CR>".into(),
'\x1b' => "<Esc>".into(),
c if (c as u32) < 0x20 => format!("<C-{}>", (c as u8 + b'@') as char),
c => c.to_string(),
}
}