use super::keymap::KeymapManager;
use super::thingy::Thingy;
pub fn init_default_bindings(km: &mut KeymapManager) {
let _ = km;
}
pub fn parse_key_sequence(s: &str) -> Vec<u8> {
let mut result = Vec::new();
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
match c {
'^' => {
if let Some(&next) = chars.peek() {
chars.next();
if next == '?' {
result.push(0x7f); } else if next == '[' {
result.push(0x1b); } else {
result.push((next.to_ascii_uppercase() as u8).wrapping_sub(b'@'));
}
}
}
'\\' => {
match chars.peek() {
Some(&'e') | Some(&'E') => {
chars.next();
result.push(0x1b); }
Some(&'n') => {
chars.next();
result.push(b'\n');
}
Some(&'t') => {
chars.next();
result.push(b'\t');
}
Some(&'r') => {
chars.next();
result.push(b'\r');
}
Some(&'M') => {
chars.next();
if chars.peek() == Some(&'-') {
chars.next();
result.push(0x1b);
if let Some(next) = chars.next() {
result.push(next as u8);
}
}
}
Some(&'C') => {
chars.next();
if chars.peek() == Some(&'-') {
chars.next();
if let Some(next) = chars.next() {
result.push((next.to_ascii_uppercase() as u8).wrapping_sub(b'@'));
}
}
}
Some(&'x') => {
chars.next();
let mut hex = String::new();
for _ in 0..2 {
if let Some(&c) = chars.peek() {
if c.is_ascii_hexdigit() {
hex.push(c);
chars.next();
} else {
break;
}
}
}
if let Ok(n) = u8::from_str_radix(&hex, 16) {
result.push(n);
}
}
Some(&c) => {
chars.next();
result.push(c as u8);
}
None => {
result.push(b'\\');
}
}
}
_ => {
result.push(c as u8);
}
}
}
result
}
pub fn format_key_sequence(seq: &[u8]) -> String {
let mut result = String::new();
let mut i = 0;
while i < seq.len() {
let b = seq[i];
match b {
0x1b => {
result.push_str("^[");
}
0x00..=0x1f => {
result.push('^');
result.push((b + b'@') as char);
}
0x7f => {
result.push_str("^?");
}
0x80..=0xff => {
result.push_str(&format!("\\x{:02x}", b));
}
_ => {
result.push(b as char);
}
}
i += 1;
}
result
}
pub fn bind_key(km: &mut KeymapManager, keymap: &str, seq: &str, widget: &str) -> bool {
let seq_bytes = parse_key_sequence(seq);
let map = match km.keymaps.get_mut(keymap) {
Some(m) => m,
None => return false,
};
let inner = std::sync::Arc::make_mut(map);
inner.bind_seq(&seq_bytes, Thingy::new(widget));
true
}
pub fn unbind_key(km: &mut KeymapManager, keymap: &str, seq: &str) -> bool {
let seq_bytes = parse_key_sequence(seq);
let map = match km.keymaps.get_mut(keymap) {
Some(m) => m,
None => return false,
};
let inner = std::sync::Arc::make_mut(map);
inner.unbind_seq(&seq_bytes);
true
}
pub fn list_bindings(km: &KeymapManager, keymap: &str) -> Vec<(String, String)> {
let mut bindings = Vec::new();
if let Some(map) = km.keymaps.get(keymap) {
for (i, thingy) in map.first.iter().enumerate() {
if let Some(t) = thingy {
let seq = format_key_sequence(&[i as u8]);
bindings.push((seq, t.name.clone()));
}
}
for (seq, binding) in &map.multi {
if let Some(t) = &binding.bind {
let seq_str = format_key_sequence(seq);
bindings.push((seq_str, t.name.clone()));
} else if let Some(s) = &binding.str {
let seq_str = format_key_sequence(seq);
bindings.push((seq_str, format!("send-string \"{}\"", s)));
}
}
}
bindings.sort_by(|a, b| a.0.cmp(&b.0));
bindings
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bind_key_returns_false_for_unknown_keymap() {
let mut km = KeymapManager::new();
assert!(!bind_key(&mut km, "no-such-keymap", "^A", "self-insert"));
}
#[test]
fn bind_key_then_unbind_round_trips_through_emacs_keymap() {
let mut km = KeymapManager::new();
assert!(bind_key(&mut km, "emacs", "\\ez", "self-insert"));
let listed = list_bindings(&km, "emacs");
let seq = format_key_sequence(&[0x1b, 0x7a]);
assert!(
listed.iter().any(|(k, v)| k == &seq && v == "self-insert"),
"bound sequence missing from list: {:?}",
listed
);
assert!(unbind_key(&mut km, "emacs", "\\ez"));
let listed = list_bindings(&km, "emacs");
assert!(
!listed.iter().any(|(k, _)| k == &seq),
"unbound sequence still present: {:?}",
listed
);
}
}