mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-03-24 11:58:11 +08:00
Stop parsing invalid CSI/SS3 sequences as alt-[/alt-o
This situation can be triggered in practice inside a terminal like tmux 3.5 by running tmux new-session fish -C 'sleep 2' -d reader -o log-file and typing "alt-escape x" The log shows that we drop treat this as alt-[ and drop the x on the floor. reader: Read char alt-\[ -- Key { modifiers: Modifiers { ctrl: false, alt: true, shift: false }, codepoint: '[' } -- [27, 91, 120] This input ("\e[x") is ambiguous. It looks like it could mean "alt-[,x". However that conflicts with a potential future CSI code, so it makes no sense to try to support this. Returning "None" from parse_csi() causes this weird behavior of returning "alt-[" and dropping the rest of the parsed sequence. This is too easy; it has even crept into a bunch of places where the input sequence is actually valid like "VT200 button released" but where we definitely don't want to report any key. Fix the default: report no key for all unknown sequences and intentionally-suppressed sequences. Treat it at "alt-[" only when there is no input byte available, which is more or less unambiguous, hence a strong enough signal that this is a actually "alt-[".
This commit is contained in:
parent
e1e963ae66
commit
3201cb9f01
@ -10,8 +10,8 @@ use crate::flog::{FloggableDebug, FLOG};
|
||||
use crate::fork_exec::flog_safe::FLOG_SAFE;
|
||||
use crate::global_safety::RelaxedAtomicBool;
|
||||
use crate::key::{
|
||||
self, alt, canonicalize_control_char, canonicalize_keyed_control_char, function_key, shift,
|
||||
Key, Modifiers, ViewportPosition,
|
||||
self, alt, canonicalize_control_char, canonicalize_keyed_control_char, ctrl, function_key,
|
||||
shift, Key, Modifiers, ViewportPosition,
|
||||
};
|
||||
use crate::reader::{reader_current_data, reader_test_and_clear_interrupted};
|
||||
use crate::threads::{iothread_port, is_main_thread};
|
||||
@ -773,6 +773,7 @@ pub trait InputEventQueuer {
|
||||
}
|
||||
return None;
|
||||
};
|
||||
let invalid = Key::from_raw(key::Invalid);
|
||||
if buffer.len() == 2 && next == b'\x1b' {
|
||||
return Some(
|
||||
match self.parse_escape_sequence(buffer, have_escape_prefix) {
|
||||
@ -780,17 +781,17 @@ pub trait InputEventQueuer {
|
||||
nested_sequence.modifiers.alt = true;
|
||||
nested_sequence
|
||||
}
|
||||
None => Key::from_raw(key::Invalid),
|
||||
None => invalid,
|
||||
},
|
||||
);
|
||||
}
|
||||
if next == b'[' {
|
||||
// potential CSI
|
||||
return Some(self.parse_csi(buffer).unwrap_or(alt('[')));
|
||||
return Some(self.parse_csi(buffer).unwrap_or(invalid));
|
||||
}
|
||||
if next == b'O' {
|
||||
// potential SS3
|
||||
return Some(self.parse_ss3(buffer).unwrap_or(alt('O')));
|
||||
return Some(self.parse_ss3(buffer).unwrap_or(invalid));
|
||||
}
|
||||
match canonicalize_control_char(next) {
|
||||
Some(mut key) => {
|
||||
@ -872,10 +873,12 @@ pub trait InputEventQueuer {
|
||||
}
|
||||
|
||||
fn parse_csi(&mut self, buffer: &mut Vec<u8>) -> Option<Key> {
|
||||
let mut next_char = |zelf: &mut Self| zelf.try_readb(buffer).unwrap_or(0xff);
|
||||
// The maximum number of CSI parameters is defined by NPAR, nominally 16.
|
||||
let mut params = [[0_u32; 4]; 16];
|
||||
let mut c = next_char(self);
|
||||
let Some(mut c) = self.try_readb(buffer) else {
|
||||
return Some(ctrl('['));
|
||||
};
|
||||
let mut next_char = |zelf: &mut Self| zelf.try_readb(buffer).unwrap_or(0xff);
|
||||
let private_mode;
|
||||
if matches!(c, b'?' | b'<' | b'=' | b'>') {
|
||||
// private mode
|
||||
@ -1063,11 +1066,11 @@ pub trait InputEventQueuer {
|
||||
} // rxvt style
|
||||
200 => {
|
||||
self.paste_start_buffering();
|
||||
return Some(Key::from_raw(key::Invalid));
|
||||
return None;
|
||||
}
|
||||
201 => {
|
||||
self.paste_commit();
|
||||
return Some(Key::from_raw(key::Invalid));
|
||||
return None;
|
||||
}
|
||||
_ => return None,
|
||||
},
|
||||
@ -1113,11 +1116,11 @@ pub trait InputEventQueuer {
|
||||
b'Z' => shift(key::Tab),
|
||||
b'I' => {
|
||||
self.push_front(CharEvent::Implicit(ImplicitEvent::FocusIn));
|
||||
return Some(Key::from_raw(key::Invalid));
|
||||
return None;
|
||||
}
|
||||
b'O' => {
|
||||
self.push_front(CharEvent::Implicit(ImplicitEvent::FocusOut));
|
||||
return Some(Key::from_raw(key::Invalid));
|
||||
return None;
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
@ -1140,13 +1143,12 @@ pub trait InputEventQueuer {
|
||||
|
||||
fn parse_ss3(&mut self, buffer: &mut Vec<u8>) -> Option<Key> {
|
||||
let mut raw_mask = 0;
|
||||
let mut code = b'0';
|
||||
loop {
|
||||
let Some(mut code) = self.try_readb(buffer) else {
|
||||
return Some(alt('O'));
|
||||
};
|
||||
while (b'0'..=b'9').contains(&code) {
|
||||
raw_mask = raw_mask * 10 + u32::from(code - b'0');
|
||||
code = self.try_readb(buffer).unwrap_or(0xff);
|
||||
if !(b'0'..=b'9').contains(&code) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let modifiers = parse_mask(raw_mask.saturating_sub(1));
|
||||
#[rustfmt::skip]
|
||||
|
Loading…
x
Reference in New Issue
Block a user