Migrate the Inputter type to a trait

This is a start on untangling input. Prior to this, a ReaderData and an
Inputter would communicate with each other; this is natural in C++ but
difficult in Rust because the Reader would own an Inputter and therefore
the Inputter could not easily reference the Reader. This was previously
"resolved" via unsafe code.

Fix this by collapsing Inputter into Reader. Now they're the same object!
Migrate Inputter's logic into a trait, so we get some modularity, and then
directly implement the remaining input methods on ReaderData.
This commit is contained in:
ridiculousfish 2024-05-26 16:21:11 -07:00 committed by Peter Ammon
parent c9a76bd634
commit c297df38c7
No known key found for this signature in database
4 changed files with 357 additions and 348 deletions

View file

@ -4,22 +4,19 @@ use crate::env::{Environment, CURSES_INITIALIZED};
use crate::event; use crate::event;
use crate::flog::FLOG; use crate::flog::FLOG;
use crate::input_common::{ 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::key::{self, canonicalize_raw_escapes, ctrl, Key, Modifiers};
use crate::parser::Parser;
use crate::proc::job_reap; use crate::proc::job_reap;
use crate::reader::{ use crate::reader::{
reader_reading_interrupted, reader_reset_interrupted, reader_schedule_prompt_repaint, reader_reading_interrupted, reader_reset_interrupted, reader_schedule_prompt_repaint,
ReaderData,
}; };
use crate::signal::signal_clear_cancel; use crate::signal::signal_clear_cancel;
use crate::threads::assert_is_main_thread; use crate::threads::assert_is_main_thread;
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
use once_cell::sync::{Lazy, OnceCell}; use once_cell::sync::{Lazy, OnceCell};
use std::collections::VecDeque;
use std::ffi::CString; use std::ffi::CString;
use std::os::fd::RawFd;
use std::rc::Rc;
use std::sync::{ use std::sync::{
atomic::{AtomicU32, Ordering}, atomic::{AtomicU32, Ordering},
Mutex, MutexGuard, Mutex, MutexGuard,
@ -46,11 +43,11 @@ pub enum KeyNameStyle {
/// Struct representing a keybinding. Returned by input_get_mappings. /// Struct representing a keybinding. Returned by input_get_mappings.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct InputMapping { pub struct InputMapping {
/// Character sequence which generates this event. /// Character sequence which generates this event.
seq: Vec<Key>, seq: Vec<Key>,
/// Commands that should be evaluated by this mapping. /// Commands that should be evaluated by this mapping.
commands: Vec<WString>, pub commands: Vec<WString>,
/// We wish to preserve the user-specified order. This is just an incrementing value. /// We wish to preserve the user-specified order. This is just an incrementing value.
specification_order: u32, specification_order: u32,
/// Mode in which this command should be evaluated. /// Mode in which this command should be evaluated.
@ -393,37 +390,19 @@ pub fn init_input() {
} }
} }
pub struct Inputter { impl InputEventQueuer for ReaderData {
in_fd: RawFd, fn get_input_data(&self) -> &InputData {
queue: VecDeque<CharEvent>, &self.input_data
paste_buffer: Option<Vec<u8>>,
// We need a parser to evaluate bindings.
parser: Rc<Parser>,
input_function_args: Vec<char>,
function_status: bool,
// Transient storage to avoid repeated allocations.
event_storage: Vec<CharEvent>,
} }
impl InputEventQueuer for Inputter { fn get_input_data_mut(&mut self) -> &mut InputData {
fn get_queue(&self) -> &VecDeque<CharEvent> { &mut self.input_data
&self.queue
}
fn get_queue_mut(&mut self) -> &mut VecDeque<CharEvent> {
&mut self.queue
}
/// Return the fd corresponding to stdin.
fn get_in_fd(&self) -> RawFd {
self.in_fd
} }
fn prepare_to_select(&mut self) { fn prepare_to_select(&mut self) {
// Fire any pending events and reap stray processes, including printing exit status messages. // Fire any pending events and reap stray processes, including printing exit status messages.
event::fire_delayed(&self.parser); event::fire_delayed(self.parser());
if job_reap(&self.parser, true) { if job_reap(self.parser(), true) {
reader_schedule_prompt_repaint(); reader_schedule_prompt_repaint();
} }
} }
@ -434,7 +413,7 @@ impl InputEventQueuer for Inputter {
signal_clear_cancel(); signal_clear_cancel();
// Fire any pending events and reap stray processes, including printing exit status messages. // 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); event::fire_delayed(parser);
if job_reap(parser, true) { if job_reap(parser, true) {
reader_schedule_prompt_repaint(); reader_schedule_prompt_repaint();
@ -452,19 +431,17 @@ impl InputEventQueuer for Inputter {
} }
fn uvar_change_notified(&mut self) { 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) { 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)); self.push_front(CharEvent::from_readline(ReadlineCmd::BeginUndoGroup));
} }
fn paste_is_buffering(&self) -> bool {
self.paste_buffer.is_some()
}
fn paste_commit(&mut self) { fn paste_commit(&mut self) {
self.push_front(CharEvent::from_readline(ReadlineCmd::EndUndoGroup)); 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; return;
}; };
self.push_front(CharEvent::Command(sprintf!( self.push_front(CharEvent::Command(sprintf!(
@ -472,116 +449,11 @@ impl InputEventQueuer for Inputter {
escape(&str2wcstring(&buffer)) 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<Parser>, 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<char> {
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. /// 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. /// 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. /// The list of events which have been dequeued.
peeked: Vec<CharEvent>, peeked: Vec<CharEvent>,
@ -594,11 +466,11 @@ struct EventQueuePeeker<'q> {
subidx: usize, subidx: usize,
/// The queue from which to read more events. /// The queue from which to read more events.
event_queue: &'q mut Inputter, event_queue: &'q mut Queuer,
} }
impl EventQueuePeeker<'_> { impl<'q, Queuer: InputEventQueuer + ?Sized> EventQueuePeeker<'q, Queuer> {
fn new(event_queue: &mut Inputter) -> EventQueuePeeker { pub fn new(event_queue: &'q mut Queuer) -> Self {
EventQueuePeeker { EventQueuePeeker {
peeked: Vec::new(), peeked: Vec::new(),
had_timeout: false, had_timeout: false,
@ -745,24 +617,13 @@ impl EventQueuePeeker<'_> {
} }
/// Reset our index back to 0. /// Reset our index back to 0.
fn restart(&mut self) { pub fn restart(&mut self) {
self.idx = 0; self.idx = 0;
self.subidx = 0; self.subidx = 0;
} }
}
impl Drop for EventQueuePeeker<'_> { /// Return true if this `peeker` matches a given sequence of char events given by `str`.
fn drop(&mut self) { 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?",
);
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!( assert!(
!seq.is_empty(), !seq.is_empty(),
"Empty sequence passed to try_peek_sequence" "Empty sequence passed to try_peek_sequence"
@ -772,12 +633,12 @@ fn try_peek_sequence(peeker: &mut EventQueuePeeker, style: &KeyNameStyle, seq: &
// If we just read an escape, we need to add a timeout for the next char, // 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. // to distinguish between the actual escape key and an "alt"-modifier.
let escaped = *style != KeyNameStyle::Plain && prev == Key::from_raw(key::Escape); let escaped = *style != KeyNameStyle::Plain && prev == Key::from_raw(key::Escape);
if !peeker.next_is_char(style, *key, escaped) { if !self.next_is_char(style, *key, escaped) {
return false; return false;
} }
prev = *key; prev = *key;
} }
if peeker.subidx != 0 { if self.subidx != 0 {
FLOG!( FLOG!(
reader, reader,
"legacy binding matched prefix of key encoding but did not consume all of it" "legacy binding matched prefix of key encoding but did not consume all of it"
@ -787,17 +648,19 @@ fn try_peek_sequence(peeker: &mut EventQueuePeeker, style: &KeyNameStyle, seq: &
true true
} }
/// Return the first mapping that matches, walking first over the user's mapping list, then the /// Return the first mapping that matches from the given mapping set, walking first over the
/// preset list. /// 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 /// Return none if nothing matches, or if we may have matched a longer sequence but it was
/// interrupted by a readline event. /// interrupted by a readline event.
impl Inputter { pub fn find_mapping(
fn find_mapping(vars: &dyn Environment, peeker: &mut EventQueuePeeker) -> Option<InputMapping> { &mut self,
vars: &dyn Environment,
ip: &InputMappingSet,
) -> Option<InputMapping> {
let mut generic: Option<&InputMapping> = None; let mut generic: Option<&InputMapping> = None;
let bind_mode = input_get_bind_mode(vars); let bind_mode = input_get_bind_mode(vars);
let mut escape: Option<&InputMapping> = None; let mut escape: Option<&InputMapping> = None;
let ip = input_mappings();
let ml = ip.mapping_list.iter().chain(ip.preset_mapping_list.iter()); let ml = ip.mapping_list.iter().chain(ip.preset_mapping_list.iter());
for m in ml { for m in ml {
if m.mode != bind_mode { if m.mode != bind_mode {
@ -813,7 +676,7 @@ impl Inputter {
} }
// FLOG!(reader, "trying mapping", format!("{:?}", m)); // 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 // A binding for just escape should also be deferred
// so escape sequences take precedence. // so escape sequences take precedence.
if m.seq == vec![Key::from_raw(key::Escape)] { if m.seq == vec![Key::from_raw(key::Escape)] {
@ -824,9 +687,9 @@ impl Inputter {
return Some(m.clone()); 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. // We might have matched a longer sequence, but we were interrupted, e.g. by a signal.
FLOG!(reader, "torn sequence, rearranging events"); FLOG!(reader, "torn sequence, rearranging events");
return None; return None;
@ -834,7 +697,7 @@ impl Inputter {
if escape.is_some() { if escape.is_some() {
// We need to reconsume the escape. // We need to reconsume the escape.
peeker.next(); self.next();
return escape.cloned(); return escape.cloned();
} }
@ -844,64 +707,21 @@ impl Inputter {
None 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..."); impl<Queue: InputEventQueuer + ?Sized> Drop for EventQueuePeeker<'_, Queue> {
let _ = peeker.next(); fn drop(&mut self) {
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 {
assert!( assert!(
self.event_storage.is_empty(), self.idx == 0 && self.subidx == 0,
"event_storage should be empty" "Events left on the queue - missing restart or consume?",
); );
let mut saved_events = std::mem::take(&mut self.event_storage); self.event_queue.insert_front(self.peeked.drain(self.idx..));
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 /// Support for reading keys from the terminal for the Reader.
self.insert_front(saved_events.drain(..)); impl ReaderData {
self.event_storage = saved_events; /// Read a key from our fd.
self.event_storage.clear();
evt_to_return
}
/// Read a key from stdin.
pub fn read_char(&mut self) -> CharEvent { pub fn read_char(&mut self) -> CharEvent {
// Clear the interrupted flag. // Clear the interrupted flag.
reader_reset_interrupted(); reader_reset_interrupted();
@ -930,8 +750,9 @@ impl Inputter {
} }
ReadlineCmd::FuncAnd | ReadlineCmd::FuncOr => { ReadlineCmd::FuncAnd | ReadlineCmd::FuncOr => {
// If previous function has bad status, skip all functions that follow us. // If previous function has bad status, skip all functions that follow us.
if (!self.function_status && readline_event.cmd == ReadlineCmd::FuncAnd) let fs = self.get_function_status();
|| (self.function_status && readline_event.cmd == ReadlineCmd::FuncOr) if (!fs && readline_event.cmd == ReadlineCmd::FuncAnd)
|| (fs && readline_event.cmd == ReadlineCmd::FuncOr)
{ {
self.drop_leading_readline_events(); 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<char> {
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 { impl InputMappingSet {

View file

@ -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<CharEvent>,
// The current paste buffer, if any.
pub paste_buffer: Option<Vec<u8>>,
// The arguments to the most recently invoked input function.
pub input_function_args: Vec<char>,
// The return status of the most recently invoked input function.
pub function_status: bool,
// Transient storage to avoid repeated allocations.
pub event_storage: Vec<CharEvent>,
}
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. /// A trait which knows how to produce a stream of input events.
/// Note this is conceptually a "base class" with override points. /// Note this is conceptually a "base class" with override points.
pub trait InputEventQueuer { pub trait InputEventQueuer {
/// Return the next event in the queue, or none if the queue is empty. /// Return the next event in the queue, or none if the queue is empty.
fn try_pop(&mut self) -> Option<CharEvent> { fn try_pop(&mut self) -> Option<CharEvent> {
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 /// 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 None
} }
/// Return our queue. These are "abstract" methods to be implemented by concrete types. /// Return the fd from which to read.
fn get_queue(&self) -> &VecDeque<CharEvent>; fn get_in_fd(&self) -> RawFd {
fn get_queue_mut(&mut self) -> &mut VecDeque<CharEvent>; self.get_input_data().in_fd
}
/// Return the fd corresponding to stdin. /// Return the input data. This is to be implemented by the concrete type.
fn get_in_fd(&self) -> RawFd; fn get_input_data(&self) -> &InputData;
fn get_input_data_mut(&mut self) -> &mut InputData;
// Support for "bracketed paste" // Support for "bracketed paste"
// The way it works is that we acknowledge our support by printing // 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). // (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. // See http://thejh.net/misc/website-terminal-copy-paste.
fn paste_start_buffering(&mut self);
fn paste_is_buffering(&self) -> bool; fn paste_start_buffering(&mut self) {
fn paste_push_char(&mut self, _b: u8) {} self.get_input_data_mut().paste_buffer = Some(Vec::new());
fn paste_commit(&mut self); }
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 /// Enqueue a character or a readline function to the queue of unread characters that
/// readch will return before actually reading from fd 0. /// readch will return before actually reading from fd 0.
fn push_back(&mut self, ch: CharEvent) { 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 /// 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. /// will be the next character returned by readch.
fn push_front(&mut self, ch: CharEvent) { 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. /// Find the first sequence of non-char events, and promote them to the front.
fn promote_interruptions_to_front(&mut self) { fn promote_interruptions_to_front(&mut self) {
// Find the first sequence of non-char events. // 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. // 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(); let is_char = |evt: &CharEvent| evt.is_char() || evt.is_eof();
// Find the index of the first non-char event. // Find the index of the first non-char event.
// If there's none, we're done. // If there's none, we're done.
@ -1120,7 +1184,7 @@ pub trait InputEventQueuer {
I: IntoIterator<Item = CharEvent>, I: IntoIterator<Item = CharEvent>,
I::IntoIter: DoubleEndedIterator, I::IntoIter: DoubleEndedIterator,
{ {
let queue = self.get_queue_mut(); let queue = &mut self.get_input_data_mut().queue;
let iter = evts.into_iter().rev(); let iter = evts.into_iter().rev();
queue.reserve(iter.size_hint().0); queue.reserve(iter.size_hint().0);
for evt in iter { for evt in iter {
@ -1130,7 +1194,7 @@ pub trait InputEventQueuer {
/// Forget all enqueued readline events in the front of the queue. /// Forget all enqueued readline events in the front of the queue.
fn drop_leading_readline_events(&mut self) { 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() { while let Some(evt) = queue.front() {
if evt.is_readline_or_command() { if evt.is_readline_or_command() {
queue.pop_front(); queue.pop_front();
@ -1145,46 +1209,43 @@ pub trait InputEventQueuer {
fn prepare_to_select(&mut self) {} fn prepare_to_select(&mut self) {}
/// Called when select() is interrupted by a signal. /// 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. /// Override point for when when select() is interrupted by the universal variable notifier.
/// The default does nothing. /// The default does nothing.
fn uvar_change_notified(&mut self) {} 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. /// Return if we have any lookahead.
fn has_lookahead(&self) -> bool { fn has_lookahead(&self) -> bool {
!self.get_queue().is_empty() !self.get_input_data().queue.is_empty()
} }
} }
/// A simple, concrete implementation of InputEventQueuer. /// A simple, concrete implementation of InputEventQueuer.
pub struct InputEventQueue { pub struct InputEventQueue {
queue: VecDeque<CharEvent>, data: InputData,
in_fd: RawFd,
is_in_bracketed_paste: bool,
} }
impl InputEventQueue { impl InputEventQueue {
pub fn new(in_fd: RawFd) -> InputEventQueue { pub fn new(in_fd: RawFd) -> InputEventQueue {
InputEventQueue { InputEventQueue {
queue: VecDeque::new(), data: InputData::new(in_fd),
in_fd,
is_in_bracketed_paste: false,
} }
} }
} }
impl InputEventQueuer for InputEventQueue { impl InputEventQueuer for InputEventQueue {
fn get_queue(&self) -> &VecDeque<CharEvent> { fn get_input_data(&self) -> &InputData {
&self.queue &self.data
} }
fn get_queue_mut(&mut self) -> &mut VecDeque<CharEvent> { fn get_input_data_mut(&mut self) -> &mut InputData {
&mut self.queue &mut self.data
}
fn get_in_fd(&self) -> RawFd {
self.in_fd
} }
fn select_interrupted(&mut self) { 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;
}
} }

View file

@ -70,10 +70,10 @@ use crate::history::{
SearchType, SearchType,
}; };
use crate::input::init_input; use crate::input::init_input;
use crate::input::Inputter; use crate::input_common::{
use crate::input_common::terminal_protocols_disable_ifn; terminal_protocols_disable_ifn, terminal_protocols_enable_ifn, CharEvent, CharInputStyle,
use crate::input_common::IS_TMUX; InputData, ReadlineCmd, IS_TMUX,
use crate::input_common::{terminal_protocols_enable_ifn, CharEvent, CharInputStyle, ReadlineCmd}; };
use crate::io::IoChain; use crate::io::IoChain;
use crate::kill::{kill_add, kill_replace, kill_yank, kill_yank_rotate}; use crate::kill::{kill_add, kill_replace, kill_yank, kill_yank_rotate};
use crate::libc::MB_CUR_MAX; use crate::libc::MB_CUR_MAX;
@ -496,8 +496,9 @@ pub struct ReaderData {
/// The representation of the current screen contents. /// The representation of the current screen contents.
screen: Screen, screen: Screen,
/// The source of input events. /// Data associated with input events.
inputter: Inputter, /// This is made public so that InputEventQueuer can be implemented on us.
pub input_data: InputData,
queued_repaint: bool, queued_repaint: bool,
/// The history. /// The history.
history: Arc<History>, history: Arc<History>,
@ -880,7 +881,7 @@ pub fn reader_set_autosuggestion_enabled(vars: &dyn Environment) {
if data.conf.autosuggest_ok != enable { if data.conf.autosuggest_ok != enable {
data.conf.autosuggest_ok = enable; data.conf.autosuggest_ok = enable;
data.force_exec_prompt_and_repaint = true; data.force_exec_prompt_and_repaint = true;
data.inputter data.input_data
.queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint));
} }
} }
@ -894,7 +895,7 @@ pub fn reader_schedule_prompt_repaint() {
}; };
if !data.force_exec_prompt_and_repaint { if !data.force_exec_prompt_and_repaint {
data.force_exec_prompt_and_repaint = true; data.force_exec_prompt_and_repaint = true;
data.inputter data.input_data
.queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint));
} }
} }
@ -914,7 +915,7 @@ pub fn reader_execute_readline_cmd(ch: CharEvent) {
data.queued_repaint = true; data.queued_repaint = true;
} }
if data.queued_repaint { if data.queued_repaint {
data.inputter.queue_char(ch); data.input_data.queue_char(ch);
return; return;
} }
if data.rls.is_none() { if data.rls.is_none() {
@ -1074,7 +1075,7 @@ fn reader_received_sighup() -> bool {
impl ReaderData { impl ReaderData {
fn new(parser: ParserRef, history: Arc<History>, conf: ReaderConfig) -> Pin<Box<Self>> { fn new(parser: ParserRef, history: Arc<History>, conf: ReaderConfig) -> Pin<Box<Self>> {
let inputter = Inputter::new(parser.clone(), conf.inputfd); let input_data = InputData::new(conf.inputfd);
Pin::new(Box::new(Self { Pin::new(Box::new(Self {
canary: Rc::new(()), canary: Rc::new(()),
parser_ref: parser, parser_ref: parser,
@ -1090,7 +1091,7 @@ impl ReaderData {
first_prompt: true, first_prompt: true,
last_flash: Default::default(), last_flash: Default::default(),
screen: Screen::new(), screen: Screen::new(),
inputter, input_data,
queued_repaint: false, queued_repaint: false,
history, history,
history_search: Default::default(), history_search: Default::default(),
@ -1162,7 +1163,7 @@ impl ReaderData {
} }
/// Access the parser. /// Access the parser.
fn parser(&self) -> &Parser { pub fn parser(&self) -> &Parser {
&self.parser_ref &self.parser_ref
} }
@ -1965,7 +1966,7 @@ impl ReaderData {
while accumulated_chars.len() < limit { while accumulated_chars.len() < limit {
terminal_protocols_enable_ifn(); terminal_protocols_enable_ifn();
let evt = self.inputter.read_char(); let evt = self.read_char();
let CharEvent::Key(kevt) = &evt else { let CharEvent::Key(kevt) = &evt else {
event_needing_handling = Some(evt); event_needing_handling = Some(evt);
break; break;
@ -2607,14 +2608,14 @@ impl ReaderData {
} else { } else {
self.autosuggestion.clear(); self.autosuggestion.clear();
} }
self.inputter.function_set_status(true); self.input_data.function_set_status(true);
return; return;
} }
if !self.history_pager_active { if !self.history_pager_active {
self.inputter.function_set_status(false); self.input_data.function_set_status(false);
return; return;
} }
self.inputter.function_set_status(true); self.input_data.function_set_status(true);
if let Some(completion) = if let Some(completion) =
self.pager.selected_completion(&self.current_page_rendering) self.pager.selected_completion(&self.current_page_rendering)
{ {
@ -2708,7 +2709,7 @@ impl ReaderData {
if c == rl::PrevdOrBackwardWord && self.command_line.is_empty() { if c == rl::PrevdOrBackwardWord && self.command_line.is_empty() {
self.eval_bind_cmd(L!("prevd")); self.eval_bind_cmd(L!("prevd"));
self.force_exec_prompt_and_repaint = true; self.force_exec_prompt_and_repaint = true;
self.inputter self.input_data
.queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint));
return; return;
} }
@ -2730,7 +2731,7 @@ impl ReaderData {
if c == rl::NextdOrForwardWord && self.command_line.is_empty() { if c == rl::NextdOrForwardWord && self.command_line.is_empty() {
self.eval_bind_cmd(L!("nextd")); self.eval_bind_cmd(L!("nextd"));
self.force_exec_prompt_and_repaint = true; self.force_exec_prompt_and_repaint = true;
self.inputter self.input_data
.queue_char(CharEvent::from_readline(ReadlineCmd::Repaint)); .queue_char(CharEvent::from_readline(ReadlineCmd::Repaint));
return; return;
} }
@ -2840,7 +2841,7 @@ impl ReaderData {
let success = !self.autosuggestion.is_empty(); let success = !self.autosuggestion.is_empty();
self.autosuggestion.clear(); self.autosuggestion.clear();
// Return true if we had a suggestion to 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 => { rl::AcceptAutosuggestion => {
self.accept_autosuggestion(true, false, MoveWordStyle::Punctuation); self.accept_autosuggestion(true, false, MoveWordStyle::Punctuation);
@ -3073,10 +3074,10 @@ impl ReaderData {
_ => unreachable!(), _ => unreachable!(),
}; };
let (elt, _el) = self.active_edit_line(); 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); let success = self.jump(direction, precision, elt, target);
self.inputter.function_set_status(success); self.input_data.function_set_status(success);
} }
} }
rl::RepeatJump => { rl::RepeatJump => {
@ -3092,7 +3093,7 @@ impl ReaderData {
); );
} }
self.inputter.function_set_status(success); self.input_data.function_set_status(success);
} }
rl::ReverseRepeatJump => { rl::ReverseRepeatJump => {
let (elt, _el) = self.active_edit_line(); let (elt, _el) = self.active_edit_line();
@ -3111,13 +3112,13 @@ impl ReaderData {
self.last_jump_direction = original_dir; self.last_jump_direction = original_dir;
self.inputter.function_set_status(success); self.input_data.function_set_status(success);
} }
rl::ExpandAbbr => { rl::ExpandAbbr => {
if self.expand_abbreviation_at_cursor(1) { if self.expand_abbreviation_at_cursor(1) {
self.inputter.function_set_status(true); self.input_data.function_set_status(true);
} else { } else {
self.inputter.function_set_status(false); self.input_data.function_set_status(false);
} }
} }
rl::Undo | rl::Redo => { rl::Undo | rl::Redo => {

View file

@ -1,18 +1,29 @@
use crate::input::{input_mappings, Inputter, KeyNameStyle, DEFAULT_BIND_MODE}; use crate::env::EnvStack;
use crate::input_common::{CharEvent, ReadlineCmd}; use crate::input::{EventQueuePeeker, InputMappingSet, KeyNameStyle, DEFAULT_BIND_MODE};
use crate::input_common::{CharEvent, InputData, InputEventQueuer};
use crate::key::Key; use crate::key::Key;
use crate::parser::Parser;
use crate::tests::prelude::*;
use crate::wchar::prelude::*; use crate::wchar::prelude::*;
use std::rc::Rc; 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] #[test]
#[serial]
fn test_input() { fn test_input() {
let _cleanup = test_init(); let vars = Rc::new(EnvStack::new());
use crate::env::EnvStack; let mut input = TestInputEventQueuer {
let parser = Parser::new(Rc::new(EnvStack::new()), false); input_data: InputData::new(i32::MAX), // value doesn't matter since we don't read from it
let mut input = Inputter::new(parser, libc::STDIN_FILENO); };
// Ensure sequences are order independent. Here we add two bindings where the first is a prefix // 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 // of the second, and then emit the second key list. The second binding should be invoked, not
// the first! // the first!
@ -22,9 +33,8 @@ fn test_input() {
let default_mode = || DEFAULT_BIND_MODE.to_owned(); let default_mode = || DEFAULT_BIND_MODE.to_owned();
{ let mut input_mappings = InputMappingSet::default();
let mut input_mapping = input_mappings(); input_mappings.add1(
input_mapping.add1(
prefix_binding, prefix_binding,
KeyNameStyle::Plain, KeyNameStyle::Plain,
WString::from_str("up-line"), WString::from_str("up-line"),
@ -32,7 +42,7 @@ fn test_input() {
None, None,
true, true,
); );
input_mapping.add1( input_mappings.add1(
desired_binding.clone(), desired_binding.clone(),
KeyNameStyle::Plain, KeyNameStyle::Plain,
WString::from_str("down-line"), WString::from_str("down-line"),
@ -40,18 +50,15 @@ fn test_input() {
None, None,
true, true,
); );
}
// Push the desired binding to the queue. // Push the desired binding to the queue.
for c in desired_binding { for c in desired_binding {
input.queue_char(CharEvent::from_key(c)); input.input_data.queue_char(CharEvent::from_key(c));
} }
// Now test. let mut peeker = EventQueuePeeker::new(&mut input);
let evt = input.read_char(); let mapping = peeker.find_mapping(&*vars, &input_mappings);
if !evt.is_readline() { assert!(mapping.is_some());
panic!("Event is not a readline"); assert!(mapping.unwrap().commands == ["down-line"]);
} else if evt.get_readline() != ReadlineCmd::DownLine { peeker.restart();
panic!("Expected to read char down_line");
}
} }