use std::collections::HashMap;
use std::sync::Arc;
use super::thingy::Thingy;
#[derive(Debug, Clone, Copy, Default)]
pub struct KeymapNameFlags {
pub immortal: bool,
}
#[derive(Debug, Clone)]
pub struct KeymapName {
pub name: String,
pub flags: KeymapNameFlags,
pub keymap: Arc<Keymap>,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct KeymapFlags {
pub immutable: bool,
}
#[derive(Debug, Clone)]
pub struct Keymap {
pub first: [Option<Thingy>; 256],
pub multi: HashMap<Vec<u8>, KeyBinding>,
pub primary: Option<String>,
pub flags: KeymapFlags,
}
#[derive(Debug, Clone)]
pub struct KeyBinding {
pub bind: Option<Thingy>,
pub str: Option<String>,
pub prefixct: i32,
}
#[derive(Debug, Clone, Default)]
pub struct BindState {
pub flags: BindStateFlags,
pub kmname: String,
pub firstseq: Vec<u8>,
pub lastseq: Vec<u8>,
pub bind: Option<Thingy>,
pub str: Option<String>,
pub prefix: Vec<u8>,
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, Default)]
pub struct BindStateFlags: u32 {
const LIST = 1 << 0;
const ALL = 1 << 1;
}
}
impl Default for Keymap {
fn default() -> Self {
Keymap {
first: std::array::from_fn(|_| None),
multi: HashMap::new(),
primary: None,
flags: KeymapFlags::default(),
}
}
}
impl Keymap {
pub fn new() -> Self {
Self::default()
}
pub fn bind_char(&mut self, c: u8, thingy: Thingy) {
self.first[c as usize] = Some(thingy);
}
pub fn unbind_char(&mut self, c: u8) {
self.first[c as usize] = None;
}
pub fn bind_seq(&mut self, seq: &[u8], thingy: Thingy) {
if seq.len() == 1 {
self.bind_char(seq[0], thingy);
} else {
for i in 1..seq.len() {
let prefix = &seq[..i];
self.multi
.entry(prefix.to_vec())
.and_modify(|kb| kb.prefixct += 1)
.or_insert(KeyBinding {
bind: None,
str: None,
prefixct: 1,
});
}
self.multi.insert(
seq.to_vec(),
KeyBinding {
bind: Some(thingy),
str: None,
prefixct: 0,
},
);
}
}
pub fn bind_str(&mut self, seq: &[u8], s: String) {
if seq.len() == 1 {
}
for i in 1..seq.len() {
let prefix = &seq[..i];
self.multi
.entry(prefix.to_vec())
.and_modify(|kb| kb.prefixct += 1)
.or_insert(KeyBinding {
bind: None,
str: None,
prefixct: 1,
});
}
self.multi.insert(
seq.to_vec(),
KeyBinding {
bind: None,
str: Some(s),
prefixct: 0,
},
);
}
pub fn unbind_seq(&mut self, seq: &[u8]) {
if seq.len() == 1 {
self.unbind_char(seq[0]);
} else {
if self.multi.remove(seq).is_some() {
for i in 1..seq.len() {
let prefix = &seq[..i];
if let Some(kb) = self.multi.get_mut(prefix) {
kb.prefixct -= 1;
if kb.prefixct == 0 && kb.bind.is_none() && kb.str.is_none() {
}
}
}
}
}
}
pub fn lookup_char(&self, c: u8) -> Option<&Thingy> {
self.first[c as usize].as_ref()
}
pub fn lookup_seq(&self, seq: &[u8]) -> Option<&KeyBinding> {
if seq.len() == 1 {
None
} else {
self.multi.get(seq)
}
}
pub fn is_prefix(&self, seq: &[u8]) -> bool {
if seq.len() == 1 {
self.multi.keys().any(|k| k.len() > 1 && k[0] == seq[0])
} else {
self.multi
.get(seq)
.map(|kb| kb.prefixct > 0)
.unwrap_or(false)
}
}
}
#[derive(Debug)]
pub struct KeymapManager {
pub keymaps: HashMap<String, Arc<Keymap>>,
pub current: Option<Arc<Keymap>>,
pub current_name: String,
pub local: Option<Arc<Keymap>>,
pub keybuf: Vec<u8>,
pub lastnamed: Option<Thingy>,
}
impl Default for KeymapManager {
fn default() -> Self {
Self::new()
}
}
impl KeymapManager {
pub fn new() -> Self {
let mut mgr = KeymapManager {
keymaps: HashMap::new(),
current: None,
current_name: "main".to_string(),
local: None,
keybuf: Vec::with_capacity(20),
lastnamed: None,
};
mgr.create_default_keymaps();
mgr
}
fn create_default_keymaps(&mut self) {
let mut emacs = Keymap::new();
emacs.primary = Some("emacs".to_string());
self.setup_emacs_keymap(&mut emacs);
self.keymaps.insert("emacs".to_string(), Arc::new(emacs));
let mut viins = Keymap::new();
viins.primary = Some("viins".to_string());
self.setup_viins_keymap(&mut viins);
self.keymaps.insert("viins".to_string(), Arc::new(viins));
let mut vicmd = Keymap::new();
vicmd.primary = Some("vicmd".to_string());
self.setup_vicmd_keymap(&mut vicmd);
self.keymaps.insert("vicmd".to_string(), Arc::new(vicmd));
let isearch = Keymap::new();
self.keymaps
.insert("isearch".to_string(), Arc::new(isearch));
let command = Keymap::new();
self.keymaps
.insert("command".to_string(), Arc::new(command));
let emacs = self.keymaps.get("emacs").cloned();
if let Some(emacs) = emacs {
self.keymaps.insert("main".to_string(), Arc::clone(&emacs));
self.current = Some(emacs);
}
}
fn setup_emacs_keymap(&self, km: &mut Keymap) {
for c in 32u8..127 {
km.bind_char(c, Thingy::builtin("self-insert"));
}
km.bind_char(0x01, Thingy::builtin("beginning-of-line")); km.bind_char(0x02, Thingy::builtin("backward-char")); km.bind_char(0x04, Thingy::builtin("delete-char-or-list")); km.bind_char(0x05, Thingy::builtin("end-of-line")); km.bind_char(0x06, Thingy::builtin("forward-char"));
km.bind_char(0x08, Thingy::builtin("backward-delete-char")); km.bind_char(0x0B, Thingy::builtin("kill-line")); km.bind_char(0x0C, Thingy::builtin("clear-screen")); km.bind_char(0x0D, Thingy::builtin("accept-line")); km.bind_char(0x0E, Thingy::builtin("down-line-or-history")); km.bind_char(0x10, Thingy::builtin("up-line-or-history")); km.bind_char(0x12, Thingy::builtin("history-incremental-search-backward")); km.bind_char(0x13, Thingy::builtin("history-incremental-search-forward")); km.bind_char(0x14, Thingy::builtin("transpose-chars")); km.bind_char(0x15, Thingy::builtin("kill-whole-line")); km.bind_char(0x17, Thingy::builtin("backward-kill-word")); km.bind_char(0x19, Thingy::builtin("yank"));
km.bind_char(0x03, Thingy::builtin("send-break"));
km.bind_char(0x09, Thingy::builtin("expand-or-complete"));
km.bind_char(0x7F, Thingy::builtin("backward-delete-char"));
km.bind_seq(b"\x1bb", Thingy::builtin("backward-word")); km.bind_seq(b"\x1bf", Thingy::builtin("forward-word")); km.bind_seq(b"\x1bd", Thingy::builtin("kill-word")); km.bind_seq(b"\x1b\x7f", Thingy::builtin("backward-kill-word"));
km.bind_seq(b"\x1b[A", Thingy::builtin("up-line-or-history")); km.bind_seq(b"\x1b[B", Thingy::builtin("down-line-or-history")); km.bind_seq(b"\x1b[C", Thingy::builtin("forward-char")); km.bind_seq(b"\x1b[D", Thingy::builtin("backward-char")); km.bind_seq(b"\x1b[H", Thingy::builtin("beginning-of-line")); km.bind_seq(b"\x1b[F", Thingy::builtin("end-of-line")); km.bind_seq(b"\x1b[3~", Thingy::builtin("delete-char"));
km.bind_seq(b"\x1bOA", Thingy::builtin("up-line-or-history"));
km.bind_seq(b"\x1bOB", Thingy::builtin("down-line-or-history"));
km.bind_seq(b"\x1bOC", Thingy::builtin("forward-char"));
km.bind_seq(b"\x1bOD", Thingy::builtin("backward-char"));
}
fn setup_viins_keymap(&self, km: &mut Keymap) {
for c in 32u8..127 {
km.bind_char(c, Thingy::builtin("self-insert"));
}
km.bind_char(0x1B, Thingy::builtin("vi-cmd-mode"));
km.bind_char(0x08, Thingy::builtin("vi-backward-delete-char")); km.bind_char(0x7F, Thingy::builtin("vi-backward-delete-char")); km.bind_char(0x0D, Thingy::builtin("accept-line")); km.bind_char(0x09, Thingy::builtin("expand-or-complete"));
km.bind_char(0x03, Thingy::builtin("send-break"));
km.bind_char(0x17, Thingy::builtin("vi-backward-kill-word"));
}
fn setup_vicmd_keymap(&self, km: &mut Keymap) {
km.bind_char(b'h', Thingy::builtin("vi-backward-char"));
km.bind_char(b'l', Thingy::builtin("vi-forward-char"));
km.bind_char(b'j', Thingy::builtin("down-line-or-history"));
km.bind_char(b'k', Thingy::builtin("up-line-or-history"));
km.bind_char(b'w', Thingy::builtin("vi-forward-word"));
km.bind_char(b'W', Thingy::builtin("vi-forward-blank-word"));
km.bind_char(b'b', Thingy::builtin("vi-backward-word"));
km.bind_char(b'B', Thingy::builtin("vi-backward-blank-word"));
km.bind_char(b'e', Thingy::builtin("vi-forward-word-end"));
km.bind_char(b'E', Thingy::builtin("vi-forward-blank-word-end"));
km.bind_char(b'0', Thingy::builtin("vi-digit-or-beginning-of-line"));
km.bind_char(b'^', Thingy::builtin("vi-first-non-blank"));
km.bind_char(b'$', Thingy::builtin("vi-end-of-line"));
km.bind_char(b'i', Thingy::builtin("vi-insert"));
km.bind_char(b'I', Thingy::builtin("vi-insert-bol"));
km.bind_char(b'a', Thingy::builtin("vi-add-next"));
km.bind_char(b'A', Thingy::builtin("vi-add-eol"));
km.bind_char(b'o', Thingy::builtin("vi-open-line-below"));
km.bind_char(b'O', Thingy::builtin("vi-open-line-above"));
km.bind_char(b'x', Thingy::builtin("vi-delete-char"));
km.bind_char(b'X', Thingy::builtin("vi-backward-delete-char"));
km.bind_char(b'd', Thingy::builtin("vi-delete"));
km.bind_char(b'D', Thingy::builtin("vi-kill-eol"));
km.bind_char(b'c', Thingy::builtin("vi-change"));
km.bind_char(b'C', Thingy::builtin("vi-change-eol"));
km.bind_char(b'y', Thingy::builtin("vi-yank"));
km.bind_char(b'Y', Thingy::builtin("vi-yank-whole-line"));
km.bind_char(b'p', Thingy::builtin("vi-put-after"));
km.bind_char(b'P', Thingy::builtin("vi-put-before"));
km.bind_char(b'r', Thingy::builtin("vi-replace-chars"));
km.bind_char(b'R', Thingy::builtin("vi-replace"));
km.bind_char(b's', Thingy::builtin("vi-substitute"));
km.bind_char(b'S', Thingy::builtin("vi-change-whole-line"));
km.bind_char(b'/', Thingy::builtin("vi-history-search-forward"));
km.bind_char(b'?', Thingy::builtin("vi-history-search-backward"));
km.bind_char(b'n', Thingy::builtin("vi-repeat-search"));
km.bind_char(b'N', Thingy::builtin("vi-rev-repeat-search"));
km.bind_char(b'f', Thingy::builtin("vi-find-next-char"));
km.bind_char(b'F', Thingy::builtin("vi-find-prev-char"));
km.bind_char(b't', Thingy::builtin("vi-find-next-char-skip"));
km.bind_char(b'T', Thingy::builtin("vi-find-prev-char-skip"));
km.bind_char(b';', Thingy::builtin("vi-repeat-find"));
km.bind_char(b',', Thingy::builtin("vi-rev-repeat-find"));
km.bind_char(b'u', Thingy::builtin("undo"));
km.bind_char(0x12, Thingy::builtin("redo"));
km.bind_char(b'.', Thingy::builtin("vi-repeat-change"));
for c in b'1'..=b'9' {
km.bind_char(c, Thingy::builtin("digit-argument"));
}
km.bind_char(0x0D, Thingy::builtin("accept-line"));
km.bind_char(0x03, Thingy::builtin("send-break"));
km.bind_char(b'J', Thingy::builtin("vi-join"));
km.bind_char(b'G', Thingy::builtin("vi-fetch-history"));
km.bind_char(b'g', Thingy::builtin("vi-goto-column")); }
pub fn get(&self, name: &str) -> Option<Arc<Keymap>> {
self.keymaps.get(name).cloned()
}
pub fn select(&mut self, name: &str) -> bool {
if let Some(km) = self.keymaps.get(name) {
self.current = Some(Arc::clone(km));
self.current_name = name.to_string();
true
} else {
false
}
}
pub fn link(&mut self, oldname: &str, newname: &str) -> bool {
if let Some(km) = self.keymaps.get(oldname) {
self.keymaps.insert(newname.to_string(), Arc::clone(km));
true
} else {
false
}
}
pub fn delete(&mut self, name: &str) -> bool {
if name == "main" || name == "emacs" || name == "viins" || name == "vicmd" {
return false;
}
self.keymaps.remove(name).is_some()
}
pub fn lookup_key(&self, c: char) -> Option<Thingy> {
let km = self.local.as_ref().or(self.current.as_ref())?;
if c as u32 <= 255 {
km.first[c as usize].clone()
} else {
None
}
}
pub fn lookup_seq(&self, seq: &[u8]) -> Option<&KeyBinding> {
let km = self.local.as_ref().or(self.current.as_ref())?;
km.lookup_seq(seq)
}
pub fn is_prefix(&self, seq: &[u8]) -> bool {
if let Some(km) = self.local.as_ref().or(self.current.as_ref()) {
km.is_prefix(seq)
} else {
false
}
}
pub fn list_names(&self) -> Vec<&String> {
self.keymaps.keys().collect()
}
pub fn new_keymap(&mut self, name: &str) -> bool {
if self.keymaps.contains_key(name) {
return false;
}
let mut km = Keymap::new();
km.primary = Some(name.to_string());
self.keymaps.insert(name.to_string(), Arc::new(km));
true
}
pub fn copy_keymap(&mut self, src: &str, dst: &str) -> bool {
if let Some(src_km) = self.keymaps.get(src) {
let new_km = (**src_km).clone();
self.keymaps.insert(dst.to_string(), Arc::new(new_km));
true
} else {
false
}
}
pub fn select_local_map(&mut self, name: Option<&str>) {
self.local = name.and_then(|n| self.keymaps.get(n).cloned());
}
pub fn reselect_keymap(&mut self) {
self.local = None;
}
pub fn read_command(&self, keys: &[u8]) -> Option<Thingy> {
let km = self.local.as_ref().or(self.current.as_ref())?;
if keys.len() == 1 {
km.first[keys[0] as usize].clone()
} else {
km.lookup_seq(keys).and_then(|kb| kb.bind.clone())
}
}
pub fn get_keybuf(&self) -> &[u8] {
&self.keybuf
}
pub fn add_keybuf(&mut self, c: u8) {
self.keybuf.push(c);
}
pub fn clear_keybuf(&mut self) {
self.keybuf.clear();
}
pub fn is_emacs(&self) -> bool {
self.current_name == "emacs" || self.current_name == "main"
}
pub fn is_vi_insert(&self) -> bool {
self.current_name == "viins"
}
pub fn is_vi_cmd(&self) -> bool {
self.current_name == "vicmd"
}
pub fn get_keymap_cmd(&self, km: &Keymap, key: u8) -> Option<Thingy> {
km.first[key as usize].clone()
}
pub fn key_is_prefix(&self, km: &Keymap, key: u8) -> bool {
km.multi.keys().any(|k| k.len() > 1 && k[0] == key)
}
pub fn keybind(&mut self, seq: &[u8], thingy: Thingy) -> bool {
if let Some(km) = self.keymaps.get_mut(&self.current_name) {
if let Some(km_mut) = Arc::get_mut(km) {
if seq.len() == 1 {
km_mut.bind_char(seq[0], thingy);
} else {
km_mut.bind_seq(seq, thingy);
}
return true;
}
}
false
}
pub fn keyunbind(&mut self, seq: &[u8]) -> bool {
if let Some(km) = self.keymaps.get_mut(&self.current_name) {
if let Some(km_mut) = Arc::get_mut(km) {
km_mut.unbind_seq(seq);
return true;
}
}
false
}
pub fn scan_keymap(&self, name: &str) -> Vec<(Vec<u8>, String)> {
let mut bindings = Vec::new();
if let Some(km) = self.keymaps.get(name) {
for (i, opt) in km.first.iter().enumerate() {
if let Some(t) = opt {
bindings.push((vec![i as u8], t.name.clone()));
}
}
for (seq, kb) in &km.multi {
if let Some(ref t) = kb.bind {
bindings.push((seq.clone(), t.name.clone()));
} else if let Some(ref s) = kb.str {
bindings.push((seq.clone(), format!("\"{}\"", s)));
}
}
}
bindings.sort_by(|a, b| a.0.cmp(&b.0));
bindings
}
pub fn zle_set_keymap(&mut self, name: &str) -> bool {
self.select(name)
}
pub fn ref_keymap_by_name(&self, name: &str) -> Option<Arc<Keymap>> {
self.keymaps.get(name).cloned()
}
pub fn init_keymaps(&mut self) {
self.create_default_keymaps();
}
pub fn cleanup_keymaps(&mut self) {
self.keymaps.clear();
self.current = None;
self.local = None;
}
}
pub fn bin_bindkey(args: &[String], opts: BindkeyOpts) -> i32 {
let _ = (args, opts);
0
}
#[derive(Debug, Default)]
pub struct BindkeyOpts {
pub list: bool, pub list_all: bool, pub delete: bool, pub remove: bool, pub meta: bool, pub new_keymap: bool, pub keymap: Option<String>, pub prefix: Option<String>, }