From 7c703e3d65aff9525846e8ae1ccee760a1b87343 Mon Sep 17 00:00:00 2001 From: Nick Dowsett Date: Fri, 6 Dec 2024 07:55:18 +0800 Subject: [PATCH] Add merge keymaps function --- youtui/src/config/keymap.rs | 129 +++++++++++++++++++++++++++++++----- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/youtui/src/config/keymap.rs b/youtui/src/config/keymap.rs index f84df05..0dab984 100644 --- a/youtui/src/config/keymap.rs +++ b/youtui/src/config/keymap.rs @@ -1,29 +1,41 @@ -use crate::{ - app::{ - component::actionhandler::Action, - ui::{ - action::{AppAction, HelpAction, ListAction, TextEntryAction}, - browser::{ - artistalbums::{ - albumsongs::{BrowserSongsAction, FilterAction, SortAction}, - artistsearch::{BrowserArtistsAction, BrowserSearchAction}, - }, - BrowserAction, - }, - logger::LoggerAction, - playlist::PlaylistAction::{self, ViewBrowser}, - }, - }, - keyaction::{KeyAction, KeyActionVisibility}, - keybind::Keybind, +use crate::app::component::actionhandler::Action; +use crate::app::ui::action::{AppAction, HelpAction, ListAction, TextEntryAction}; +use crate::app::ui::browser::artistalbums::albumsongs::{ + BrowserSongsAction, FilterAction, SortAction, }; +use crate::app::ui::browser::artistalbums::artistsearch::{ + BrowserArtistsAction, BrowserSearchAction, +}; +use crate::app::ui::browser::BrowserAction; +use crate::app::ui::logger::LoggerAction; +use crate::app::ui::playlist::PlaylistAction::{self, ViewBrowser}; +use crate::keyaction::{KeyAction, KeyActionVisibility}; +use crate::keybind::Keybind; use crossterm::event::KeyModifiers; use serde::{Deserialize, Serialize}; +use std::collections::btree_map::Entry; use std::{borrow::Cow, collections::BTreeMap, convert::Infallible, str::FromStr}; /// Convenience type alias pub type Keymap = BTreeMap>; +/// Merge `other` into `this` leaving `this` empty and returning the merged +/// keymap. This recurively handles modes, merging them also. +fn merge_keymaps(this: &mut Keymap, other: Keymap) { + for (other_key, other_tree) in other { + let entry = this.entry(other_key); + match entry { + Entry::Vacant(e) => { + e.insert(other_tree); + } + Entry::Occupied(mut e) => { + let this_tree = e.get_mut(); + this_tree.merge(other_tree); + } + } + } +} + #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum KeyStringTree { @@ -40,6 +52,28 @@ pub enum KeyActionTree { keys: BTreeMap>, }, } +impl KeyActionTree { + fn merge(&mut self, other: KeyActionTree) { + match self { + KeyActionTree::Key(_) => *self = other, + KeyActionTree::Mode { + name: this_name, + keys: keys_this, + } => match other { + KeyActionTree::Key(key_action) => *self = KeyActionTree::Key(key_action), + KeyActionTree::Mode { + name: other_name, + keys: keys_other, + } => { + if other_name.is_some() { + *this_name = other_name; + } + merge_keymaps(keys_this, keys_other); + } + }, + } + } +} #[derive(Debug, PartialEq)] pub struct YoutuiKeymap { @@ -778,3 +812,62 @@ fn default_list_keybinds() -> BTreeMap> { ), ]) } + +#[cfg(test)] +mod tests { + use super::{default_list_keybinds, merge_keymaps, KeyActionTree}; + use crate::{ + app::ui::action::{AppAction, ListAction}, + keyaction::KeyActionVisibility, + keybind::Keybind, + }; + + #[test] + fn test_add_key() { + let mut keys = default_list_keybinds(); + let to_add = FromIterator::from_iter([( + Keybind::new_unmodified(crossterm::event::KeyCode::Up), + KeyActionTree::new_key_defaulted(AppAction::Quit), + )]); + merge_keymaps(&mut keys, to_add); + let expected = FromIterator::from_iter([ + ( + Keybind::new_unmodified(crossterm::event::KeyCode::Up), + KeyActionTree::new_key( + AppAction::List(ListAction::Up), + 1, + KeyActionVisibility::Hidden, + ), + ), + ( + Keybind::new_unmodified(crossterm::event::KeyCode::Down), + KeyActionTree::new_key( + AppAction::List(ListAction::Down), + 1, + KeyActionVisibility::Hidden, + ), + ), + ( + Keybind::new_unmodified(crossterm::event::KeyCode::PageUp), + KeyActionTree::new_key( + AppAction::List(ListAction::Up), + 10, + KeyActionVisibility::Standard, + ), + ), + ( + Keybind::new_unmodified(crossterm::event::KeyCode::PageDown), + KeyActionTree::new_key( + AppAction::List(ListAction::Down), + 10, + KeyActionVisibility::Standard, + ), + ), + ( + Keybind::new_unmodified(crossterm::event::KeyCode::Up), + KeyActionTree::new_key_defaulted(AppAction::Quit), + ), + ]); + pretty_assertions::assert_eq!(keys, expected); + } +}