From b54f8e59e82b6931ac10f8dfa69437f584584e3b Mon Sep 17 00:00:00 2001 From: Soispha Date: Wed, 18 Oct 2023 22:52:07 +0200 Subject: [PATCH] fix(key_input): Handle shifted characters correctly This commit fixes multiple bugs introduced in the new keymapping system: When we can't find a keymapping for a string, we simply pass it along, by calling the 'send_input_unprocessed' function with the inputted key turned into it's string representation. Turning a shifted key into it's repr. returned a doubly shifted string: 'A' turned into '', which then fails to parse as a valid key input. Thus, we simply unshifted the value. The next bug, becoming apparent now, is caused by this: We never reshifted the value, when we converted it into a crossterm key input event. --- .../event_types/event/handlers/input.rs | 1 + src/app/keymappings/key/key.rs | 68 +++++++++++++++++-- src/app/keymappings/key/mod.rs | 15 ++++ 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/app/events/event_types/event/handlers/input.rs b/src/app/events/event_types/event/handlers/input.rs index a4b72b6..ce9bbad 100644 --- a/src/app/events/event_types/event/handlers/input.rs +++ b/src/app/events/event_types/event/handlers/input.rs @@ -12,6 +12,7 @@ use crate::app::{ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result { async fn default(converted_key: Key, app: &mut App<'_>, old_state: &State) -> Result<()> { + info!("No keymaps exist for key ('{}'), passing it along..", converted_key); // Just let the input event slip through if no keymap matches app.tx .send(Event::CommandEvent( diff --git a/src/app/keymappings/key/key.rs b/src/app/keymappings/key/key.rs index 74f06cb..f56609a 100644 --- a/src/app/keymappings/key/key.rs +++ b/src/app/keymappings/key/key.rs @@ -1,7 +1,8 @@ -use std::{str::FromStr, fmt::Display}; +use std::{fmt::Display, str::FromStr}; use anyhow::{bail, Context}; -use crossterm::event::{Event, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers}; +use cli_log::{debug, info}; +use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers}; use super::{Chars, KeyValue, Keys}; @@ -236,7 +237,33 @@ impl Into for Key { } let output = Event::Key(KeyEvent { - code: self.value.unwrap_or(KeyValue::Null).into(), + code: { + // We sorta hit a edge case here, if we have a shifted char we need to tell + // crossterm about that. Thus we need to manually apply the shift modifier on the + // value. + if self.shift { + if let Some(KeyValue::Char(char)) = self.value { + let upper_case_char: char = { + let chars = char.to_uppercase().collect::>(); + if chars.len() != 1 { + unimplemented!( + " + I have no idea how we handle this case, + we'll just have to hope, that it never comes up. + " + ); + } else { + *chars.first().expect("We checked the length") + } + }; + KeyCode::Char(upper_case_char) + } else { + self.value.unwrap_or(KeyValue::Null).into() + } + } else { + self.value.unwrap_or(KeyValue::Null).into() + } + }, modifiers, kind: KeyEventKind::Press, state: KeyEventState::NONE, @@ -266,7 +293,40 @@ impl TryFrom<&Event> for Key { { let key_code = key_event.code; - output_key.value = Some(key_code.into()); + if output_key.shift { + // We need to deal have an edge case for shift, as the value will be in the + // shifted form (for example 'A'). If we left that, we would get "" as + // Key representation, which can't be parsed. So we simply unshift the 'A' + // here, turning the representation into: "" + if let KeyCode::Char(char) = key_code { + let lower_case_char: char = { + let chars = char.to_lowercase().collect::>(); + if chars.len() != 1 { + unimplemented!( + " + I have no idea how we handle this case, + we'll just have to hope, that it never comes up. + " + ); + } else { + *chars.first().expect("We checked the length") + } + }; + info!( + "Had to translate key ('{}') to it's lowercase variant ('{}')", + char, lower_case_char + ); + output_key.value = Some(KeyValue::Char(lower_case_char)); + } else { + debug!( + "Key ('{}') is shifted but not a character!", + Into::::into(key_code) + ); + output_key.value = Some(key_code.into()); + } + } else { + output_key.value = Some(key_code.into()); + } } Ok(output_key) diff --git a/src/app/keymappings/key/mod.rs b/src/app/keymappings/key/mod.rs index 97806e6..4a02938 100644 --- a/src/app/keymappings/key/mod.rs +++ b/src/app/keymappings/key/mod.rs @@ -45,6 +45,21 @@ mod test { ) } + #[test] + fn test_shift_a() { + let keys: Keys = "".parse().unwrap(); + assert_eq!( + keys, + Keys(vec![Key { + alt: false, + ctrl: false, + meta: false, + shift: true, + value: Some(KeyValue::Char('a')) + }]) + ) + } + #[test] fn test_string_repr() { let key = Key {