mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-07 20:15:23 +00:00
refactor(config)!: simplify keybindings configuration syntax
BREAKING CHANGE: mode keybindings dropped in favor of a global table
This commit is contained in:
parent
47ea5a2b68
commit
7639707247
@ -72,7 +72,7 @@ theme = "TwoDark"
|
||||
#
|
||||
# Channel mode
|
||||
# ------------------------
|
||||
[keybindings.Channel]
|
||||
[keybindings]
|
||||
# Quit the application
|
||||
quit = ["esc", "ctrl-c"]
|
||||
# Scrolling through entries
|
||||
@ -101,46 +101,6 @@ toggle_help = "ctrl-g"
|
||||
toggle_preview = "ctrl-o"
|
||||
|
||||
|
||||
# Remote control mode
|
||||
# -------------------------------
|
||||
[keybindings.RemoteControl]
|
||||
# Quit the application
|
||||
quit = "esc"
|
||||
# Scrolling through entries
|
||||
select_next_entry = ["down", "ctrl-n", "ctrl-j"]
|
||||
select_prev_entry = ["up", "ctrl-p", "ctrl-k"]
|
||||
select_next_page = "pagedown"
|
||||
select_prev_page = "pageup"
|
||||
# Select an entry
|
||||
select_entry = "enter"
|
||||
# Toggle the remote control mode
|
||||
toggle_remote_control = "ctrl-r"
|
||||
# Toggle the help bar
|
||||
toggle_help = "ctrl-g"
|
||||
# Toggle the preview panel
|
||||
toggle_preview = "ctrl-o"
|
||||
|
||||
|
||||
# Send to channel mode
|
||||
# --------------------------------
|
||||
[keybindings.SendToChannel]
|
||||
# Quit the application
|
||||
quit = "esc"
|
||||
# Scrolling through entries
|
||||
select_next_entry = ["down", "ctrl-n", "ctrl-j"]
|
||||
select_prev_entry = ["up", "ctrl-p", "ctrl-k"]
|
||||
select_next_page = "pagedown"
|
||||
select_prev_page = "pageup"
|
||||
# Select an entry
|
||||
select_entry = "enter"
|
||||
# Toggle the send to channel mode
|
||||
toggle_send_to_channel = "ctrl-s"
|
||||
# Toggle the help bar
|
||||
toggle_help = "ctrl-g"
|
||||
# Toggle the preview panel
|
||||
toggle_preview = "ctrl-o"
|
||||
|
||||
|
||||
# Shell integration
|
||||
# ----------------------------------------------------------------------------
|
||||
#
|
||||
|
@ -6,7 +6,9 @@ use tracing::{debug, info, trace};
|
||||
|
||||
use crate::channels::entry::Entry;
|
||||
use crate::channels::TelevisionChannel;
|
||||
use crate::config::{parse_key, Config};
|
||||
use crate::config::{
|
||||
merge_keybindings, parse_key, Binding, Config, KeyBindings,
|
||||
};
|
||||
use crate::keymap::Keymap;
|
||||
use crate::render::UiState;
|
||||
use crate::television::{Mode, Television};
|
||||
@ -109,16 +111,22 @@ impl App {
|
||||
let (_, event_rx) = mpsc::unbounded_channel();
|
||||
let (event_abort_tx, _) = mpsc::unbounded_channel();
|
||||
let tick_rate = config.config.tick_rate;
|
||||
let keymap = Keymap::from(&config.keybindings).with_mode_mappings(
|
||||
Mode::Channel,
|
||||
passthrough_keybindings
|
||||
.iter()
|
||||
.flat_map(|s| match parse_key(s) {
|
||||
Ok(key) => Ok((key, Action::SelectPassthrough(s.clone()))),
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
let keybindings = merge_keybindings(config.keybindings.clone(), {
|
||||
&KeyBindings::from(passthrough_keybindings.iter().filter_map(
|
||||
|s| match parse_key(s) {
|
||||
Ok(key) => Some((
|
||||
Action::SelectPassthrough(s.to_string()),
|
||||
Binding::SingleKey(key),
|
||||
)),
|
||||
Err(e) => {
|
||||
debug!("Failed to parse keybinding: {}", e);
|
||||
None
|
||||
}
|
||||
},
|
||||
))
|
||||
});
|
||||
let keymap = Keymap::from(&keybindings);
|
||||
|
||||
debug!("{:?}", keymap);
|
||||
let (ui_state_tx, ui_state_rx) = mpsc::unbounded_channel();
|
||||
let television =
|
||||
@ -258,14 +266,13 @@ impl App {
|
||||
_ => {}
|
||||
}
|
||||
// get action based on keybindings
|
||||
self.keymap
|
||||
.get(&self.television.mode)
|
||||
.and_then(|keymap| keymap.get(&keycode).cloned())
|
||||
.unwrap_or(if let Key::Char(c) = keycode {
|
||||
self.keymap.get(&keycode).cloned().unwrap_or(
|
||||
if let Key::Char(c) = keycode {
|
||||
Action::AddInputChar(c)
|
||||
} else {
|
||||
Action::NoOp
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
// terminal events
|
||||
Event::Tick => Action::Tick,
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::action::Action;
|
||||
use crate::event::{convert_raw_event_to_key, Key};
|
||||
use crate::television::Mode;
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
@ -30,7 +29,16 @@ impl Display for Binding {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct KeyBindings(pub FxHashMap<Mode, FxHashMap<Action, Binding>>);
|
||||
pub struct KeyBindings(pub FxHashMap<Action, Binding>);
|
||||
|
||||
impl<I> From<I> for KeyBindings
|
||||
where
|
||||
I: IntoIterator<Item = (Action, Binding)>,
|
||||
{
|
||||
fn from(iter: I) -> Self {
|
||||
KeyBindings(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for KeyBindings {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
@ -40,7 +48,7 @@ impl Hash for KeyBindings {
|
||||
}
|
||||
|
||||
impl Deref for KeyBindings {
|
||||
type Target = FxHashMap<Mode, FxHashMap<Action, Binding>>;
|
||||
type Target = FxHashMap<Action, Binding>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
@ -52,27 +60,20 @@ impl DerefMut for KeyBindings {
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge two sets of keybindings together.
|
||||
///
|
||||
/// Note that this function won't "meld", for a given action, the bindings from the first set
|
||||
/// with the bindings from the second set. Instead, it will simply overwrite them with the second
|
||||
/// set's keys.
|
||||
/// This is because it is assumed that the second set will be the user's custom keybindings, and
|
||||
/// they should take precedence over the default ones, effectively replacing them to avoid
|
||||
/// conflicts.
|
||||
pub fn merge_keybindings(
|
||||
mut keybindings: KeyBindings,
|
||||
new_keybindings: &KeyBindings,
|
||||
) -> KeyBindings {
|
||||
for (mode, bindings) in new_keybindings.iter() {
|
||||
for (action, binding) in bindings {
|
||||
match keybindings.get_mut(mode) {
|
||||
Some(mode_bindings) => {
|
||||
mode_bindings.insert(action.clone(), binding.clone());
|
||||
}
|
||||
None => {
|
||||
keybindings.insert(
|
||||
*mode,
|
||||
[(action.clone(), binding.clone())]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (action, binding) in new_keybindings.iter() {
|
||||
keybindings.insert(action.clone(), binding.clone());
|
||||
}
|
||||
keybindings
|
||||
}
|
||||
@ -89,43 +90,30 @@ impl<'de> Deserialize<'de> for KeyBindings {
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let parsed_map = FxHashMap::<
|
||||
Mode,
|
||||
FxHashMap<Action, SerializedBinding>,
|
||||
>::deserialize(deserializer)?;
|
||||
let parsed_map =
|
||||
FxHashMap::<Action, SerializedBinding>::deserialize(deserializer)?;
|
||||
|
||||
let keybindings: FxHashMap<Mode, FxHashMap<Action, Binding>> =
|
||||
parsed_map
|
||||
.into_iter()
|
||||
.map(|(mode, inner_map)| {
|
||||
let converted_inner_map = inner_map
|
||||
.into_iter()
|
||||
.map(|(cmd, binding)| {
|
||||
(
|
||||
cmd,
|
||||
match binding {
|
||||
SerializedBinding::SingleKey(key_str) => {
|
||||
Binding::SingleKey(
|
||||
parse_key(&key_str).unwrap(),
|
||||
)
|
||||
}
|
||||
SerializedBinding::MultipleKeys(
|
||||
keys_str,
|
||||
) => Binding::MultipleKeys(
|
||||
keys_str
|
||||
.iter()
|
||||
.map(|key_str| {
|
||||
parse_key(key_str).unwrap()
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
},
|
||||
let keybindings: FxHashMap<Action, Binding> = parsed_map
|
||||
.into_iter()
|
||||
.map(|(cmd, binding)| {
|
||||
(
|
||||
cmd,
|
||||
match binding {
|
||||
SerializedBinding::SingleKey(key_str) => {
|
||||
Binding::SingleKey(parse_key(&key_str).unwrap())
|
||||
}
|
||||
SerializedBinding::MultipleKeys(keys_str) => {
|
||||
Binding::MultipleKeys(
|
||||
keys_str
|
||||
.iter()
|
||||
.map(|key_str| parse_key(key_str).unwrap())
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
(mode, converted_inner_map)
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(KeyBindings(keybindings))
|
||||
}
|
||||
@ -380,4 +368,127 @@ mod tests {
|
||||
KeyEvent::new(KeyCode::Enter, KeyModifiers::ALT)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_keybindings() {
|
||||
let keybindings: KeyBindings = toml::from_str(
|
||||
r#"
|
||||
# Quit the application
|
||||
quit = ["esc", "ctrl-c"]
|
||||
# Scrolling through entries
|
||||
select_next_entry = ["down", "ctrl-n", "ctrl-j"]
|
||||
select_prev_entry = ["up", "ctrl-p", "ctrl-k"]
|
||||
select_next_page = "pagedown"
|
||||
select_prev_page = "pageup"
|
||||
# Scrolling the preview pane
|
||||
scroll_preview_half_page_down = "ctrl-d"
|
||||
scroll_preview_half_page_up = "ctrl-u"
|
||||
# Add entry to selection and move to the next entry
|
||||
toggle_selection_down = "tab"
|
||||
# Add entry to selection and move to the previous entry
|
||||
toggle_selection_up = "backtab"
|
||||
# Confirm selection
|
||||
confirm_selection = "enter"
|
||||
# Copy the selected entry to the clipboard
|
||||
copy_entry_to_clipboard = "ctrl-y"
|
||||
# Toggle the remote control mode
|
||||
toggle_remote_control = "ctrl-r"
|
||||
# Toggle the send to channel mode
|
||||
toggle_send_to_channel = "ctrl-s"
|
||||
# Toggle the help bar
|
||||
toggle_help = "ctrl-g"
|
||||
# Toggle the preview panel
|
||||
toggle_preview = "ctrl-o"
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
keybindings,
|
||||
KeyBindings::from(vec![
|
||||
(
|
||||
Action::Quit,
|
||||
Binding::MultipleKeys(vec![Key::Esc, Key::Ctrl('c'),])
|
||||
),
|
||||
(
|
||||
Action::SelectNextEntry,
|
||||
Binding::MultipleKeys(vec![
|
||||
Key::Down,
|
||||
Key::Ctrl('n'),
|
||||
Key::Ctrl('j'),
|
||||
])
|
||||
),
|
||||
(
|
||||
Action::SelectPrevEntry,
|
||||
Binding::MultipleKeys(vec![
|
||||
Key::Up,
|
||||
Key::Ctrl('p'),
|
||||
Key::Ctrl('k'),
|
||||
])
|
||||
),
|
||||
(Action::SelectNextPage, Binding::SingleKey(Key::PageDown)),
|
||||
(Action::SelectPrevPage, Binding::SingleKey(Key::PageUp)),
|
||||
(
|
||||
Action::ScrollPreviewHalfPageDown,
|
||||
Binding::SingleKey(Key::Ctrl('d'))
|
||||
),
|
||||
(
|
||||
Action::ScrollPreviewHalfPageUp,
|
||||
Binding::SingleKey(Key::Ctrl('u'))
|
||||
),
|
||||
(Action::ToggleSelectionDown, Binding::SingleKey(Key::Tab)),
|
||||
(Action::ToggleSelectionUp, Binding::SingleKey(Key::BackTab)),
|
||||
(Action::ConfirmSelection, Binding::SingleKey(Key::Enter)),
|
||||
(
|
||||
Action::CopyEntryToClipboard,
|
||||
Binding::SingleKey(Key::Ctrl('y'))
|
||||
),
|
||||
(
|
||||
Action::ToggleRemoteControl,
|
||||
Binding::SingleKey(Key::Ctrl('r'))
|
||||
),
|
||||
(
|
||||
Action::ToggleSendToChannel,
|
||||
Binding::SingleKey(Key::Ctrl('s'))
|
||||
),
|
||||
(Action::ToggleHelp, Binding::SingleKey(Key::Ctrl('g'))),
|
||||
(Action::TogglePreview, Binding::SingleKey(Key::Ctrl('o'))),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_merge_keybindings() {
|
||||
let base_keybindings = KeyBindings::from(vec![
|
||||
(Action::Quit, Binding::SingleKey(Key::Esc)),
|
||||
(
|
||||
Action::SelectNextEntry,
|
||||
Binding::MultipleKeys(vec![Key::Down, Key::Ctrl('n')]),
|
||||
),
|
||||
(Action::SelectPrevEntry, Binding::SingleKey(Key::Up)),
|
||||
]);
|
||||
let custom_keybindings = KeyBindings::from(vec![
|
||||
(Action::SelectNextEntry, Binding::SingleKey(Key::Ctrl('j'))),
|
||||
(
|
||||
Action::SelectPrevEntry,
|
||||
Binding::MultipleKeys(vec![Key::Up, Key::Ctrl('k')]),
|
||||
),
|
||||
(Action::SelectNextPage, Binding::SingleKey(Key::PageDown)),
|
||||
]);
|
||||
|
||||
let merged = merge_keybindings(base_keybindings, &custom_keybindings);
|
||||
|
||||
assert_eq!(
|
||||
merged,
|
||||
KeyBindings::from(vec![
|
||||
(Action::Quit, Binding::SingleKey(Key::Esc)),
|
||||
(Action::SelectNextEntry, Binding::SingleKey(Key::Ctrl('j'))),
|
||||
(
|
||||
Action::SelectPrevEntry,
|
||||
Binding::MultipleKeys(vec![Key::Up, Key::Ctrl('k')]),
|
||||
),
|
||||
(Action::SelectNextPage, Binding::SingleKey(Key::PageDown)),
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ use std::{
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use directories::ProjectDirs;
|
||||
use keybindings::merge_keybindings;
|
||||
pub use keybindings::merge_keybindings;
|
||||
pub use keybindings::{parse_key, Binding, KeyBindings};
|
||||
use previewers::PreviewersConfig;
|
||||
use serde::Deserialize;
|
||||
@ -100,6 +100,17 @@ pub fn default_config_from_file() -> Result<Config> {
|
||||
Ok(default_config)
|
||||
}
|
||||
|
||||
const USER_CONFIG_ERROR_MSG: &str = "
|
||||
╔══════════════════════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ If this follows a recent update, it is likely due to a breaking change in ║
|
||||
║ the configuration format. ║
|
||||
║ ║
|
||||
║ Check https://github.com/alexpasmantier/television/releases/latest for the ║
|
||||
║ latest release notes. ║
|
||||
║ ║
|
||||
╚══════════════════════════════════════════════════════════════════════════════╝";
|
||||
|
||||
impl Config {
|
||||
#[allow(clippy::missing_panics_doc, clippy::missing_errors_doc)]
|
||||
pub fn new(config_env: &ConfigEnv) -> Result<Self> {
|
||||
@ -134,8 +145,11 @@ impl Config {
|
||||
fn load_user_config(config_dir: &Path) -> Result<Self> {
|
||||
let path = config_dir.join(CONFIG_FILE_NAME);
|
||||
let contents = std::fs::read_to_string(&path)?;
|
||||
let user_cfg: Config = toml::from_str(&contents)
|
||||
.context("Error parsing configuration file.")?;
|
||||
let user_cfg: Config = toml::from_str(&contents).context(format!(
|
||||
"Error parsing configuration file: {}\n{}",
|
||||
path.display(),
|
||||
USER_CONFIG_ERROR_MSG,
|
||||
))?;
|
||||
Ok(user_cfg)
|
||||
}
|
||||
|
||||
@ -246,7 +260,6 @@ fn default_tick_rate() -> f64 {
|
||||
mod tests {
|
||||
use crate::action::Action;
|
||||
use crate::event::Key;
|
||||
use crate::television::Mode;
|
||||
|
||||
use super::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
@ -322,11 +335,9 @@ mod tests {
|
||||
[previewers.file]
|
||||
theme = "Visual Studio Dark"
|
||||
|
||||
[keybindings.Channel]
|
||||
[keybindings]
|
||||
toggle_help = ["ctrl-a", "ctrl-b"]
|
||||
|
||||
[keybindings.RemoteControl]
|
||||
toggle_help = ["ctrl-c", "ctrl-d"]
|
||||
confirm_selection = "ctrl-enter"
|
||||
|
||||
[shell_integration.commands]
|
||||
"git add" = "git-diff"
|
||||
@ -358,36 +369,18 @@ mod tests {
|
||||
default_config.ui.theme = "television".to_string();
|
||||
default_config.previewers.file.theme =
|
||||
"Visual Studio Dark".to_string();
|
||||
default_config
|
||||
.keybindings
|
||||
.get_mut(&Mode::Channel)
|
||||
.unwrap()
|
||||
.extend({
|
||||
let mut map = FxHashMap::default();
|
||||
map.insert(
|
||||
Action::ToggleHelp,
|
||||
Binding::MultipleKeys(vec![
|
||||
Key::Ctrl('a'),
|
||||
Key::Ctrl('b'),
|
||||
]),
|
||||
);
|
||||
map
|
||||
});
|
||||
default_config
|
||||
.keybindings
|
||||
.get_mut(&Mode::RemoteControl)
|
||||
.unwrap()
|
||||
.extend({
|
||||
let mut map = FxHashMap::default();
|
||||
map.insert(
|
||||
Action::ToggleHelp,
|
||||
Binding::MultipleKeys(vec![
|
||||
Key::Ctrl('c'),
|
||||
Key::Ctrl('d'),
|
||||
]),
|
||||
);
|
||||
map
|
||||
});
|
||||
default_config.keybindings.extend({
|
||||
let mut map = FxHashMap::default();
|
||||
map.insert(
|
||||
Action::ToggleHelp,
|
||||
Binding::MultipleKeys(vec![Key::Ctrl('a'), Key::Ctrl('b')]),
|
||||
);
|
||||
map.insert(
|
||||
Action::ConfirmSelection,
|
||||
Binding::SingleKey(Key::CtrlEnter),
|
||||
);
|
||||
map
|
||||
});
|
||||
|
||||
default_config
|
||||
.shell_integration
|
||||
|
@ -193,16 +193,12 @@ pub fn draw(ctx: &Ctx, f: &mut Frame<'_>, area: Rect) -> Result<Layout> {
|
||||
&ctx.colorscheme,
|
||||
&ctx.config
|
||||
.keybindings
|
||||
.get(&ctx.tv_state.mode)
|
||||
.unwrap()
|
||||
.get(&Action::ToggleHelp)
|
||||
// just display the first keybinding
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
&ctx.config
|
||||
.keybindings
|
||||
.get(&ctx.tv_state.mode)
|
||||
.unwrap()
|
||||
.get(&Action::TogglePreview)
|
||||
// just display the first keybinding
|
||||
.unwrap()
|
||||
|
@ -1,8 +1,6 @@
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::television::Mode;
|
||||
|
||||
use crate::action::Action;
|
||||
use crate::config::{Binding, KeyBindings};
|
||||
use crate::event::Key;
|
||||
@ -13,20 +11,15 @@ use crate::event::Key;
|
||||
/// # Example:
|
||||
/// ```ignore
|
||||
/// Keymap {
|
||||
/// Mode::Channel => {
|
||||
/// Key::Char('j') => Action::MoveDown,
|
||||
/// Key::Char('k') => Action::MoveUp,
|
||||
/// Key::Char('q') => Action::Quit,
|
||||
/// },
|
||||
/// Mode::Insert => {
|
||||
/// Key::Ctrl('a') => Action::MoveToStart,
|
||||
/// },
|
||||
/// Key::Char('j') => Action::MoveDown,
|
||||
/// Key::Char('k') => Action::MoveUp,
|
||||
/// Key::Char('q') => Action::Quit,
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Keymap(pub FxHashMap<Mode, FxHashMap<Key, Action>>);
|
||||
pub struct Keymap(pub FxHashMap<Key, Action>);
|
||||
|
||||
impl Deref for Keymap {
|
||||
type Target = FxHashMap<Mode, FxHashMap<Key, Action>>;
|
||||
type Target = FxHashMap<Key, Action>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
@ -40,38 +33,18 @@ impl From<&KeyBindings> for Keymap {
|
||||
/// key events.
|
||||
fn from(keybindings: &KeyBindings) -> Self {
|
||||
let mut keymap = FxHashMap::default();
|
||||
for (mode, bindings) in keybindings.iter() {
|
||||
let mut mode_keymap = FxHashMap::default();
|
||||
for (action, binding) in bindings {
|
||||
match binding {
|
||||
Binding::SingleKey(key) => {
|
||||
mode_keymap.insert(*key, action.clone());
|
||||
}
|
||||
Binding::MultipleKeys(keys) => {
|
||||
for key in keys {
|
||||
mode_keymap.insert(*key, action.clone());
|
||||
}
|
||||
for (action, binding) in keybindings.iter() {
|
||||
match binding {
|
||||
Binding::SingleKey(key) => {
|
||||
keymap.insert(*key, action.clone());
|
||||
}
|
||||
Binding::MultipleKeys(keys) => {
|
||||
for key in keys {
|
||||
keymap.insert(*key, action.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
keymap.insert(*mode, mode_keymap);
|
||||
}
|
||||
Self(keymap)
|
||||
}
|
||||
}
|
||||
|
||||
impl Keymap {
|
||||
/// For a provided `Mode`, merge the given `mappings` into the keymap.
|
||||
pub fn with_mode_mappings(
|
||||
mut self,
|
||||
mode: Mode,
|
||||
mappings: Vec<(Key, Action)>,
|
||||
) -> Self {
|
||||
let mode_keymap = self.0.entry(mode).or_default();
|
||||
|
||||
for (key, action) in mappings {
|
||||
mode_keymap.insert(key, action);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -146,15 +146,7 @@ fn serialized_keys_for_actions(
|
||||
) -> Vec<String> {
|
||||
actions
|
||||
.iter()
|
||||
.map(|a| {
|
||||
keybindings
|
||||
.get(&Mode::Channel)
|
||||
.unwrap()
|
||||
.get(a)
|
||||
.unwrap()
|
||||
.clone()
|
||||
.to_string()
|
||||
})
|
||||
.map(|a| keybindings.get(a).unwrap().clone().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user