diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2024-08-03 00:08:47 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2024-08-03 00:08:47 +0200 |
| commit | eafde55afcdf9dc4c17c6c97c1db472fc9ff9957 (patch) | |
| tree | 838cab985495ead6dc005bd934f9e87c896e4ddd /src/config | |
| parent | bb9944d086332ed0b8d6064316225e901c456bd7 (diff) | |
add keymap for view and global
Diffstat (limited to 'src/config')
| -rw-r--r-- | src/config/keymap/event_from_string.rs | 78 | ||||
| -rw-r--r-- | src/config/keymap/keymap_store.rs | 81 | ||||
| -rw-r--r-- | src/config/keymap/mod.rs | 41 | ||||
| -rw-r--r-- | src/config/keymap/template.rs | 57 | ||||
| -rw-r--r-- | src/config/mod.rs | 13 |
5 files changed, 267 insertions, 3 deletions
diff --git a/src/config/keymap/event_from_string.rs b/src/config/keymap/event_from_string.rs new file mode 100644 index 0000000..00179c5 --- /dev/null +++ b/src/config/keymap/event_from_string.rs @@ -0,0 +1,78 @@ +use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; + +fn parse_key_names(s: &str) -> Option<KeyCode> { + if s.chars().count() == 1 { + return Some(KeyCode::Char(s.chars().next().unwrap())); + } + + match s { + "esc" => Some(KeyCode::Esc), + "up" => Some(KeyCode::Up), + "down" => Some(KeyCode::Down), + "left" => Some(KeyCode::Left), + "right" => Some(KeyCode::Right), + "backspace" => Some(KeyCode::Backspace), + "enter" => Some(KeyCode::Enter), + "home" => Some(KeyCode::Home), + "end" => Some(KeyCode::End), + "pageup" => Some(KeyCode::PageUp), + "pagedown" => Some(KeyCode::PageDown), + "tab" => Some(KeyCode::Tab), + "delete" => Some(KeyCode::Delete), + "insert" => Some(KeyCode::Insert), + "f1" => Some(KeyCode::F(1)), + "f2" => Some(KeyCode::F(2)), + "f3" => Some(KeyCode::F(3)), + "f4" => Some(KeyCode::F(4)), + "f5" => Some(KeyCode::F(5)), + "f6" => Some(KeyCode::F(6)), + "f7" => Some(KeyCode::F(7)), + "f8" => Some(KeyCode::F(8)), + "f9" => Some(KeyCode::F(9)), + "f10" => Some(KeyCode::F(10)), + "f11" => Some(KeyCode::F(11)), + "f12" => Some(KeyCode::F(12)), + "capslock" => Some(KeyCode::CapsLock), + "scrolllock" => Some(KeyCode::ScrollLock), + "numlock" => Some(KeyCode::NumLock), + "printscreen" => Some(KeyCode::PrintScreen), + "pause" => Some(KeyCode::Pause), + "menu" => Some(KeyCode::Menu), + "keypadbegin" => Some(KeyCode::KeypadBegin), + _ => None + } +} + +pub fn event_from_string(s: String) -> Result<KeyEvent, String> { + let mut modifiers = KeyModifiers::NONE; + let s = s.to_lowercase(); + + let code = if s.chars().count() == 1 { + KeyCode::Char(s.chars().next().unwrap()) + } else if s.starts_with('<') && s.ends_with('>') { + let mut inner = &s[1..s.len() - 1]; + + loop { + if inner.starts_with("c-") { + inner = &inner[2..]; + modifiers |= KeyModifiers::CONTROL; + } else if inner.starts_with("a-") { + inner = &inner[2..]; + modifiers |= KeyModifiers::ALT; + } else { + break; + } + } + + match parse_key_names(inner) { + Some(keycode) => keycode, + None => return Err(format!("{:?} is not a valid keyname", inner)) + } + } else { + return Err(format!("{:?} is not a valid keyname", s)); + }; + + println!("{:?}", KeyEvent::new(code, modifiers)); + + Ok(KeyEvent::new(code, modifiers)) +} diff --git a/src/config/keymap/keymap_store.rs b/src/config/keymap/keymap_store.rs new file mode 100644 index 0000000..c782219 --- /dev/null +++ b/src/config/keymap/keymap_store.rs @@ -0,0 +1,81 @@ +use core::fmt; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use mlua::{Function, RegistryKey, Result}; +use ratatui::crossterm::event::KeyEvent; + +use crate::lua; + +#[derive(Default, Clone)] +pub struct KeyMapStore { + store: Option<HashMap<KeyEvent, Arc<Mutex<dyn Runnable>>>>, +} + +impl KeyMapStore { + pub const fn new() -> Self { + Self { store: None } + } + + pub fn get(&mut self, event: KeyEvent) -> Option<Arc<Mutex<dyn Runnable>>> { + match &self.store { + Some(store) => match store.get(&event) { + Some(func) => Some(Arc::clone(func)), + None => None, + }, + None => { + self.store = Some(HashMap::new()); + None + } + } + } + + pub fn map(&mut self, event: KeyEvent, func: impl Runnable + 'static) { + match &self.store { + Some(_) => {} + None => self.store = Some(HashMap::new()), + } + + if let Some(store) = &mut self.store { + store.insert(event, Arc::new(Mutex::new(func))); + } + } +} + +pub trait Runnable +where + Self: Send, +{ + fn run(&self) -> Result<()>; +} + +impl<T> Runnable for T +where + T: Fn(), + Self: Send, +{ + fn run(&self) -> Result<()> { + self(); + Ok(()) + } +} + +impl Runnable for RegistryKey +where + Self: Send, +{ + fn run(&self) -> Result<()> { + let lua = lua::get(); + let func: Function = lua.registry_value(self)?; + func.call::<(), ()>(())?; + Ok(()) + } +} + +impl fmt::Debug for KeyMapStore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "KeyMapStore") + } +} diff --git a/src/config/keymap/mod.rs b/src/config/keymap/mod.rs new file mode 100644 index 0000000..e9995f2 --- /dev/null +++ b/src/config/keymap/mod.rs @@ -0,0 +1,41 @@ +use mlua::{Function, UserData}; +use ratatui::crossterm::event::KeyEvent; + +use self::{ + event_from_string::event_from_string, + keymap_store::{KeyMapStore, Runnable}, +}; + +use super::{GlobalConfig, DUMMY_CONFIG}; +use template::KeyMapSections; + +KeyMapSections!( + ViewKeyMap => view, + GlobalKeyMap => global, +); + +#[derive(Debug, Default, Clone)] +pub struct KeyMap { + pub view: ViewKeyMap, + pub global: GlobalKeyMap, +} + +pub mod event_from_string; +pub mod keymap_store; +pub mod template; + +impl KeyMap { + pub const fn new() -> Self { + Self { + view: ViewKeyMap::new(), + global: GlobalKeyMap::new(), + } + } +} + +impl UserData for KeyMap { + fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_function_get("view", |_, _| Ok(DUMMY_CONFIG.keymap.view.clone())); + fields.add_field_function_get("global", |_, _| Ok(DUMMY_CONFIG.keymap.global.clone())); + } +} diff --git a/src/config/keymap/template.rs b/src/config/keymap/template.rs new file mode 100644 index 0000000..bebcdc3 --- /dev/null +++ b/src/config/keymap/template.rs @@ -0,0 +1,57 @@ +macro_rules! KeyMapSections { + ($($name:ident => $key:ident),+ $(,)?) => { + $( + #[derive(Debug, Default, Clone)] + pub struct $name { + store: KeyMapStore, + } + + impl $name { + pub const fn new() -> Self { + Self { + store: KeyMapStore::new(), + } + } + + pub fn handle(event: KeyEvent) -> bool { + let func = { GlobalConfig::instance_mut().keymap.$key.store.get(event) }; + + if let Some(func) = func { + let func = func.lock().unwrap(); + if let Err(_error) = func.run() { + // TODO: add error buffer + } + true + } else { + false + } + } + + pub fn map(event: KeyEvent, func: impl Runnable + 'static) { + GlobalConfig::instance_mut() + .keymap + .$key + .store + .map(event, func) + } + } + + impl UserData for $name { + fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>( + methods: &mut M, + ) { + methods.add_function("map", |lua, (event, func): (String, Function<'_>)| { + let key = lua.create_registry_value(func)?; + + if let Ok(event) = event_from_string(event) { + $name::map(event, key); + } + Ok(()) + }); + } + } + )* + }; +} + +pub(super) use KeyMapSections; diff --git a/src/config/mod.rs b/src/config/mod.rs index 4f8ad7d..cc4154c 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,8 +1,9 @@ use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; +use lazy_static::lazy_static; use mlua::{UserData, UserDataFields}; -use self::theme::Theme; +use self::{keymap::KeyMap, theme::Theme}; pub mod constants; pub mod theme; @@ -11,15 +12,20 @@ pub mod keymap; #[derive(Debug, Default)] pub struct GlobalConfig { pub theme: Theme, + pub keymap: KeyMap, +} + +lazy_static! { + static ref GLOBAL_CONFIG: RwLock<GlobalConfig> = RwLock::new(GlobalConfig::new()); } -static GLOBAL_CONFIG: RwLock<GlobalConfig> = RwLock::new(GlobalConfig::new()); const DUMMY_CONFIG: GlobalConfig = GlobalConfig::new(); impl GlobalConfig { const fn new() -> Self { Self { theme: Theme::new(), + keymap: KeyMap::new(), } } @@ -34,6 +40,7 @@ impl GlobalConfig { impl UserData for GlobalConfig { fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) { - fields.add_field_function_get("theme", |_, _| Ok(DUMMY_CONFIG.theme)) + fields.add_field_function_get("theme", |_, _| Ok(DUMMY_CONFIG.theme)); + fields.add_field_function_get("keymap", |_, _| Ok(DUMMY_CONFIG.keymap)); } } |