mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-02-24 16:34:42 +08:00
Fix query response wait confusion over builtin read
Every reader gets their own wait handle which is wrong and not actually needed - it's a singleton. We should probaly make it global. Let's do an intermediate solution for now -- not much time this weekend ;). Fixes #11110
This commit is contained in:
parent
cbedfc8a64
commit
66e2b6d8c1
14
src/input.rs
14
src/input.rs
@ -463,18 +463,10 @@ impl<'a> InputEventQueuer for Reader<'a> {
|
||||
}
|
||||
|
||||
fn is_blocked(&self) -> bool {
|
||||
self.blocking_wait.is_some()
|
||||
self.blocking_wait().is_some()
|
||||
}
|
||||
fn unblock_input(&mut self) -> bool {
|
||||
if !self.is_blocked() {
|
||||
return false;
|
||||
}
|
||||
self.blocking_wait = None;
|
||||
true
|
||||
}
|
||||
|
||||
fn blocking_wait(&self) -> Option<&BlockingWait> {
|
||||
self.blocking_wait.as_ref()
|
||||
fn blocking_wait(&self) -> MutexGuard<Option<BlockingWait>> {
|
||||
self.data.blocking_wait()
|
||||
}
|
||||
|
||||
fn on_mouse_left_click(&mut self, position: ViewportPosition) {
|
||||
|
@ -25,6 +25,7 @@ use std::os::fd::RawFd;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
// The range of key codes for inputrc-style keyboard functions.
|
||||
pub const R_END_INPUT_FUNCTIONS: usize = (ReadlineCmd::ReverseRepeatJump as usize) + 1;
|
||||
@ -801,7 +802,7 @@ pub trait InputEventQueuer {
|
||||
reader,
|
||||
"Received interrupt key, giving up waiting for response from terminal"
|
||||
);
|
||||
let ok = self.unblock_input();
|
||||
let ok = unblock_input(self.blocking_wait());
|
||||
assert!(ok);
|
||||
}
|
||||
continue;
|
||||
@ -1062,7 +1063,9 @@ pub trait InputEventQueuer {
|
||||
if code != 0 || c != b'M' || modifiers.is_some() {
|
||||
return None;
|
||||
}
|
||||
let Some(wait) = self.blocking_wait() else {
|
||||
let wait_guard = self.blocking_wait();
|
||||
let Some(wait) = &*wait_guard else {
|
||||
drop(wait_guard);
|
||||
self.on_mouse_left_click(position);
|
||||
return None;
|
||||
};
|
||||
@ -1109,7 +1112,8 @@ pub trait InputEventQueuer {
|
||||
return invalid_sequence(buffer);
|
||||
};
|
||||
FLOG!(reader, "Received cursor position report y:", y, "x:", x);
|
||||
let Some(BlockingWait::CursorPosition(wait)) = self.blocking_wait() else {
|
||||
let wait_guard = self.blocking_wait();
|
||||
let Some(BlockingWait::CursorPosition(wait)) = &*wait_guard else {
|
||||
CURSOR_POSITION_REPORTING_SUPPORTED.store(true);
|
||||
return None;
|
||||
};
|
||||
@ -1124,6 +1128,7 @@ pub trait InputEventQueuer {
|
||||
ImplicitEvent::ScrollbackPushContinuation(y)
|
||||
}
|
||||
};
|
||||
drop(wait_guard);
|
||||
self.push_front(CharEvent::Implicit(continuation));
|
||||
return None;
|
||||
}
|
||||
@ -1536,15 +1541,13 @@ pub trait InputEventQueuer {
|
||||
}
|
||||
}
|
||||
|
||||
fn blocking_wait(&self) -> Option<&BlockingWait> {
|
||||
None
|
||||
fn blocking_wait(&self) -> MutexGuard<Option<BlockingWait>> {
|
||||
static NO_WAIT: Mutex<Option<BlockingWait>> = Mutex::new(None);
|
||||
NO_WAIT.lock().unwrap()
|
||||
}
|
||||
fn is_blocked(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn unblock_input(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn on_mouse_left_click(&mut self, _position: ViewportPosition) {}
|
||||
|
||||
@ -1559,7 +1562,7 @@ pub trait InputEventQueuer {
|
||||
let vintr = shell_modes().c_cc[libc::VINTR];
|
||||
if vintr != 0 {
|
||||
let interrupt_evt = CharEvent::from_key(Key::from_single_byte(vintr));
|
||||
if self.unblock_input() {
|
||||
if unblock_input(self.blocking_wait()) {
|
||||
FLOG!(
|
||||
reader,
|
||||
"Received interrupt, giving up on waiting for terminal response"
|
||||
@ -1590,6 +1593,14 @@ pub trait InputEventQueuer {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn unblock_input(mut wait_guard: MutexGuard<Option<BlockingWait>>) -> bool {
|
||||
if wait_guard.is_none() {
|
||||
return false;
|
||||
}
|
||||
*wait_guard = None;
|
||||
true
|
||||
}
|
||||
|
||||
fn invalid_sequence(buffer: &[u8]) -> Option<Key> {
|
||||
FLOG!(
|
||||
reader,
|
||||
|
@ -82,6 +82,7 @@ use crate::history::{
|
||||
};
|
||||
use crate::input::init_input;
|
||||
use crate::input_common::kitty_progressive_enhancements_query;
|
||||
use crate::input_common::unblock_input;
|
||||
use crate::input_common::BlockingWait;
|
||||
use crate::input_common::Capability;
|
||||
use crate::input_common::CursorPositionWait;
|
||||
@ -521,7 +522,7 @@ pub struct ReaderData {
|
||||
/// The representation of the current screen contents.
|
||||
screen: Screen,
|
||||
|
||||
pub blocking_wait: Option<BlockingWait>,
|
||||
pub blocking_wait: Rc<Mutex<Option<BlockingWait>>>,
|
||||
|
||||
/// Data associated with input events.
|
||||
/// This is made public so that InputEventQueuer can be implemented on us.
|
||||
@ -1159,6 +1160,10 @@ fn reader_received_sighup() -> bool {
|
||||
|
||||
impl ReaderData {
|
||||
fn new(history: Arc<History>, conf: ReaderConfig) -> Pin<Box<Self>> {
|
||||
let blocking_wait = reader_current_data().map_or_else(
|
||||
|| Rc::new(Mutex::new(Some(BlockingWait::Startup(Queried::NotYet)))),
|
||||
|parent| parent.blocking_wait.clone(),
|
||||
);
|
||||
let input_data = InputData::new(conf.inputfd);
|
||||
Pin::new(Box::new(Self {
|
||||
canary: Rc::new(()),
|
||||
@ -1176,7 +1181,7 @@ impl ReaderData {
|
||||
last_flash: Default::default(),
|
||||
flash_autosuggestion: false,
|
||||
screen: Screen::new(),
|
||||
blocking_wait: Some(BlockingWait::Startup(Queried::NotYet)),
|
||||
blocking_wait,
|
||||
input_data,
|
||||
queued_repaint: false,
|
||||
history,
|
||||
@ -1203,6 +1208,10 @@ impl ReaderData {
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn blocking_wait(&self) -> MutexGuard<Option<BlockingWait>> {
|
||||
self.blocking_wait.lock().unwrap()
|
||||
}
|
||||
|
||||
// We repaint our prompt if fstat reports the tty as having changed.
|
||||
// But don't react to tty changes that we initiated, because of commands or
|
||||
// on-variable events (e.g. for fish_bind_mode). See #3481.
|
||||
@ -1410,11 +1419,12 @@ impl ReaderData {
|
||||
pub fn request_cursor_position(
|
||||
&mut self,
|
||||
out: &mut Outputter,
|
||||
cursor_position_wait: Option<CursorPositionWait>,
|
||||
wait: Option<CursorPositionWait>,
|
||||
) {
|
||||
if let Some(cursor_position_wait) = cursor_position_wait {
|
||||
assert!(self.blocking_wait.is_none());
|
||||
self.blocking_wait = Some(BlockingWait::CursorPosition(cursor_position_wait));
|
||||
if let Some(cursor_position_wait) = wait {
|
||||
let mut wait_guard = self.blocking_wait();
|
||||
assert!(wait_guard.is_none());
|
||||
*wait_guard = Some(BlockingWait::CursorPosition(cursor_position_wait));
|
||||
}
|
||||
let _ = out.write_all(b"\x1b[6n");
|
||||
self.save_screen_state();
|
||||
@ -2167,11 +2177,11 @@ impl<'a> Reader<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if zelf.blocking_wait == Some(BlockingWait::Startup(Queried::NotYet)) {
|
||||
if *zelf.blocking_wait() == Some(BlockingWait::Startup(Queried::NotYet)) {
|
||||
if is_dumb() || IN_MIDNIGHT_COMMANDER.load() || IN_DVTM.load() {
|
||||
zelf.blocking_wait = None;
|
||||
*zelf.blocking_wait() = None;
|
||||
} else {
|
||||
zelf.blocking_wait = Some(BlockingWait::Startup(Queried::Once));
|
||||
*zelf.blocking_wait() = Some(BlockingWait::Startup(Queried::Once));
|
||||
let mut out = Outputter::stdoutput().borrow_mut();
|
||||
out.begin_buffering();
|
||||
// Query for kitty keyboard protocol support.
|
||||
@ -2497,7 +2507,8 @@ impl<'a> Reader<'a> {
|
||||
self.save_screen_state();
|
||||
}
|
||||
ImplicitEvent::PrimaryDeviceAttribute => {
|
||||
let Some(wait) = &self.blocking_wait else {
|
||||
let mut wait_guard = self.blocking_wait();
|
||||
let Some(wait) = &*wait_guard else {
|
||||
// Rogue reply.
|
||||
return ControlFlow::Continue(());
|
||||
};
|
||||
@ -2520,22 +2531,23 @@ impl<'a> Reader<'a> {
|
||||
query_capabilities_via_dcs(out.by_ref());
|
||||
let _ = out.write_all(QUERY_PRIMARY_DEVICE_ATTRIBUTE);
|
||||
out.end_buffering();
|
||||
*wait_guard = Some(BlockingWait::Startup(Queried::Twice));
|
||||
drop(wait_guard);
|
||||
self.save_screen_state();
|
||||
self.blocking_wait = Some(BlockingWait::Startup(Queried::Twice));
|
||||
return ControlFlow::Continue(());
|
||||
}
|
||||
}
|
||||
Queried::Twice => (),
|
||||
}
|
||||
self.unblock_input();
|
||||
unblock_input(wait_guard);
|
||||
}
|
||||
ImplicitEvent::MouseLeftClickContinuation(cursor, click_position) => {
|
||||
self.mouse_left_click(cursor, click_position);
|
||||
self.unblock_input();
|
||||
unblock_input(self.blocking_wait());
|
||||
}
|
||||
ImplicitEvent::ScrollbackPushContinuation(cursor_y) => {
|
||||
self.screen.push_to_scrollback(cursor_y);
|
||||
self.unblock_input();
|
||||
unblock_input(self.blocking_wait());
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -3786,7 +3798,9 @@ impl<'a> Reader<'a> {
|
||||
if !SCROLL_FORWARD_SUPPORTED.load() || !CURSOR_UP_SUPPORTED.load() {
|
||||
return;
|
||||
}
|
||||
let Some(wait) = self.blocking_wait() else {
|
||||
let wait_guard = self.blocking_wait();
|
||||
let Some(wait) = &*wait_guard else {
|
||||
drop(wait_guard);
|
||||
self.request_cursor_position(
|
||||
&mut Outputter::stdoutput().borrow_mut(),
|
||||
Some(CursorPositionWait::ScrollbackPush),
|
||||
|
20
tests/checks/tmux-read.fish
Normal file
20
tests/checks/tmux-read.fish
Normal file
@ -0,0 +1,20 @@
|
||||
#RUN: %fish %s
|
||||
#REQUIRES: command -v tmux
|
||||
#REQUIRES: test -z "$CI"
|
||||
|
||||
set -g isolated_tmux_fish_extra_args -C '
|
||||
function fish_greeting
|
||||
set -l name (read)
|
||||
echo hello $name
|
||||
end
|
||||
'
|
||||
isolated-tmux-start
|
||||
|
||||
isolated-tmux send-keys name Enter 'echo foo' Enter
|
||||
tmux-sleep
|
||||
isolated-tmux capture-pane -p
|
||||
# CHECK: read> name
|
||||
# CHECK: hello name
|
||||
# CHECK: prompt 0> echo foo
|
||||
# CHECK: foo
|
||||
# CHECK: prompt 1>
|
Loading…
x
Reference in New Issue
Block a user