diff --git a/src/input.rs b/src/input.rs index 91c9e75e8..d5151addc 100644 --- a/src/input.rs +++ b/src/input.rs @@ -4,22 +4,19 @@ use crate::env::{Environment, CURSES_INITIALIZED}; use crate::event; use crate::flog::FLOG; use crate::input_common::{ - CharEvent, CharInputStyle, InputEventQueuer, ReadlineCmd, R_END_INPUT_FUNCTIONS, + CharEvent, CharInputStyle, InputData, InputEventQueuer, ReadlineCmd, R_END_INPUT_FUNCTIONS, }; use crate::key::{self, canonicalize_raw_escapes, ctrl, Key, Modifiers}; -use crate::parser::Parser; use crate::proc::job_reap; use crate::reader::{ reader_reading_interrupted, reader_reset_interrupted, reader_schedule_prompt_repaint, + ReaderData, }; use crate::signal::signal_clear_cancel; use crate::threads::assert_is_main_thread; use crate::wchar::prelude::*; use once_cell::sync::{Lazy, OnceCell}; -use std::collections::VecDeque; use std::ffi::CString; -use std::os::fd::RawFd; -use std::rc::Rc; use std::sync::{ atomic::{AtomicU32, Ordering}, Mutex, MutexGuard, @@ -46,11 +43,11 @@ pub enum KeyNameStyle { /// Struct representing a keybinding. Returned by input_get_mappings. #[derive(Debug, Clone)] -struct InputMapping { +pub struct InputMapping { /// Character sequence which generates this event. seq: Vec, /// Commands that should be evaluated by this mapping. - commands: Vec, + pub commands: Vec, /// We wish to preserve the user-specified order. This is just an incrementing value. specification_order: u32, /// Mode in which this command should be evaluated. @@ -393,37 +390,19 @@ pub fn init_input() { } } -pub struct Inputter { - in_fd: RawFd, - queue: VecDeque, - paste_buffer: Option>, - // We need a parser to evaluate bindings. - parser: Rc, - input_function_args: Vec, - function_status: bool, - - // Transient storage to avoid repeated allocations. - event_storage: Vec, -} - -impl InputEventQueuer for Inputter { - fn get_queue(&self) -> &VecDeque { - &self.queue +impl InputEventQueuer for ReaderData { + fn get_input_data(&self) -> &InputData { + &self.input_data } - fn get_queue_mut(&mut self) -> &mut VecDeque { - &mut self.queue - } - - /// Return the fd corresponding to stdin. - fn get_in_fd(&self) -> RawFd { - self.in_fd + fn get_input_data_mut(&mut self) -> &mut InputData { + &mut self.input_data } fn prepare_to_select(&mut self) { // Fire any pending events and reap stray processes, including printing exit status messages. - event::fire_delayed(&self.parser); - if job_reap(&self.parser, true) { + event::fire_delayed(self.parser()); + if job_reap(self.parser(), true) { reader_schedule_prompt_repaint(); } } @@ -434,7 +413,7 @@ impl InputEventQueuer for Inputter { signal_clear_cancel(); // Fire any pending events and reap stray processes, including printing exit status messages. - let parser = &self.parser; + let parser = self.parser(); event::fire_delayed(parser); if job_reap(parser, true) { reader_schedule_prompt_repaint(); @@ -452,19 +431,17 @@ impl InputEventQueuer for Inputter { } fn uvar_change_notified(&mut self) { - self.parser.sync_uvars_and_fire(true /* always */); + self.parser().sync_uvars_and_fire(true /* always */); } fn paste_start_buffering(&mut self) { - self.paste_buffer = Some(vec![]); + self.input_data.paste_buffer = Some(vec![]); self.push_front(CharEvent::from_readline(ReadlineCmd::BeginUndoGroup)); } - fn paste_is_buffering(&self) -> bool { - self.paste_buffer.is_some() - } + fn paste_commit(&mut self) { self.push_front(CharEvent::from_readline(ReadlineCmd::EndUndoGroup)); - let Some(buffer) = self.paste_buffer.take() else { + let Some(buffer) = self.input_data.paste_buffer.take() else { return; }; self.push_front(CharEvent::Command(sprintf!( @@ -472,116 +449,11 @@ impl InputEventQueuer for Inputter { escape(&str2wcstring(&buffer)) ))); } - fn paste_push_char(&mut self, b: u8) { - self.paste_buffer.as_mut().unwrap().push(b) - } -} - -impl Inputter { - /// Construct from a parser, and the fd from which to read. - pub fn new(parser: Rc, in_fd: RawFd) -> Inputter { - Inputter { - in_fd, - queue: VecDeque::new(), - paste_buffer: None, - parser, - input_function_args: Vec::new(), - function_status: false, - event_storage: Vec::new(), - } - } - - fn function_push_arg(&mut self, arg: char) { - self.input_function_args.push(arg); - } - - pub fn function_pop_arg(&mut self) -> Option { - self.input_function_args.pop() - } - - fn function_push_args(&mut self, code: ReadlineCmd) { - let arity = input_function_arity(code); - assert!( - self.event_storage.is_empty(), - "event_storage should be empty" - ); - let mut skipped = std::mem::take(&mut self.event_storage); - for _ in 0..arity { - // Skip and queue up any function codes. See issue #2357. - let arg: char; - loop { - let evt = self.readch(); - if let Some(kevt) = evt.get_key() { - if let Some(c) = kevt.key.codepoint_text() { - // TODO forward the whole key - arg = c; - break; - } - } - skipped.push(evt); - } - self.function_push_arg(arg); - } - - // Push the function codes back into the input stream. - self.insert_front(skipped.drain(..)); - self.event_storage = skipped; - self.event_storage.clear(); - } - - /// Perform the action of the specified binding. - fn mapping_execute(&mut self, m: &InputMapping) { - let has_command = m - .commands - .iter() - .any(|cmd| input_function_get_code(cmd).is_none()); - if has_command { - self.push_front(CharEvent::from_check_exit()); - } - for cmd in m.commands.iter().rev() { - let evt = match input_function_get_code(cmd) { - Some(code) => { - self.function_push_args(code); - // At this point, the sequence is only used for reinserting the keys into - // the event queue for self-insert. Modifiers make no sense here so drop them. - CharEvent::from_readline_seq( - code, - m.seq - .iter() - .filter(|key| key.modifiers.is_none()) - .map(|key| key.codepoint) - .collect(), - ) - } - None => CharEvent::Command(cmd.clone()), - }; - self.push_front(evt); - } - // Missing bind mode indicates to not reset the mode (#2871) - if let Some(mode) = m.sets_mode.as_ref() { - self.push_front(CharEvent::Command(sprintf!( - "set --global %s %s", - FISH_BIND_MODE_VAR, - escape(mode) - ))); - } - } - - /// Enqueue a char event to the queue of unread characters that input_readch will return before - /// actually reading from fd 0. - pub fn queue_char(&mut self, ch: CharEvent) { - self.queue.push_back(ch); - } - - /// Sets the return status of the most recently executed input function. - pub fn function_set_status(&mut self, status: bool) { - self.function_status = status; - } } /// A struct which allows accumulating input events, or returns them to the queue. /// This contains a list of events which have been dequeued, and a current index into that list. -struct EventQueuePeeker<'q> { +pub struct EventQueuePeeker<'q, Queuer: InputEventQueuer + ?Sized> { /// The list of events which have been dequeued. peeked: Vec, @@ -594,11 +466,11 @@ struct EventQueuePeeker<'q> { subidx: usize, /// The queue from which to read more events. - event_queue: &'q mut Inputter, + event_queue: &'q mut Queuer, } -impl EventQueuePeeker<'_> { - fn new(event_queue: &mut Inputter) -> EventQueuePeeker { +impl<'q, Queuer: InputEventQueuer + ?Sized> EventQueuePeeker<'q, Queuer> { + pub fn new(event_queue: &'q mut Queuer) -> Self { EventQueuePeeker { peeked: Vec::new(), had_timeout: false, @@ -745,59 +617,50 @@ impl EventQueuePeeker<'_> { } /// Reset our index back to 0. - fn restart(&mut self) { + pub fn restart(&mut self) { self.idx = 0; self.subidx = 0; } -} -impl Drop for EventQueuePeeker<'_> { - fn drop(&mut self) { + /// Return true if this `peeker` matches a given sequence of char events given by `str`. + fn try_peek_sequence(&mut self, style: &KeyNameStyle, seq: &[Key]) -> bool { assert!( - self.idx == 0 && self.subidx == 0, - "Events left on the queue - missing restart or consume?", + !seq.is_empty(), + "Empty sequence passed to try_peek_sequence" ); - self.event_queue.insert_front(self.peeked.drain(self.idx..)); - } -} - -/// Return true if a given `peeker` matches a given sequence of char events given by `str`. -fn try_peek_sequence(peeker: &mut EventQueuePeeker, style: &KeyNameStyle, seq: &[Key]) -> bool { - assert!( - !seq.is_empty(), - "Empty sequence passed to try_peek_sequence" - ); - let mut prev = Key::from_raw(key::Invalid); - for key in seq { - // If we just read an escape, we need to add a timeout for the next char, - // to distinguish between the actual escape key and an "alt"-modifier. - let escaped = *style != KeyNameStyle::Plain && prev == Key::from_raw(key::Escape); - if !peeker.next_is_char(style, *key, escaped) { + let mut prev = Key::from_raw(key::Invalid); + for key in seq { + // If we just read an escape, we need to add a timeout for the next char, + // to distinguish between the actual escape key and an "alt"-modifier. + let escaped = *style != KeyNameStyle::Plain && prev == Key::from_raw(key::Escape); + if !self.next_is_char(style, *key, escaped) { + return false; + } + prev = *key; + } + if self.subidx != 0 { + FLOG!( + reader, + "legacy binding matched prefix of key encoding but did not consume all of it" + ); return false; } - prev = *key; + true } - if peeker.subidx != 0 { - FLOG!( - reader, - "legacy binding matched prefix of key encoding but did not consume all of it" - ); - return false; - } - true -} -/// Return the first mapping that matches, walking first over the user's mapping list, then the -/// preset list. -/// Return none if nothing matches, or if we may have matched a longer sequence but it was -/// interrupted by a readline event. -impl Inputter { - fn find_mapping(vars: &dyn Environment, peeker: &mut EventQueuePeeker) -> Option { + /// Return the first mapping that matches from the given mapping set, walking first over the + /// user's mapping list, then the preset list. + /// Return none if nothing matches, or if we may have matched a longer sequence but it was + /// interrupted by a readline event. + pub fn find_mapping( + &mut self, + vars: &dyn Environment, + ip: &InputMappingSet, + ) -> Option { let mut generic: Option<&InputMapping> = None; let bind_mode = input_get_bind_mode(vars); let mut escape: Option<&InputMapping> = None; - let ip = input_mappings(); let ml = ip.mapping_list.iter().chain(ip.preset_mapping_list.iter()); for m in ml { if m.mode != bind_mode { @@ -813,7 +676,7 @@ impl Inputter { } // FLOG!(reader, "trying mapping", format!("{:?}", m)); - if try_peek_sequence(peeker, &m.key_name_style, &m.seq) { + if self.try_peek_sequence(&m.key_name_style, &m.seq) { // A binding for just escape should also be deferred // so escape sequences take precedence. if m.seq == vec![Key::from_raw(key::Escape)] { @@ -824,9 +687,9 @@ impl Inputter { return Some(m.clone()); } } - peeker.restart(); + self.restart(); } - if peeker.char_sequence_interrupted() { + if self.char_sequence_interrupted() { // We might have matched a longer sequence, but we were interrupted, e.g. by a signal. FLOG!(reader, "torn sequence, rearranging events"); return None; @@ -834,7 +697,7 @@ impl Inputter { if escape.is_some() { // We need to reconsume the escape. - peeker.next(); + self.next(); return escape.cloned(); } @@ -844,64 +707,21 @@ impl Inputter { None } } +} - fn mapping_execute_matching_or_generic(&mut self) { - let vars = self.parser.vars_ref(); - let mut peeker = EventQueuePeeker::new(self); - // Check for ordinary mappings. - if let Some(mapping) = Self::find_mapping(&*vars, &mut peeker) { - FLOG!( - reader, - format!("Found mapping {:?} from {:?}", &mapping, &peeker.peeked) - ); - peeker.consume(); - self.mapping_execute(&mapping); - return; - } - peeker.restart(); - - if peeker.char_sequence_interrupted() { - // This may happen if we received a signal in the middle of an escape sequence or other - // multi-char binding. Move these non-char events to the front of the queue, handle them - // first, and then later we'll return and try the sequence again. See #8628. - peeker.consume(); - self.promote_interruptions_to_front(); - return; - } - - FLOG!(reader, "no generic found, ignoring char..."); - let _ = peeker.next(); - peeker.consume(); - } - - /// Helper function. Picks through the queue of incoming characters until we get to one that's not a - /// readline function. - fn read_characters_no_readline(&mut self) -> CharEvent { +impl Drop for EventQueuePeeker<'_, Queue> { + fn drop(&mut self) { assert!( - self.event_storage.is_empty(), - "event_storage should be empty" + self.idx == 0 && self.subidx == 0, + "Events left on the queue - missing restart or consume?", ); - let mut saved_events = std::mem::take(&mut self.event_storage); - - let evt_to_return: CharEvent; - loop { - let evt = self.readch(); - if evt.is_readline_or_command() { - saved_events.push(evt); - } else { - evt_to_return = evt; - break; - } - } - - // Restore any readline functions - self.insert_front(saved_events.drain(..)); - self.event_storage = saved_events; - self.event_storage.clear(); - evt_to_return + self.event_queue.insert_front(self.peeked.drain(self.idx..)); } +} - /// Read a key from stdin. +/// Support for reading keys from the terminal for the Reader. +impl ReaderData { + /// Read a key from our fd. pub fn read_char(&mut self) -> CharEvent { // Clear the interrupted flag. reader_reset_interrupted(); @@ -930,8 +750,9 @@ impl Inputter { } ReadlineCmd::FuncAnd | ReadlineCmd::FuncOr => { // If previous function has bad status, skip all functions that follow us. - if (!self.function_status && readline_event.cmd == ReadlineCmd::FuncAnd) - || (self.function_status && readline_event.cmd == ReadlineCmd::FuncOr) + let fs = self.get_function_status(); + if (!fs && readline_event.cmd == ReadlineCmd::FuncAnd) + || (fs && readline_event.cmd == ReadlineCmd::FuncOr) { self.drop_leading_readline_events(); } @@ -969,6 +790,135 @@ impl Inputter { } } } + + fn mapping_execute_matching_or_generic(&mut self) { + let vars = self.parser().vars_ref(); + let mut peeker = EventQueuePeeker::new(self); + // Check for ordinary mappings. + let ip = input_mappings(); + if let Some(mapping) = peeker.find_mapping(&*vars, &ip) { + FLOG!( + reader, + format!("Found mapping {:?} from {:?}", &mapping, &peeker.peeked) + ); + peeker.consume(); + self.mapping_execute(&mapping); + return; + } + std::mem::drop(ip); + peeker.restart(); + + if peeker.char_sequence_interrupted() { + // This may happen if we received a signal in the middle of an escape sequence or other + // multi-char binding. Move these non-char events to the front of the queue, handle them + // first, and then later we'll return and try the sequence again. See #8628. + peeker.consume(); + self.promote_interruptions_to_front(); + return; + } + + FLOG!(reader, "no generic found, ignoring char..."); + let _ = peeker.next(); + peeker.consume(); + } + + /// Helper function. Picks through the queue of incoming characters until we get to one that's not a + /// readline function. + fn read_characters_no_readline(&mut self) -> CharEvent { + let mut saved_events = std::mem::take(&mut self.get_input_data_mut().event_storage); + assert!(saved_events.is_empty(), "event_storage should be empty"); + + let evt_to_return: CharEvent; + loop { + let evt = self.readch(); + if evt.is_readline_or_command() { + saved_events.push(evt); + } else { + evt_to_return = evt; + break; + } + } + + // Restore any readline functions + self.insert_front(saved_events.drain(..)); + saved_events.clear(); + self.get_input_data_mut().event_storage = saved_events; + evt_to_return + } + + /// Perform the action of the specified binding. + fn mapping_execute(&mut self, m: &InputMapping) { + let has_command = m + .commands + .iter() + .any(|cmd| input_function_get_code(cmd).is_none()); + if has_command { + self.push_front(CharEvent::from_check_exit()); + } + for cmd in m.commands.iter().rev() { + let evt = match input_function_get_code(cmd) { + Some(code) => { + self.function_push_args(code); + // At this point, the sequence is only used for reinserting the keys into + // the event queue for self-insert. Modifiers make no sense here so drop them. + CharEvent::from_readline_seq( + code, + m.seq + .iter() + .filter(|key| key.modifiers.is_none()) + .map(|key| key.codepoint) + .collect(), + ) + } + None => CharEvent::Command(cmd.clone()), + }; + self.push_front(evt); + } + // Missing bind mode indicates to not reset the mode (#2871) + if let Some(mode) = m.sets_mode.as_ref() { + self.push_front(CharEvent::Command(sprintf!( + "set --global %s %s", + FISH_BIND_MODE_VAR, + escape(mode) + ))); + } + } + + fn function_push_arg(&mut self, arg: char) { + self.get_input_data_mut().input_function_args.push(arg); + } + + pub fn function_pop_arg(&mut self) -> Option { + self.get_input_data_mut().input_function_args.pop() + } + + fn function_push_args(&mut self, code: ReadlineCmd) { + let arity = input_function_arity(code); + let mut skipped = std::mem::take(&mut self.get_input_data_mut().event_storage); + assert!(skipped.is_empty(), "event_storage should be empty"); + + for _ in 0..arity { + // Skip and queue up any function codes. See issue #2357. + let arg: char; + loop { + let evt = self.readch(); + if let Some(kevt) = evt.get_key() { + if let Some(c) = kevt.key.codepoint_text() { + // TODO forward the whole key + arg = c; + break; + } + } + skipped.push(evt); + } + self.function_push_arg(arg); + } + + // Push the function codes back into the input stream. + self.insert_front(skipped.drain(..)); + skipped.clear(); + self.get_input_data_mut().event_storage = skipped; + } } impl InputMappingSet { diff --git a/src/input_common.rs b/src/input_common.rs index a97cdbeb8..7fbaa6f47 100644 --- a/src/input_common.rs +++ b/src/input_common.rs @@ -508,12 +508,58 @@ fn parse_mask(mask: u32) -> Modifiers { } } +// A data type used by the input machinery. +pub struct InputData { + // The file descriptor from which we read input, often stdin. + pub in_fd: RawFd, + + // Queue of unread characters. + pub queue: VecDeque, + + // The current paste buffer, if any. + pub paste_buffer: Option>, + + // The arguments to the most recently invoked input function. + pub input_function_args: Vec, + + // The return status of the most recently invoked input function. + pub function_status: bool, + + // Transient storage to avoid repeated allocations. + pub event_storage: Vec, +} + +impl InputData { + /// Construct from the fd from which to read. + pub fn new(in_fd: RawFd) -> Self { + Self { + in_fd, + queue: VecDeque::new(), + paste_buffer: None, + input_function_args: Vec::new(), + function_status: false, + event_storage: Vec::new(), + } + } + + /// Enqueue a char event to the queue of unread characters that input_readch will return before + /// actually reading from fd 0. + pub fn queue_char(&mut self, ch: CharEvent) { + self.queue.push_back(ch); + } + + /// Sets the return status of the most recently executed input function. + pub fn function_set_status(&mut self, status: bool) { + self.function_status = status; + } +} + /// A trait which knows how to produce a stream of input events. /// Note this is conceptually a "base class" with override points. pub trait InputEventQueuer { /// Return the next event in the queue, or none if the queue is empty. fn try_pop(&mut self) -> Option { - self.get_queue_mut().pop_front() + self.get_input_data_mut().queue.pop_front() } /// Function used by input_readch to read bytes from stdin until enough bytes have been read to @@ -1053,12 +1099,14 @@ pub trait InputEventQueuer { None } - /// Return our queue. These are "abstract" methods to be implemented by concrete types. - fn get_queue(&self) -> &VecDeque; - fn get_queue_mut(&mut self) -> &mut VecDeque; + /// Return the fd from which to read. + fn get_in_fd(&self) -> RawFd { + self.get_input_data().in_fd + } - /// Return the fd corresponding to stdin. - fn get_in_fd(&self) -> RawFd; + /// Return the input data. This is to be implemented by the concrete type. + fn get_input_data(&self) -> &InputData; + fn get_input_data_mut(&mut self) -> &mut InputData; // Support for "bracketed paste" // The way it works is that we acknowledge our support by printing @@ -1073,28 +1121,44 @@ pub trait InputEventQueuer { // (though it only supports it since then, it seems to be the last term to gain support). // // See http://thejh.net/misc/website-terminal-copy-paste. - fn paste_start_buffering(&mut self); - fn paste_is_buffering(&self) -> bool; - fn paste_push_char(&mut self, _b: u8) {} - fn paste_commit(&mut self); + + fn paste_start_buffering(&mut self) { + self.get_input_data_mut().paste_buffer = Some(Vec::new()); + } + + fn paste_is_buffering(&self) -> bool { + self.get_input_data().paste_buffer.is_some() + } + + fn paste_push_char(&mut self, b: u8) { + self.get_input_data_mut() + .paste_buffer + .as_mut() + .unwrap() + .push(b) + } + + fn paste_commit(&mut self) { + self.get_input_data_mut().paste_buffer = None; + } /// Enqueue a character or a readline function to the queue of unread characters that /// readch will return before actually reading from fd 0. fn push_back(&mut self, ch: CharEvent) { - self.get_queue_mut().push_back(ch); + self.get_input_data_mut().queue.push_back(ch); } /// Add a character or a readline function to the front of the queue of unread characters. This /// will be the next character returned by readch. fn push_front(&mut self, ch: CharEvent) { - self.get_queue_mut().push_front(ch); + self.get_input_data_mut().queue.push_front(ch); } /// Find the first sequence of non-char events, and promote them to the front. fn promote_interruptions_to_front(&mut self) { // Find the first sequence of non-char events. // EOF is considered a char: we don't want to pull EOF in front of real chars. - let queue = self.get_queue_mut(); + let queue = &mut self.get_input_data_mut().queue; let is_char = |evt: &CharEvent| evt.is_char() || evt.is_eof(); // Find the index of the first non-char event. // If there's none, we're done. @@ -1120,7 +1184,7 @@ pub trait InputEventQueuer { I: IntoIterator, I::IntoIter: DoubleEndedIterator, { - let queue = self.get_queue_mut(); + let queue = &mut self.get_input_data_mut().queue; let iter = evts.into_iter().rev(); queue.reserve(iter.size_hint().0); for evt in iter { @@ -1130,7 +1194,7 @@ pub trait InputEventQueuer { /// Forget all enqueued readline events in the front of the queue. fn drop_leading_readline_events(&mut self) { - let queue = self.get_queue_mut(); + let queue = &mut self.get_input_data_mut().queue; while let Some(evt) = queue.front() { if evt.is_readline_or_command() { queue.pop_front(); @@ -1145,46 +1209,43 @@ pub trait InputEventQueuer { fn prepare_to_select(&mut self) {} /// Called when select() is interrupted by a signal. - fn select_interrupted(&mut self); + fn select_interrupted(&mut self) {} /// Override point for when when select() is interrupted by the universal variable notifier. /// The default does nothing. fn uvar_change_notified(&mut self) {} + /// Reset the function status. + fn get_function_status(&self) -> bool { + self.get_input_data().function_status + } + /// Return if we have any lookahead. fn has_lookahead(&self) -> bool { - !self.get_queue().is_empty() + !self.get_input_data().queue.is_empty() } } /// A simple, concrete implementation of InputEventQueuer. pub struct InputEventQueue { - queue: VecDeque, - in_fd: RawFd, - is_in_bracketed_paste: bool, + data: InputData, } impl InputEventQueue { pub fn new(in_fd: RawFd) -> InputEventQueue { InputEventQueue { - queue: VecDeque::new(), - in_fd, - is_in_bracketed_paste: false, + data: InputData::new(in_fd), } } } impl InputEventQueuer for InputEventQueue { - fn get_queue(&self) -> &VecDeque { - &self.queue + fn get_input_data(&self) -> &InputData { + &self.data } - fn get_queue_mut(&mut self) -> &mut VecDeque { - &mut self.queue - } - - fn get_in_fd(&self) -> RawFd { - self.in_fd + fn get_input_data_mut(&mut self) -> &mut InputData { + &mut self.data } fn select_interrupted(&mut self) { @@ -1195,14 +1256,4 @@ impl InputEventQueuer for InputEventQueue { } } } - - fn paste_start_buffering(&mut self) { - self.is_in_bracketed_paste = true; - } - fn paste_is_buffering(&self) -> bool { - self.is_in_bracketed_paste - } - fn paste_commit(&mut self) { - self.is_in_bracketed_paste = false; - } } diff --git a/src/reader.rs b/src/reader.rs index 36b8fa305..143e1cc65 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -70,10 +70,10 @@ use crate::history::{ SearchType, }; use crate::input::init_input; -use crate::input::Inputter; -use crate::input_common::terminal_protocols_disable_ifn; -use crate::input_common::IS_TMUX; -use crate::input_common::{terminal_protocols_enable_ifn, CharEvent, CharInputStyle, ReadlineCmd}; +use crate::input_common::{ + terminal_protocols_disable_ifn, terminal_protocols_enable_ifn, CharEvent, CharInputStyle, + InputData, ReadlineCmd, IS_TMUX, +}; use crate::io::IoChain; use crate::kill::{kill_add, kill_replace, kill_yank, kill_yank_rotate}; use crate::libc::MB_CUR_MAX; @@ -496,8 +496,9 @@ pub struct ReaderData { /// The representation of the current screen contents. screen: Screen, - /// The source of input events. - inputter: Inputter, + /// Data associated with input events. + /// This is made public so that InputEventQueuer can be implemented on us. + pub input_data: InputData, queued_repaint: bool, /// The history. history: Arc, @@ -880,7 +881,7 @@ pub fn reader_set_autosuggestion_enabled(vars: &dyn Environment) { if data.conf.autosuggest_ok != enable { data.conf.autosuggest_ok = enable; data.force_exec_prompt_and_repaint = true; - data.inputter + data.input_data .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); } } @@ -894,7 +895,7 @@ pub fn reader_schedule_prompt_repaint() { }; if !data.force_exec_prompt_and_repaint { data.force_exec_prompt_and_repaint = true; - data.inputter + data.input_data .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); } } @@ -914,7 +915,7 @@ pub fn reader_execute_readline_cmd(ch: CharEvent) { data.queued_repaint = true; } if data.queued_repaint { - data.inputter.queue_char(ch); + data.input_data.queue_char(ch); return; } if data.rls.is_none() { @@ -1074,7 +1075,7 @@ fn reader_received_sighup() -> bool { impl ReaderData { fn new(parser: ParserRef, history: Arc, conf: ReaderConfig) -> Pin> { - let inputter = Inputter::new(parser.clone(), conf.inputfd); + let input_data = InputData::new(conf.inputfd); Pin::new(Box::new(Self { canary: Rc::new(()), parser_ref: parser, @@ -1090,7 +1091,7 @@ impl ReaderData { first_prompt: true, last_flash: Default::default(), screen: Screen::new(), - inputter, + input_data, queued_repaint: false, history, history_search: Default::default(), @@ -1162,7 +1163,7 @@ impl ReaderData { } /// Access the parser. - fn parser(&self) -> &Parser { + pub fn parser(&self) -> &Parser { &self.parser_ref } @@ -1965,7 +1966,7 @@ impl ReaderData { while accumulated_chars.len() < limit { terminal_protocols_enable_ifn(); - let evt = self.inputter.read_char(); + let evt = self.read_char(); let CharEvent::Key(kevt) = &evt else { event_needing_handling = Some(evt); break; @@ -2607,14 +2608,14 @@ impl ReaderData { } else { self.autosuggestion.clear(); } - self.inputter.function_set_status(true); + self.input_data.function_set_status(true); return; } if !self.history_pager_active { - self.inputter.function_set_status(false); + self.input_data.function_set_status(false); return; } - self.inputter.function_set_status(true); + self.input_data.function_set_status(true); if let Some(completion) = self.pager.selected_completion(&self.current_page_rendering) { @@ -2708,7 +2709,7 @@ impl ReaderData { if c == rl::PrevdOrBackwardWord && self.command_line.is_empty() { self.eval_bind_cmd(L!("prevd")); self.force_exec_prompt_and_repaint = true; - self.inputter + self.input_data .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); return; } @@ -2730,7 +2731,7 @@ impl ReaderData { if c == rl::NextdOrForwardWord && self.command_line.is_empty() { self.eval_bind_cmd(L!("nextd")); self.force_exec_prompt_and_repaint = true; - self.inputter + self.input_data .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); return; } @@ -2840,7 +2841,7 @@ impl ReaderData { let success = !self.autosuggestion.is_empty(); self.autosuggestion.clear(); // Return true if we had a suggestion to clear. - self.inputter.function_set_status(success); + self.input_data.function_set_status(success); } rl::AcceptAutosuggestion => { self.accept_autosuggestion(true, false, MoveWordStyle::Punctuation); @@ -3073,10 +3074,10 @@ impl ReaderData { _ => unreachable!(), }; let (elt, _el) = self.active_edit_line(); - if let Some(target) = self.inputter.function_pop_arg() { + if let Some(target) = self.function_pop_arg() { let success = self.jump(direction, precision, elt, target); - self.inputter.function_set_status(success); + self.input_data.function_set_status(success); } } rl::RepeatJump => { @@ -3092,7 +3093,7 @@ impl ReaderData { ); } - self.inputter.function_set_status(success); + self.input_data.function_set_status(success); } rl::ReverseRepeatJump => { let (elt, _el) = self.active_edit_line(); @@ -3111,13 +3112,13 @@ impl ReaderData { self.last_jump_direction = original_dir; - self.inputter.function_set_status(success); + self.input_data.function_set_status(success); } rl::ExpandAbbr => { if self.expand_abbreviation_at_cursor(1) { - self.inputter.function_set_status(true); + self.input_data.function_set_status(true); } else { - self.inputter.function_set_status(false); + self.input_data.function_set_status(false); } } rl::Undo | rl::Redo => { diff --git a/src/tests/input.rs b/src/tests/input.rs index e23972f72..a4caa4265 100644 --- a/src/tests/input.rs +++ b/src/tests/input.rs @@ -1,18 +1,29 @@ -use crate::input::{input_mappings, Inputter, KeyNameStyle, DEFAULT_BIND_MODE}; -use crate::input_common::{CharEvent, ReadlineCmd}; +use crate::env::EnvStack; +use crate::input::{EventQueuePeeker, InputMappingSet, KeyNameStyle, DEFAULT_BIND_MODE}; +use crate::input_common::{CharEvent, InputData, InputEventQueuer}; use crate::key::Key; -use crate::parser::Parser; -use crate::tests::prelude::*; use crate::wchar::prelude::*; use std::rc::Rc; +struct TestInputEventQueuer { + input_data: InputData, +} + +impl InputEventQueuer for TestInputEventQueuer { + fn get_input_data(&self) -> &InputData { + &self.input_data + } + fn get_input_data_mut(&mut self) -> &mut InputData { + &mut self.input_data + } +} + #[test] -#[serial] fn test_input() { - let _cleanup = test_init(); - use crate::env::EnvStack; - let parser = Parser::new(Rc::new(EnvStack::new()), false); - let mut input = Inputter::new(parser, libc::STDIN_FILENO); + let vars = Rc::new(EnvStack::new()); + let mut input = TestInputEventQueuer { + input_data: InputData::new(i32::MAX), // value doesn't matter since we don't read from it + }; // Ensure sequences are order independent. Here we add two bindings where the first is a prefix // of the second, and then emit the second key list. The second binding should be invoked, not // the first! @@ -22,36 +33,32 @@ fn test_input() { let default_mode = || DEFAULT_BIND_MODE.to_owned(); - { - let mut input_mapping = input_mappings(); - input_mapping.add1( - prefix_binding, - KeyNameStyle::Plain, - WString::from_str("up-line"), - default_mode(), - None, - true, - ); - input_mapping.add1( - desired_binding.clone(), - KeyNameStyle::Plain, - WString::from_str("down-line"), - default_mode(), - None, - true, - ); - } + let mut input_mappings = InputMappingSet::default(); + input_mappings.add1( + prefix_binding, + KeyNameStyle::Plain, + WString::from_str("up-line"), + default_mode(), + None, + true, + ); + input_mappings.add1( + desired_binding.clone(), + KeyNameStyle::Plain, + WString::from_str("down-line"), + default_mode(), + None, + true, + ); // Push the desired binding to the queue. for c in desired_binding { - input.queue_char(CharEvent::from_key(c)); + input.input_data.queue_char(CharEvent::from_key(c)); } - // Now test. - let evt = input.read_char(); - if !evt.is_readline() { - panic!("Event is not a readline"); - } else if evt.get_readline() != ReadlineCmd::DownLine { - panic!("Expected to read char down_line"); - } + let mut peeker = EventQueuePeeker::new(&mut input); + let mapping = peeker.find_mapping(&*vars, &input_mappings); + assert!(mapping.is_some()); + assert!(mapping.unwrap().commands == ["down-line"]); + peeker.restart(); }