mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-08 12:35:26 +00:00
refactor(config)!: simplify keybindings configuration syntax (#404)
BREAKING CHANGE: mode keybindings dropped in favor of a global table **What this means in practice:** ```toml [keybindings.Channel] quit = ["esc", "ctrl-c"] # ... [keybindings.RemoteControl] quit = ["esc", "ctrl-c"] # ... [keybindings.SendToChannel] quit = ["esc", "ctrl-c"] # ... ``` are being replaced with ```toml [keybindings] quit = ["esc", "ctrl-c"] # ... ``` Mode keybindings were I believe a premature optimization which only brought additional complexity and redundancy to the code and did not provide any real functionality in the current state of things for end users.
This commit is contained in:
parent
47ea5a2b68
commit
7a85728da6
@ -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