diff options
-rw-r--r-- | default-plugins/status-bar/src/first_line.rs | 2 | ||||
-rw-r--r-- | default-plugins/status-bar/src/main.rs | 6 | ||||
-rw-r--r-- | src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap | 2 | ||||
-rw-r--r-- | src/tests/fixtures/configs/changed_keys.kdl | 4 | ||||
-rw-r--r-- | zellij-utils/src/data.rs | 82 | ||||
-rw-r--r-- | zellij-utils/src/input/mod.rs | 10 | ||||
-rw-r--r-- | zellij-utils/src/plugin_api/key.rs | 86 |
7 files changed, 120 insertions, 72 deletions
diff --git a/default-plugins/status-bar/src/first_line.rs b/default-plugins/status-bar/src/first_line.rs index 23a6d5869..4ac090646 100644 --- a/default-plugins/status-bar/src/first_line.rs +++ b/default-plugins/status-bar/src/first_line.rs @@ -62,6 +62,8 @@ impl KeyShortcut { } else { match key { Key::F(c) => format!("{}", c), + Key::CtrlF(n) => format!("F{}", n), + Key::AltF(n) => format!("F{}", n), Key::Ctrl(c) => format!("{}", c), Key::Char(_) => format!("{}", key), Key::Alt(c) => format!("{}", c), diff --git a/default-plugins/status-bar/src/main.rs b/default-plugins/status-bar/src/main.rs index 2910d6786..1d85c7f69 100644 --- a/default-plugins/status-bar/src/main.rs +++ b/default-plugins/status-bar/src/main.rs @@ -333,8 +333,8 @@ pub fn get_common_modifier(keyvec: Vec<&Key>) -> Option<String> { let mut new_modifier; for key in keyvec.iter() { match key { - Key::Ctrl(_) => new_modifier = "Ctrl", - Key::Alt(_) => new_modifier = "Alt", + Key::Ctrl(_) | Key::CtrlF(_) => new_modifier = "Ctrl", + Key::Alt(_) | Key::AltF(_) => new_modifier = "Alt", _ => return None, } if modifier.is_empty() { @@ -468,7 +468,9 @@ pub fn style_key_with_modifier( } else { match key { Key::Ctrl(c) => format!("{}", Key::Char(*c)), + Key::CtrlF(n) => format!("{}", Key::F(*n)), Key::Alt(c) => format!("{}", c), + Key::AltF(n) => format!("{}", Key::F(*n)), _ => format!("{}", key), } } diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap index 2f983243f..974a63bd8 100644 --- a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap +++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__status_bar_loads_custom_keybindings.snap @@ -25,5 +25,5 @@ expression: last_snapshot │ │ │ │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ - <F1> LOCK <F2> PANE <F3> TAB <F4> RESIZE <F5> MOVE <F6> SEARCH <F7> SESSION <F8> QUIT + <F1> LOCK <F2> PANE <F3> TAB <F4> RESIZE <F5> MOVE <F6> SEARCH <Alt+F7> SESSION <Ctrl+F8> QUIT Tip: UNBOUND => open new pane. UNBOUND => navigate between panes. UNBOUND => increase/decrease pane size. diff --git a/src/tests/fixtures/configs/changed_keys.kdl b/src/tests/fixtures/configs/changed_keys.kdl index 6123b1d5b..c8bed1be2 100644 --- a/src/tests/fixtures/configs/changed_keys.kdl +++ b/src/tests/fixtures/configs/changed_keys.kdl @@ -6,7 +6,7 @@ keybinds clear-defaults=true { bind "F4" { SwitchToMode "Resize"; } bind "F5" { SwitchToMode "Move"; } bind "F6" { SwitchToMode "Scroll"; } - bind "F7" { SwitchToMode "Session"; } - bind "F8" { Quit; } + bind "Alt F7" { SwitchToMode "Session"; } + bind "Ctrl F8" { Quit; } } } diff --git a/zellij-utils/src/data.rs b/zellij-utils/src/data.rs index 998392ff7..204c57e43 100644 --- a/zellij-utils/src/data.rs +++ b/zellij-utils/src/data.rs @@ -62,6 +62,8 @@ pub enum Key { BackTab, Null, Esc, + AltF(u8), + CtrlF(u8), } impl FromStr for Key { @@ -78,14 +80,7 @@ impl FromStr for Key { } match (modifier, main_key) { (Some("Ctrl"), Some(main_key)) => { - let mut key_chars = main_key.chars(); - let key_count = main_key.chars().count(); - if key_count == 1 { - let key_char = key_chars.next().unwrap(); - Ok(Key::Ctrl(key_char)) - } else { - Err(format!("Failed to parse key: {}", key_str).into()) - } + parse_main_key(main_key, key_str, Key::Ctrl, Key::CtrlF) }, (Some("Alt"), Some(main_key)) => { match main_key { @@ -96,16 +91,12 @@ impl FromStr for Key { "Right" => Ok(Key::Alt(CharOrArrow::Direction(Direction::Right))), "Up" => Ok(Key::Alt(CharOrArrow::Direction(Direction::Up))), "Down" => Ok(Key::Alt(CharOrArrow::Direction(Direction::Down))), - _ => { - let mut key_chars = main_key.chars(); - let key_count = main_key.chars().count(); - if key_count == 1 { - let key_char = key_chars.next().unwrap(); - Ok(Key::Alt(CharOrArrow::Char(key_char))) - } else { - Err(format!("Failed to parse key: {}", key_str).into()) - } - }, + _ => parse_main_key( + main_key, + key_str, + |c| Key::Alt(CharOrArrow::Char(c)), + Key::AltF, + ), } }, (None, Some(main_key)) => match main_key { @@ -124,35 +115,42 @@ impl FromStr for Key { "Space" => Ok(Key::Char(' ')), "Enter" => Ok(Key::Char('\n')), "Esc" => Ok(Key::Esc), - _ => { - let mut key_chars = main_key.chars(); - let key_count = main_key.chars().count(); - if key_count == 1 { - let key_char = key_chars.next().unwrap(); - Ok(Key::Char(key_char)) - } else if key_count > 1 { - if let Some(first_char) = key_chars.next() { - if first_char == 'F' { - let f_index: String = key_chars.collect(); - let f_index: u8 = f_index - .parse() - .map_err(|e| format!("Failed to parse F index: {}", e))?; - if f_index >= 1 && f_index <= 12 { - return Ok(Key::F(f_index)); - } - } - } - Err(format!("Failed to parse key: {}", key_str).into()) - } else { - Err(format!("Failed to parse key: {}", key_str).into()) - } - }, + _ => parse_main_key(main_key, key_str, Key::Char, Key::F), }, _ => Err(format!("Failed to parse key: {}", key_str).into()), } } } +fn parse_main_key( + main_key: &str, + key_str: &str, + to_char_key: impl FnOnce(char) -> Key, + to_fn_key: impl FnOnce(u8) -> Key, +) -> Result<Key, Box<dyn std::error::Error>> { + let mut key_chars = main_key.chars(); + let key_count = main_key.chars().count(); + if key_count == 1 { + let key_char = key_chars.next().unwrap(); + Ok(to_char_key(key_char)) + } else if key_count > 1 { + if let Some(first_char) = key_chars.next() { + if first_char == 'F' { + let f_index: String = key_chars.collect(); + let f_index: u8 = f_index + .parse() + .map_err(|e| format!("Failed to parse F index: {}", e))?; + if f_index >= 1 && f_index <= 12 { + return Ok(to_fn_key(f_index)); + } + } + } + Err(format!("Failed to parse key: {}", key_str).into()) + } else { + Err(format!("Failed to parse key: {}", key_str).into()) + } +} + impl fmt::Display for Key { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -177,6 +175,8 @@ impl fmt::Display for Key { }, Key::Alt(c) => write!(f, "Alt+{}", c), Key::Ctrl(c) => write!(f, "Ctrl+{}", Key::Char(*c)), + Key::AltF(n) => write!(f, "Alt+F{}", n), + Key::CtrlF(n) => write!(f, "Ctrl+F{}", n), Key::Null => write!(f, "NULL"), Key::Esc => write!(f, "ESC"), } diff --git a/zellij-utils/src/input/mod.rs b/zellij-utils/src/input/mod.rs index 1c95f348b..1d30841f3 100644 --- a/zellij-utils/src/input/mod.rs +++ b/zellij-utils/src/input/mod.rs @@ -113,7 +113,15 @@ mod not_wasm { KeyCode::Tab => Key::BackTab, // TODO: ??? KeyCode::Delete => Key::Delete, KeyCode::Insert => Key::Insert, - KeyCode::Function(n) => Key::F(n), + KeyCode::Function(n) => { + if modifiers.contains(Modifiers::ALT) { + Key::AltF(n) + } else if modifiers.contains(Modifiers::CTRL) { + Key::CtrlF(n) + } else { + Key::F(n) + } + }, KeyCode::Escape => Key::Esc, KeyCode::Enter => Key::Char('\n'), _ => Key::Esc, // there are other keys we can implement here, but we might need additional terminal support to implement them, not just exhausting this enum diff --git a/zellij-utils/src/plugin_api/key.rs b/zellij-utils/src/plugin_api/key.rs index 7cef4ba76..c5ce10870 100644 --- a/zellij-utils/src/plugin_api/key.rs +++ b/zellij-utils/src/plugin_api/key.rs @@ -12,12 +12,21 @@ impl TryFrom<ProtobufKey> for Key { let key_modifier = parse_optional_modifier(&protobuf_key); match key_modifier { Some(KeyModifier::Ctrl) => { - let character = char_from_main_key(protobuf_key.main_key)?; - Ok(Key::Ctrl(character)) + if let Ok(character) = char_from_main_key(protobuf_key.main_key.clone()) { + Ok(Key::Ctrl(character)) + } else { + let index = fn_index_from_main_key(protobuf_key.main_key)?; + Ok(Key::CtrlF(index)) + } }, Some(KeyModifier::Alt) => { - let char_or_arrow = CharOrArrow::from_main_key(protobuf_key.main_key)?; - Ok(Key::Alt(char_or_arrow)) + if let Ok(char_or_arrow) = CharOrArrow::from_main_key(protobuf_key.main_key.clone()) + { + Ok(Key::Alt(char_or_arrow)) + } else { + let index = fn_index_from_main_key(protobuf_key.main_key)?; + Ok(Key::AltF(index)) + } }, None => match protobuf_key.main_key.as_ref().ok_or("invalid key")? { MainKey::Char(_key_index) => { @@ -81,27 +90,18 @@ impl TryFrom<Key> for ProtobufKey { modifier: None, main_key: Some(MainKey::Key(NamedKey::Insert as i32)), }), - Key::F(index) => { - let main_key = match index { - 1 => Some(MainKey::Key(NamedKey::F1 as i32)), - 2 => Some(MainKey::Key(NamedKey::F2 as i32)), - 3 => Some(MainKey::Key(NamedKey::F3 as i32)), - 4 => Some(MainKey::Key(NamedKey::F4 as i32)), - 5 => Some(MainKey::Key(NamedKey::F5 as i32)), - 6 => Some(MainKey::Key(NamedKey::F6 as i32)), - 7 => Some(MainKey::Key(NamedKey::F7 as i32)), - 8 => Some(MainKey::Key(NamedKey::F8 as i32)), - 9 => Some(MainKey::Key(NamedKey::F9 as i32)), - 10 => Some(MainKey::Key(NamedKey::F10 as i32)), - 11 => Some(MainKey::Key(NamedKey::F11 as i32)), - 12 => Some(MainKey::Key(NamedKey::F12 as i32)), - _ => return Err("Invalid key"), - }; - Ok(ProtobufKey { - modifier: None, - main_key, - }) - }, + Key::F(index) => Ok(ProtobufKey { + modifier: None, + main_key: Some(fn_index_to_main_key(index)?), + }), + Key::CtrlF(index) => Ok(ProtobufKey { + modifier: Some(KeyModifier::Ctrl as i32), + main_key: Some(fn_index_to_main_key(index)?), + }), + Key::AltF(index) => Ok(ProtobufKey { + modifier: Some(KeyModifier::Alt as i32), + main_key: Some(fn_index_to_main_key(index)?), + }), Key::Char(character) => Ok(ProtobufKey { modifier: None, main_key: Some(MainKey::Char((character as u8) as i32)), @@ -147,6 +147,24 @@ impl TryFrom<Key> for ProtobufKey { } } +fn fn_index_to_main_key(index: u8) -> Result<MainKey, &'static str> { + match index { + 1 => Ok(MainKey::Key(NamedKey::F1 as i32)), + 2 => Ok(MainKey::Key(NamedKey::F2 as i32)), + 3 => Ok(MainKey::Key(NamedKey::F3 as i32)), + 4 => Ok(MainKey::Key(NamedKey::F4 as i32)), + 5 => Ok(MainKey::Key(NamedKey::F5 as i32)), + 6 => Ok(MainKey::Key(NamedKey::F6 as i32)), + 7 => Ok(MainKey::Key(NamedKey::F7 as i32)), + 8 => Ok(MainKey::Key(NamedKey::F8 as i32)), + 9 => Ok(MainKey::Key(NamedKey::F9 as i32)), + 10 => Ok(MainKey::Key(NamedKey::F10 as i32)), + 11 => Ok(MainKey::Key(NamedKey::F11 as i32)), + 12 => Ok(MainKey::Key(NamedKey::F12 as i32)), + _ => Err("Invalid key"), + } +} + impl CharOrArrow { pub fn from_main_key( main_key: std::option::Option<MainKey>, @@ -191,6 +209,24 @@ fn char_from_main_key(main_key: Option<MainKey>) -> Result<char, &'static str> { } } +fn fn_index_from_main_key(main_key: Option<MainKey>) -> Result<u8, &'static str> { + match main_key { + Some(MainKey::Key(n)) if n == NamedKey::F1 as i32 => Ok(1), + Some(MainKey::Key(n)) if n == NamedKey::F2 as i32 => Ok(2), + Some(MainKey::Key(n)) if n == NamedKey::F3 as i32 => Ok(3), + Some(MainKey::Key(n)) if n == NamedKey::F4 as i32 => Ok(4), + Some(MainKey::Key(n)) if n == NamedKey::F5 as i32 => Ok(5), + Some(MainKey::Key(n)) if n == NamedKey::F6 as i32 => Ok(6), + Some(MainKey::Key(n)) if n == NamedKey::F7 as i32 => Ok(7), + Some(MainKey::Key(n)) if n == NamedKey::F8 as i32 => Ok(8), + Some(MainKey::Key(n)) if n == NamedKey::F9 as i32 => Ok(9), + Some(MainKey::Key(n)) if n == NamedKey::F10 as i32 => Ok(10), + Some(MainKey::Key(n)) if n == NamedKey::F11 as i32 => Ok(11), + Some(MainKey::Key(n)) if n == NamedKey::F12 as i32 => Ok(12), + _ => Err("Unsupported key"), + } +} + fn named_key_to_key(named_key: NamedKey) -> Key { match named_key { NamedKey::PageDown => Key::PageDown, |