summaryrefslogtreecommitdiff
path: root/src/config/keymap
diff options
context:
space:
mode:
Diffstat (limited to 'src/config/keymap')
-rw-r--r--src/config/keymap/event_from_string.rs78
-rw-r--r--src/config/keymap/keymap_store.rs81
-rw-r--r--src/config/keymap/mod.rs41
-rw-r--r--src/config/keymap/template.rs57
4 files changed, 257 insertions, 0 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;