use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use eyre::{Result, eyre};
use crate::tui::event::{self, Action};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum SkimEvent {
Start,
Load,
Change,
Result,
Focus,
Zero,
One,
}
impl SkimEvent {
#[must_use]
pub const fn key_code(self) -> KeyCode {
match self {
SkimEvent::Change => KeyCode::F(255),
SkimEvent::Start => KeyCode::F(254),
SkimEvent::Load => KeyCode::F(253),
SkimEvent::Result => KeyCode::F(252),
SkimEvent::Focus => KeyCode::F(251),
SkimEvent::Zero => KeyCode::F(250),
SkimEvent::One => KeyCode::F(249),
}
}
#[must_use]
pub const fn key_event(self) -> KeyEvent {
KeyEvent::new(self.key_code(), KeyModifiers::NONE)
}
#[must_use]
pub fn from_name(name: &str) -> Option<Self> {
match name {
"start" => Some(SkimEvent::Start),
"load" => Some(SkimEvent::Load),
"change" => Some(SkimEvent::Change),
"result" => Some(SkimEvent::Result),
"focus" => Some(SkimEvent::Focus),
"zero" => Some(SkimEvent::Zero),
"one" => Some(SkimEvent::One),
_ => None,
}
}
}
impl From<SkimEvent> for KeyEvent {
fn from(event: SkimEvent) -> Self {
event.key_event()
}
}
#[derive(Clone, Debug)]
pub struct KeyMap(pub HashMap<KeyEvent, Vec<Action>>);
impl Deref for KeyMap {
type Target = HashMap<KeyEvent, Vec<Action>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for KeyMap {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<&str> for KeyMap {
fn from(value: &str) -> Self {
parse_keymaps(split_top_level(value, ',').into_iter())
}
}
impl Default for KeyMap {
fn default() -> Self {
get_default_key_map()
}
}
impl KeyMap {
pub(crate) fn add_keymaps_str(&mut self, source: &str) {
self.add_keymaps(split_top_level(source, ',').into_iter());
}
pub fn add_keymaps<'a, T>(&mut self, source: T)
where
T: Iterator<Item = &'a str>,
{
for map in source {
if let Ok((key, action_chain)) = parse_keymap(map) {
self.bind(key, action_chain)
.unwrap_or_else(|err| debug!("Failed to bind key {map}: {err}"));
} else {
debug!("Failed to parse key: {map}");
}
}
}
fn bind(&mut self, key: &str, action_chain: Vec<Action>) -> Result<()> {
let key = parse_key(key)?;
let _ = self.remove(&key);
self.entry(key).or_insert(action_chain);
Ok(())
}
}
#[rustfmt::skip]
#[must_use]
pub fn get_default_key_map() -> KeyMap {
let mut ret = HashMap::new();
ret.insert(KeyEvent::new(KeyCode::Down, KeyModifiers::NONE), vec![Action::Down(1)]);
ret.insert(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE), vec![Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::PageUp, KeyModifiers::NONE), vec![Action::PageUp(1)]);
ret.insert(KeyEvent::new(KeyCode::PageDown, KeyModifiers::NONE), vec![Action::PageDown(1)]);
ret.insert(KeyEvent::new(KeyCode::End, KeyModifiers::NONE), vec![Action::EndOfLine]);
ret.insert(KeyEvent::new(KeyCode::Home, KeyModifiers::NONE), vec![Action::BeginningOfLine]);
ret.insert(KeyEvent::new(KeyCode::Delete, KeyModifiers::NONE), vec![Action::DeleteChar]);
ret.insert(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE), vec![Action::Toggle, Action::Down(1)]);
ret.insert(KeyEvent::new(KeyCode::BackTab, KeyModifiers::all()), vec![Action::Toggle, Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE), vec![Action::Abort]);
ret.insert(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE), vec![Action::Accept(None)]);
ret.insert(KeyEvent::new(KeyCode::Left, KeyModifiers::NONE), vec![Action::BackwardChar]);
ret.insert(KeyEvent::new(KeyCode::Right, KeyModifiers::NONE), vec![Action::ForwardChar]);
ret.insert(KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE), vec![Action::BackwardDeleteChar]);
ret.insert(KeyEvent::new(KeyCode::Left, KeyModifiers::SHIFT), vec![Action::BackwardWord]);
ret.insert(KeyEvent::new(KeyCode::Right, KeyModifiers::SHIFT), vec![Action::ForwardWord]);
ret.insert(KeyEvent::new(KeyCode::Up, KeyModifiers::SHIFT), vec![Action::PreviewUp(1)]);
ret.insert(KeyEvent::new(KeyCode::Down, KeyModifiers::SHIFT), vec![Action::PreviewDown(1)]);
ret.insert(KeyEvent::new(KeyCode::Tab, KeyModifiers::SHIFT), vec![Action::Toggle, Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::BackTab, KeyModifiers::SHIFT), vec![Action::Toggle, Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::Home, KeyModifiers::SHIFT), vec![Action::BeginningOfLine]);
ret.insert(KeyEvent::new(KeyCode::Left, KeyModifiers::CONTROL), vec![Action::BackwardWord]);
ret.insert(KeyEvent::new(KeyCode::Right, KeyModifiers::CONTROL), vec![Action::ForwardWord]);
ret.insert(KeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL), vec![Action::BeginningOfLine]);
ret.insert(KeyEvent::new(KeyCode::Char('b'), KeyModifiers::CONTROL), vec![Action::BackwardChar]);
ret.insert(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL), vec![Action::Abort]);
ret.insert(KeyEvent::new(KeyCode::Char('d'), KeyModifiers::CONTROL), vec![Action::Abort]);
ret.insert(KeyEvent::new(KeyCode::Char('e'), KeyModifiers::CONTROL), vec![Action::EndOfLine]);
ret.insert(KeyEvent::new(KeyCode::Char('f'), KeyModifiers::CONTROL), vec![Action::ForwardChar]);
ret.insert(KeyEvent::new(KeyCode::Char('g'), KeyModifiers::CONTROL), vec![Action::Abort]);
ret.insert(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::CONTROL), vec![Action::BackwardDeleteChar]);
ret.insert(KeyEvent::new(KeyCode::Char('j'), KeyModifiers::CONTROL), vec![Action::Down(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('k'), KeyModifiers::CONTROL), vec![Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('l'), KeyModifiers::CONTROL), vec![Action::ClearScreen]);
ret.insert(KeyEvent::new(KeyCode::Char('n'), KeyModifiers::CONTROL), vec![Action::Down(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('p'), KeyModifiers::CONTROL), vec![Action::Up(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('q'), KeyModifiers::CONTROL), vec![Action::ToggleInteractive]);
ret.insert(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL), vec![Action::RotateMode]);
ret.insert(KeyEvent::new(KeyCode::Char('u'), KeyModifiers::CONTROL), vec![Action::UnixLineDiscard]);
ret.insert(KeyEvent::new(KeyCode::Char('w'), KeyModifiers::CONTROL), vec![Action::UnixWordRubout]);
ret.insert(KeyEvent::new(KeyCode::Char('y'), KeyModifiers::CONTROL), vec![Action::Yank]);
ret.insert(KeyEvent::new(KeyCode::Backspace, KeyModifiers::ALT), vec![Action::BackwardKillWord]);
ret.insert(KeyEvent::new(KeyCode::Char('b'), KeyModifiers::ALT), vec![Action::BackwardWord]);
ret.insert(KeyEvent::new(KeyCode::Char('d'), KeyModifiers::ALT), vec![Action::KillWord]);
ret.insert(KeyEvent::new(KeyCode::Char('f'), KeyModifiers::ALT), vec![Action::ForwardWord]);
ret.insert(KeyEvent::new(KeyCode::Char('h'), KeyModifiers::ALT), vec![Action::ScrollLeft(1)]);
ret.insert(KeyEvent::new(KeyCode::Char('l'), KeyModifiers::ALT), vec![Action::ScrollRight(1)]);
KeyMap(ret)
}
pub fn parse_key(key: &str) -> Result<KeyEvent> {
if key.is_empty() {
return Err(eyre!("Cannot parse empty key"));
}
let parts = key.split('-').collect::<Vec<&str>>();
let mut mods = KeyModifiers::NONE;
if parts.len() > 1 {
let mod_strs = &parts[..parts.len() - 1];
for mod_str in mod_strs {
mods |= match *mod_str {
"ctrl" => KeyModifiers::CONTROL,
"alt" => KeyModifiers::ALT,
"shift" => KeyModifiers::SHIFT,
s => return Err(eyre!("Failed to parse {} as key modifier", s)),
}
}
}
let key = parts.last().unwrap_or(&"").to_string();
let keycode: KeyCode;
if key.len() == 1 {
let char = key.chars().next().unwrap_or_default();
if char.is_uppercase() {
mods |= KeyModifiers::SHIFT;
keycode = KeyCode::Char(char.to_ascii_lowercase());
} else {
keycode = KeyCode::Char(char);
}
} else if let Some(f) = key.strip_prefix('f')
&& let Ok(f_index) = f.parse::<u8>()
{
keycode = KeyCode::F(f_index);
} else {
keycode = match key.as_str() {
"space" => KeyCode::Char(' '),
"enter" => KeyCode::Enter,
"bspace" | "bs" => KeyCode::Backspace,
"up" => KeyCode::Up,
"down" => KeyCode::Down,
"left" => KeyCode::Left,
"right" => KeyCode::Right,
"tab" => KeyCode::Tab,
"btab" => KeyCode::BackTab,
"esc" => KeyCode::Esc,
"home" => KeyCode::Home,
"end" => KeyCode::End,
"pgup" => KeyCode::PageUp,
"pgdown" => KeyCode::PageDown,
s => match SkimEvent::from_name(s) {
Some(event) => event.key_code(),
None => return Err(eyre!("Unknown key {}", s)),
},
}
}
debug!("parsed key {keycode:?} and mods {mods:?}");
Ok(KeyEvent::new(keycode, mods))
}
pub fn parse_keymaps<'a, T>(maps: T) -> KeyMap
where
T: Iterator<Item = &'a str>,
{
let mut res = KeyMap::default();
res.add_keymaps(maps);
res
}
pub(crate) fn split_top_level(value: &str, separator: char) -> Vec<&str> {
let mut depth = 0_u32;
let mut start = 0;
let mut parts = Vec::new();
for (index, ch) in value.char_indices() {
match ch {
'(' => depth += 1,
')' => depth = depth.saturating_sub(1),
_ if ch == separator && depth == 0 => {
parts.push(&value[start..index]);
start = index + ch.len_utf8();
}
_ => {}
}
}
parts.push(&value[start..]);
parts
}
#[must_use]
pub fn parse_action_binds<'a, T>(maps: T) -> HashMap<String, Vec<Action>>
where
T: Iterator<Item = &'a str>,
{
let mut res = HashMap::new();
for map in maps {
let Some((key, chain)) = map.split_once(':') else {
continue;
};
if parse_key(key).is_ok() {
continue;
}
let Some(name) = action_trigger_name(key) else {
debug!("Ignoring bind `{map}`: `{key}` is neither a key nor an action");
continue;
};
match parse_action_chain(chain) {
Ok(actions) => {
res.insert(name.to_string(), actions);
}
Err(err) => debug!("Ignoring bind `{map}`: invalid action chain `{chain}`: {err}"),
}
}
res
}
pub(crate) fn action_trigger_name(trigger: &str) -> Option<&'static str> {
let action_name = trigger.strip_prefix("act-").unwrap_or(trigger);
let action = event::parse_action(action_name).or_else(|| event::parse_action(&format!("{action_name}()")))?;
Some(action.name())
}
pub fn parse_action_chain(action_chain: &str) -> Result<Vec<Action>> {
let mut actions: Vec<Action> = vec![];
let mut split = split_top_level(action_chain, '+').into_iter();
while let Some(mut s) = split.next().map(String::from) {
if (s.starts_with("if-") || s.ends_with('{'))
&& let Some(otherwise) = split.next()
{
s += &(String::from("+") + otherwise);
}
if let Some(act) = event::parse_action(&s) {
actions.push(act);
}
}
if actions.is_empty() {
Err(eyre!("Empty action chain or unknown action `{}`", action_chain))
} else {
Ok(actions)
}
}
pub fn parse_keymap(key_action: &str) -> Result<(&str, Vec<Action>)> {
if key_action.is_empty() {
return Err(eyre!("Got an empty keybind, skipping"));
}
debug!("got key_action: {key_action:?}");
let (key, action_chain) = key_action
.split_once(':')
.ok_or(eyre!("Failed to parse {} as key and action", key_action))?;
debug!("parsed key_action: {key:?}: {action_chain:?}");
Ok((key, parse_action_chain(action_chain)?))
}
#[cfg(test)]
#[path = "binds_tests.rs"]
mod tests;