diff --git a/src/builtins/fish_key_reader.rs b/src/builtins/fish_key_reader.rs index e0ac918f5..2cb5ec98e 100644 --- a/src/builtins/fish_key_reader.rs +++ b/src/builtins/fish_key_reader.rs @@ -20,7 +20,7 @@ input_common::{ kitty_progressive_enhancements_query, terminal_protocol_hacks, terminal_protocols_enable_ifn, Capability, CharEvent, ImplicitEvent, InputEventQueue, - InputEventQueuer, KITTY_KEYBOARD_SUPPORTED, + InputEventQueuer, KeyEvent, KITTY_KEYBOARD_SUPPORTED, }, key::{char_to_symbol, Key}, nix::isatty, @@ -38,7 +38,7 @@ use super::prelude::*; /// Return true if the recent sequence of characters indicates the user wants to exit the program. -fn should_exit(streams: &mut IoStreams, recent_keys: &mut Vec, key: Key) -> bool { +fn should_exit(streams: &mut IoStreams, recent_keys: &mut Vec, key: KeyEvent) -> bool { recent_keys.push(key); for evt in [VINTR, VEOF] { @@ -46,7 +46,12 @@ fn should_exit(streams: &mut IoStreams, recent_keys: &mut Vec, key: Key) -> let cc = Key::from_single_byte(modes.c_cc[evt]); if key == cc { - if recent_keys.iter().rev().nth(1) == Some(&cc) { + if recent_keys + .iter() + .rev() + .nth(1) + .is_some_and(|&prev| prev == cc) + { return true; } streams.err.append(wgettext_fmt!( diff --git a/src/input_common.rs b/src/input_common.rs index d23249235..87913df57 100644 --- a/src/input_common.rs +++ b/src/input_common.rs @@ -142,11 +142,53 @@ pub enum ReadlineCmd { ReverseRepeatJump, } +#[derive(Clone, Copy, Debug)] +pub struct KeyEvent { + pub key: Key, +} + +impl KeyEvent { + pub(crate) fn new(modifiers: Modifiers, codepoint: char) -> Self { + Self::from(Key::new(modifiers, codepoint)) + } + pub(crate) fn from_raw(codepoint: char) -> Self { + Self::from(Key::from_raw(codepoint)) + } + pub fn from_single_byte(c: u8) -> Self { + Self::from(Key::from_single_byte(c)) + } +} + +impl From for KeyEvent { + fn from(key: Key) -> Self { + Self { key } + } +} + +impl std::ops::Deref for KeyEvent { + type Target = Key; + fn deref(&self) -> &Self::Target { + &self.key + } +} + +impl std::ops::DerefMut for KeyEvent { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.key + } +} + +impl PartialEq for KeyEvent { + fn eq(&self, key: &Key) -> bool { + &self.key == key + } +} + /// Represents an event on the character input stream. #[derive(Debug, Clone)] pub enum CharEventType { /// A character was entered. - Char(Key), + Char(KeyInputEvent), /// A readline event. Readline(ReadlineCmd), @@ -172,9 +214,9 @@ pub struct ReadlineCmdEvent { } #[derive(Debug, Clone)] -pub struct KeyEvent { +pub struct KeyInputEvent { // The key. - pub key: Key, + pub key: KeyEvent, // The style to use when inserting characters into the command line. pub input_style: CharInputStyle, /// The sequence of characters in the input mapping which generated this event. @@ -207,7 +249,7 @@ pub enum ImplicitEvent { #[derive(Debug, Clone)] pub enum CharEvent { /// A character was entered. - Key(KeyEvent), + Key(KeyInputEvent), /// A readline event. Readline(ReadlineCmdEvent), @@ -240,7 +282,7 @@ pub fn get_char(&self) -> char { kevt.key.codepoint } - pub fn get_key(&self) -> Option<&KeyEvent> { + pub fn get_key(&self) -> Option<&KeyInputEvent> { match self { CharEvent::Key(kevt) => Some(kevt), _ => None, @@ -262,15 +304,15 @@ pub fn get_command(&self) -> Option<&wstr> { } pub fn from_char(c: char) -> CharEvent { - Self::from_key(Key::from_raw(c)) + Self::from_key(KeyEvent::from_raw(c)) } - pub fn from_key(key: Key) -> CharEvent { + pub fn from_key(key: KeyEvent) -> CharEvent { Self::from_key_seq(key, WString::new()) } - pub fn from_key_seq(key: Key, seq: WString) -> CharEvent { - CharEvent::Key(KeyEvent { + pub fn from_key_seq(key: KeyEvent, seq: WString) -> CharEvent { + CharEvent::Key(KeyInputEvent { key, input_style: CharInputStyle::Normal, seq, @@ -726,7 +768,7 @@ fn try_readch(&mut self, blocking: bool) -> Option { let key_with_escape = if read_byte == 0x1b { self.parse_escape_sequence(&mut buffer, &mut have_escape_prefix) } else { - canonicalize_control_char(read_byte) + canonicalize_control_char(read_byte).map(KeyEvent::from) }; if self.paste_is_buffering() { if read_byte != 0x1b { @@ -736,7 +778,7 @@ fn try_readch(&mut self, blocking: bool) -> Option { } let mut seq = WString::new(); let mut key = key_with_escape; - if key == Some(Key::from_raw(key::Invalid)) { + if key.is_some_and(|key| key == Key::from_raw(key::Invalid)) { continue; } assert!(key.map_or(true, |key| key.codepoint != key::Invalid)); @@ -780,7 +822,7 @@ fn try_readch(&mut self, blocking: bool) -> Option { continue; }; ( - CharEvent::from_key_seq(Key::from_raw(c), seq.clone()), + CharEvent::from_key_seq(KeyEvent::from_raw(c), seq.clone()), Some(seq.chars().skip(1).map(CharEvent::from_char)), ) }; @@ -797,7 +839,8 @@ fn try_readch(&mut self, blocking: bool) -> Option { } }); let vintr = shell_modes().c_cc[libc::VINTR]; - if vintr != 0 && key == Some(Key::from_single_byte(vintr)) { + if vintr != 0 && key.is_some_and(|key| key == Key::from_single_byte(vintr)) + { FLOG!( reader, "Received interrupt key, giving up waiting for response from terminal" @@ -827,22 +870,22 @@ fn parse_escape_sequence( &mut self, buffer: &mut Vec, have_escape_prefix: &mut bool, - ) -> Option { + ) -> Option { assert!(buffer.len() <= 2); let recursive_invocation = buffer.len() == 2; let Some(next) = self.try_readb(buffer) else { if !self.paste_is_buffering() { - return Some(Key::from_raw(key::Escape)); + return Some(KeyEvent::from_raw(key::Escape)); } return None; }; - let invalid = Key::from_raw(key::Invalid); + let invalid = KeyEvent::from_raw(key::Invalid); if recursive_invocation && next == b'\x1b' { return Some( match self.parse_escape_sequence(buffer, have_escape_prefix) { Some(mut nested_sequence) => { - if nested_sequence == invalid { - return Some(Key::from_raw(key::Escape)); + if nested_sequence == invalid.key { + return Some(KeyEvent::from_raw(key::Escape)); } nested_sequence.modifiers.alt = true; nested_sequence @@ -866,7 +909,7 @@ fn parse_escape_sequence( match canonicalize_control_char(next) { Some(mut key) => { key.modifiers.alt = true; - Some(key) + Some(KeyEvent::from(key)) } None => { *have_escape_prefix = true; @@ -878,7 +921,7 @@ fn parse_escape_sequence( fn parse_codepoint( &mut self, state: &mut mbstate_t, - out_key: &mut Option, + out_key: &mut Option, out_seq: &mut WString, buffer: &[u8], i: usize, @@ -928,7 +971,7 @@ fn parse_codepoint( if !fish_reserved_codepoint(res) { if *have_escape_prefix && i != 0 { *have_escape_prefix = false; - *out_key = Some(alt(res)); + *out_key = Some(KeyEvent::from(alt(res))); } *consumed += 1; out_seq.push(res); @@ -942,11 +985,11 @@ fn parse_codepoint( ControlFlow::Continue(true) } - fn parse_csi(&mut self, buffer: &mut Vec) -> Option { + fn parse_csi(&mut self, buffer: &mut Vec) -> Option { // The maximum number of CSI parameters is defined by NPAR, nominally 16. let mut params = [[0_u32; 4]; 16]; let Some(mut c) = self.try_readb(buffer) else { - return Some(ctrl('[')); + return Some(KeyEvent::from(ctrl('['))); }; let mut next_char = |zelf: &mut Self| zelf.try_readb(buffer).unwrap_or(0xff); let private_mode; @@ -993,10 +1036,7 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { codepoint = shifted_codepoint; } } - Key { - modifiers, - codepoint, - } + KeyEvent::new(modifiers, codepoint) }; let key = match c { @@ -1013,9 +1053,9 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { return None; } match params[0][0] { - 23 | 24 => shift( + 23 | 24 => KeyEvent::from(shift( char::from_u32(u32::from(function_key(11)) + params[0][0] - 23).unwrap(), // rxvt style - ), + )), _ => return None, } } @@ -1154,23 +1194,23 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { char::from_u32(u32::from(function_key(11)) + params[0][0] - 23).unwrap(), None, ), - 25 | 26 => { - shift(char::from_u32(u32::from(function_key(3)) + params[0][0] - 25).unwrap()) - } // rxvt style + 25 | 26 => KeyEvent::from(shift( + char::from_u32(u32::from(function_key(3)) + params[0][0] - 25).unwrap(), + )), // rxvt style 27 => { let key = canonicalize_keyed_control_char(char::from_u32(params[2][0]).unwrap()); masked_key(key, None) } - 28 | 29 => { - shift(char::from_u32(u32::from(function_key(5)) + params[0][0] - 28).unwrap()) - } // rxvt style - 31 | 32 => { - shift(char::from_u32(u32::from(function_key(7)) + params[0][0] - 31).unwrap()) - } // rxvt style - 33 | 34 => { - shift(char::from_u32(u32::from(function_key(9)) + params[0][0] - 33).unwrap()) - } // rxvt style + 28 | 29 => KeyEvent::from(shift( + char::from_u32(u32::from(function_key(5)) + params[0][0] - 28).unwrap(), + )), // rxvt style + 31 | 32 => KeyEvent::from(shift( + char::from_u32(u32::from(function_key(7)) + params[0][0] - 31).unwrap(), + )), // rxvt style + 33 | 34 => KeyEvent::from(shift( + char::from_u32(u32::from(function_key(9)) + params[0][0] - 33).unwrap(), + )), // rxvt style 200 => { self.paste_start_buffering(); return None; @@ -1235,7 +1275,7 @@ fn parse_csi(&mut self, buffer: &mut Vec) -> Option { )), ) } - b'Z' => shift(key::Tab), + b'Z' => KeyEvent::from(shift(key::Tab)), b'I' => { self.push_front(CharEvent::Implicit(ImplicitEvent::FocusIn)); return None; @@ -1263,10 +1303,10 @@ fn disable_mouse_tracking(&mut self) { self.push_front(CharEvent::Implicit(ImplicitEvent::DisableMouseTracking)); } - fn parse_ss3(&mut self, buffer: &mut Vec) -> Option { + fn parse_ss3(&mut self, buffer: &mut Vec) -> Option { let mut raw_mask = 0; let Some(mut code) = self.try_readb(buffer) else { - return Some(alt('O')); + return Some(KeyEvent::from(alt('O'))); }; while (b'0'..=b'9').contains(&code) { raw_mask = raw_mask * 10 + u32::from(code - b'0'); @@ -1275,36 +1315,36 @@ fn parse_ss3(&mut self, buffer: &mut Vec) -> Option { let modifiers = parse_mask(raw_mask.saturating_sub(1)); #[rustfmt::skip] let key = match code { - b' ' => Key::new(modifiers, key::Space), - b'A' => Key::new(modifiers, key::Up), - b'B' => Key::new(modifiers, key::Down), - b'C' => Key::new(modifiers, key::Right), - b'D' => Key::new(modifiers, key::Left), - b'F' => Key::new(modifiers, key::End), - b'H' => Key::new(modifiers, key::Home), - b'I' => Key::new(modifiers, key::Tab), - b'M' => Key::new(modifiers, key::Enter), - b'P' => Key::new(modifiers, function_key(1)), - b'Q' => Key::new(modifiers, function_key(2)), - b'R' => Key::new(modifiers, function_key(3)), - b'S' => Key::new(modifiers, function_key(4)), - b'X' => Key::new(modifiers, '='), - b'j' => Key::new(modifiers, '*'), - b'k' => Key::new(modifiers, '+'), - b'l' => Key::new(modifiers, ','), - b'm' => Key::new(modifiers, '-'), - b'n' => Key::new(modifiers, '.'), - b'o' => Key::new(modifiers, '/'), - b'p' => Key::new(modifiers, '0'), - b'q' => Key::new(modifiers, '1'), - b'r' => Key::new(modifiers, '2'), - b's' => Key::new(modifiers, '3'), - b't' => Key::new(modifiers, '4'), - b'u' => Key::new(modifiers, '5'), - b'v' => Key::new(modifiers, '6'), - b'w' => Key::new(modifiers, '7'), - b'x' => Key::new(modifiers, '8'), - b'y' => Key::new(modifiers, '9'), + b' ' => KeyEvent::new(modifiers, key::Space), + b'A' => KeyEvent::new(modifiers, key::Up), + b'B' => KeyEvent::new(modifiers, key::Down), + b'C' => KeyEvent::new(modifiers, key::Right), + b'D' => KeyEvent::new(modifiers, key::Left), + b'F' => KeyEvent::new(modifiers, key::End), + b'H' => KeyEvent::new(modifiers, key::Home), + b'I' => KeyEvent::new(modifiers, key::Tab), + b'M' => KeyEvent::new(modifiers, key::Enter), + b'P' => KeyEvent::new(modifiers, function_key(1)), + b'Q' => KeyEvent::new(modifiers, function_key(2)), + b'R' => KeyEvent::new(modifiers, function_key(3)), + b'S' => KeyEvent::new(modifiers, function_key(4)), + b'X' => KeyEvent::new(modifiers, '='), + b'j' => KeyEvent::new(modifiers, '*'), + b'k' => KeyEvent::new(modifiers, '+'), + b'l' => KeyEvent::new(modifiers, ','), + b'm' => KeyEvent::new(modifiers, '-'), + b'n' => KeyEvent::new(modifiers, '.'), + b'o' => KeyEvent::new(modifiers, '/'), + b'p' => KeyEvent::new(modifiers, '0'), + b'q' => KeyEvent::new(modifiers, '1'), + b'r' => KeyEvent::new(modifiers, '2'), + b's' => KeyEvent::new(modifiers, '3'), + b't' => KeyEvent::new(modifiers, '4'), + b'u' => KeyEvent::new(modifiers, '5'), + b'v' => KeyEvent::new(modifiers, '6'), + b'w' => KeyEvent::new(modifiers, '7'), + b'x' => KeyEvent::new(modifiers, '8'), + b'y' => KeyEvent::new(modifiers, '9'), _ => return None, }; Some(key) @@ -1334,10 +1374,10 @@ fn parse_xtversion(&mut self, buffer: &mut Vec) { ); } - fn parse_dcs(&mut self, buffer: &mut Vec) -> Option { + fn parse_dcs(&mut self, buffer: &mut Vec) -> Option { assert!(buffer.len() == 2); let Some(success) = self.try_readb(buffer) else { - return Some(alt('P')); + return Some(KeyEvent::from(alt('P'))); }; let success = match success { b'0' => false, @@ -1589,7 +1629,7 @@ fn select_interrupted(&mut self) {} fn enqueue_interrupt_key(&mut self) { let vintr = shell_modes().c_cc[libc::VINTR]; if vintr != 0 { - let interrupt_evt = CharEvent::from_key(Key::from_single_byte(vintr)); + let interrupt_evt = CharEvent::from_key(KeyEvent::from_single_byte(vintr)); if unblock_input(self.blocking_wait()) { FLOG!( reader, @@ -1629,7 +1669,7 @@ pub(crate) fn unblock_input(mut wait_guard: MutexGuard>) -> true } -fn invalid_sequence(buffer: &[u8]) -> Option { +fn invalid_sequence(buffer: &[u8]) -> Option { FLOG!( reader, "Error: invalid escape sequence: ", diff --git a/src/tests/input.rs b/src/tests/input.rs index a4caa4265..27860274c 100644 --- a/src/tests/input.rs +++ b/src/tests/input.rs @@ -1,6 +1,6 @@ use crate::env::EnvStack; use crate::input::{EventQueuePeeker, InputMappingSet, KeyNameStyle, DEFAULT_BIND_MODE}; -use crate::input_common::{CharEvent, InputData, InputEventQueuer}; +use crate::input_common::{CharEvent, InputData, InputEventQueuer, KeyEvent}; use crate::key::Key; use crate::wchar::prelude::*; use std::rc::Rc; @@ -52,8 +52,10 @@ fn test_input() { ); // Push the desired binding to the queue. - for c in desired_binding { - input.input_data.queue_char(CharEvent::from_key(c)); + for key in desired_binding { + input + .input_data + .queue_char(CharEvent::from_key(KeyEvent::from(key))); } let mut peeker = EventQueuePeeker::new(&mut input);