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 '<S-A>', 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.
This commit is contained in:
Benedikt Peetz 2023-10-18 22:52:07 +02:00
parent 66bf5e3b6f
commit b54f8e59e8
Signed by: bpeetz
GPG Key ID: A5E94010C3A642AD
3 changed files with 80 additions and 4 deletions

View File

@ -12,6 +12,7 @@ use crate::app::{
pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<EventStatus> { pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<EventStatus> {
async fn default(converted_key: Key, app: &mut App<'_>, old_state: &State) -> 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 // Just let the input event slip through if no keymap matches
app.tx app.tx
.send(Event::CommandEvent( .send(Event::CommandEvent(

View File

@ -1,7 +1,8 @@
use std::{str::FromStr, fmt::Display}; use std::{fmt::Display, str::FromStr};
use anyhow::{bail, Context}; 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}; use super::{Chars, KeyValue, Keys};
@ -236,7 +237,33 @@ impl Into<Event> for Key {
} }
let output = Event::Key(KeyEvent { 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::<Vec<char>>();
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, modifiers,
kind: KeyEventKind::Press, kind: KeyEventKind::Press,
state: KeyEventState::NONE, state: KeyEventState::NONE,
@ -266,7 +293,40 @@ impl TryFrom<&Event> for Key {
{ {
let key_code = key_event.code; 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 "<S-A>" as
// Key representation, which can't be parsed. So we simply unshift the 'A'
// here, turning the representation into: "<S-a>"
if let KeyCode::Char(char) = key_code {
let lower_case_char: char = {
let chars = char.to_lowercase().collect::<Vec<char>>();
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::<KeyValue>::into(key_code)
);
output_key.value = Some(key_code.into());
}
} else {
output_key.value = Some(key_code.into());
}
} }
Ok(output_key) Ok(output_key)

View File

@ -45,6 +45,21 @@ mod test {
) )
} }
#[test]
fn test_shift_a() {
let keys: Keys = "<S-a>".parse().unwrap();
assert_eq!(
keys,
Keys(vec![Key {
alt: false,
ctrl: false,
meta: false,
shift: true,
value: Some(KeyValue::Char('a'))
}])
)
}
#[test] #[test]
fn test_string_repr() { fn test_string_repr() {
let key = Key { let key = Key {