fix(ui): handle pasted text properly (#494)
* fix(ui): handle pasted text properly * style(fmt): rustfmt
This commit is contained in:
parent
ce50d6e406
commit
3f1ec7c295
5 changed files with 100 additions and 9 deletions
|
|
@ -31,8 +31,9 @@ pub enum ClientInstruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: Config) {
|
pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: Config) {
|
||||||
let clear_client_terminal_attributes = "\u{1b}[?1l\u{1b}=\u{1b}[r\u{1b}12l\u{1b}[?1000l\u{1b}[?1002l\u{1b}[?1003l\u{1b}[?1005l\u{1b}[?1006l";
|
let clear_client_terminal_attributes = "\u{1b}[?1l\u{1b}=\u{1b}[r\u{1b}12l\u{1b}[?1000l\u{1b}[?1002l\u{1b}[?1003l\u{1b}[?1005l\u{1b}[?1006l\u{1b}[?12l";
|
||||||
let take_snapshot = "\u{1b}[?1049h";
|
let take_snapshot = "\u{1b}[?1049h";
|
||||||
|
let bracketed_paste = "\u{1b}[?2004h";
|
||||||
os_input.unset_raw_mode(0);
|
os_input.unset_raw_mode(0);
|
||||||
let _ = os_input
|
let _ = os_input
|
||||||
.get_stdout_writer()
|
.get_stdout_writer()
|
||||||
|
|
@ -57,6 +58,10 @@ pub fn start_client(mut os_input: Box<dyn ClientOsApi>, opts: CliArgs, config: C
|
||||||
config_options,
|
config_options,
|
||||||
));
|
));
|
||||||
os_input.set_raw_mode(0);
|
os_input.set_raw_mode(0);
|
||||||
|
let _ = os_input
|
||||||
|
.get_stdout_writer()
|
||||||
|
.write(bracketed_paste.as_bytes())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (send_client_instructions, receive_client_instructions): SyncChannelWithContext<
|
let (send_client_instructions, receive_client_instructions): SyncChannelWithContext<
|
||||||
ClientInstruction,
|
ClientInstruction,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ struct InputHandler {
|
||||||
command_is_executing: CommandIsExecuting,
|
command_is_executing: CommandIsExecuting,
|
||||||
send_client_instructions: SenderWithContext<ClientInstruction>,
|
send_client_instructions: SenderWithContext<ClientInstruction>,
|
||||||
should_exit: bool,
|
should_exit: bool,
|
||||||
|
pasting: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputHandler {
|
impl InputHandler {
|
||||||
|
|
@ -40,6 +41,7 @@ impl InputHandler {
|
||||||
command_is_executing,
|
command_is_executing,
|
||||||
send_client_instructions,
|
send_client_instructions,
|
||||||
should_exit: false,
|
should_exit: false,
|
||||||
|
pasting: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,6 +51,8 @@ impl InputHandler {
|
||||||
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
|
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
|
||||||
err_ctx.add_call(ContextType::StdinHandler);
|
err_ctx.add_call(ContextType::StdinHandler);
|
||||||
let alt_left_bracket = vec![27, 91];
|
let alt_left_bracket = vec![27, 91];
|
||||||
|
let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~
|
||||||
|
let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201
|
||||||
loop {
|
loop {
|
||||||
if self.should_exit {
|
if self.should_exit {
|
||||||
break;
|
break;
|
||||||
|
|
@ -67,6 +71,10 @@ impl InputHandler {
|
||||||
if unsupported_key == alt_left_bracket {
|
if unsupported_key == alt_left_bracket {
|
||||||
let key = Key::Alt('[');
|
let key = Key::Alt('[');
|
||||||
self.handle_key(&key, raw_bytes);
|
self.handle_key(&key, raw_bytes);
|
||||||
|
} else if unsupported_key == bracketed_paste_start {
|
||||||
|
self.pasting = true;
|
||||||
|
} else if unsupported_key == bracketed_paste_end {
|
||||||
|
self.pasting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
termion::event::Event::Mouse(_) => {
|
termion::event::Event::Mouse(_) => {
|
||||||
|
|
@ -81,10 +89,20 @@ impl InputHandler {
|
||||||
}
|
}
|
||||||
fn handle_key(&mut self, key: &Key, raw_bytes: Vec<u8>) {
|
fn handle_key(&mut self, key: &Key, raw_bytes: Vec<u8>) {
|
||||||
let keybinds = &self.config.keybinds;
|
let keybinds = &self.config.keybinds;
|
||||||
for action in Keybinds::key_to_actions(&key, raw_bytes, &self.mode, keybinds) {
|
if self.pasting {
|
||||||
let should_exit = self.dispatch_action(action);
|
// we're inside a paste block, if we're in a mode that allows sending text to the
|
||||||
if should_exit {
|
// terminal, send all text directly without interpreting it
|
||||||
self.should_exit = true;
|
// otherwise, just discard the input
|
||||||
|
if self.mode == InputMode::Normal || self.mode == InputMode::Locked {
|
||||||
|
let action = Action::Write(raw_bytes);
|
||||||
|
self.dispatch_action(action);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for action in Keybinds::key_to_actions(&key, raw_bytes, &self.mode, keybinds) {
|
||||||
|
let should_exit = self.dispatch_action(action);
|
||||||
|
if should_exit {
|
||||||
|
self.should_exit = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ use ::insta::assert_snapshot;
|
||||||
use crate::common::input::config::Config;
|
use crate::common::input::config::Config;
|
||||||
use crate::tests::fakes::FakeInputOutput;
|
use crate::tests::fakes::FakeInputOutput;
|
||||||
use crate::tests::utils::commands::{
|
use crate::tests::utils::commands::{
|
||||||
PANE_MODE, QUIT, SCROLL_DOWN_IN_SCROLL_MODE, SCROLL_MODE, SCROLL_PAGE_DOWN_IN_SCROLL_MODE,
|
BRACKETED_PASTE_END, BRACKETED_PASTE_START, PANE_MODE, QUIT, SCROLL_DOWN_IN_SCROLL_MODE,
|
||||||
SCROLL_PAGE_UP_IN_SCROLL_MODE, SCROLL_UP_IN_SCROLL_MODE, SPAWN_TERMINAL_IN_PANE_MODE,
|
SCROLL_MODE, SCROLL_PAGE_DOWN_IN_SCROLL_MODE, SCROLL_PAGE_UP_IN_SCROLL_MODE,
|
||||||
SPLIT_DOWN_IN_PANE_MODE, SPLIT_RIGHT_IN_PANE_MODE,
|
SCROLL_UP_IN_SCROLL_MODE, SPAWN_TERMINAL_IN_PANE_MODE, SPLIT_DOWN_IN_PANE_MODE,
|
||||||
TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
|
SPLIT_RIGHT_IN_PANE_MODE, TOGGLE_ACTIVE_TERMINAL_FULLSCREEN_IN_PANE_MODE,
|
||||||
};
|
};
|
||||||
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
|
use crate::tests::utils::{get_next_to_last_snapshot, get_output_frame_snapshots};
|
||||||
use crate::{start, CliArgs};
|
use crate::{start, CliArgs};
|
||||||
|
|
@ -441,3 +441,43 @@ pub fn toggle_focused_pane_fullscreen() {
|
||||||
get_next_to_last_snapshot(snapshots).expect("could not find snapshot");
|
get_next_to_last_snapshot(snapshots).expect("could not find snapshot");
|
||||||
assert_snapshot!(snapshot_before_quit);
|
assert_snapshot!(snapshot_before_quit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn bracketed_paste() {
|
||||||
|
// bracketed paste (https://xfree86.org/current/ctlseqs.html#Bracketed%20Paste%20Mode)
|
||||||
|
// makes sure that text the user pastes is not interpreted as commands by the running program
|
||||||
|
// (zellij in this case)
|
||||||
|
// this tests makes sure the "SPLIT_RIGHT_IN_PANE_MODE" command is not interpreted as Zellij,
|
||||||
|
// since it's inside a bracketed paste block, while the "QUIT" command is, since it is already
|
||||||
|
// past the block
|
||||||
|
let fake_win_size = PositionAndSize {
|
||||||
|
columns: 121,
|
||||||
|
rows: 20,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let mut fake_input_output = get_fake_os_input(&fake_win_size);
|
||||||
|
fake_input_output.add_terminal_input(&[
|
||||||
|
&PANE_MODE,
|
||||||
|
&BRACKETED_PASTE_START,
|
||||||
|
&SPLIT_RIGHT_IN_PANE_MODE,
|
||||||
|
&BRACKETED_PASTE_END,
|
||||||
|
&QUIT,
|
||||||
|
]);
|
||||||
|
start(
|
||||||
|
Box::new(fake_input_output.clone()),
|
||||||
|
CliArgs::default(),
|
||||||
|
Box::new(fake_input_output.clone()),
|
||||||
|
Config::default(),
|
||||||
|
);
|
||||||
|
let output_frames = fake_input_output
|
||||||
|
.stdout_writer
|
||||||
|
.output_frames
|
||||||
|
.lock()
|
||||||
|
.unwrap();
|
||||||
|
let snapshots = get_output_frame_snapshots(&output_frames, &fake_win_size);
|
||||||
|
let snapshot_before_quit =
|
||||||
|
get_next_to_last_snapshot(snapshots).expect("could not find snapshot");
|
||||||
|
assert_snapshot!(snapshot_before_quit);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
source: src/tests/integration/basic.rs
|
||||||
|
expression: snapshot_before_quit
|
||||||
|
|
||||||
|
---
|
||||||
|
line1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line2-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line3-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line4-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line5-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line6-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line7-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line8-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line9-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line10-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line11-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line12-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line13-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line14-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line15-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line16-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line17-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line18-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
line19-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
prompt $ █
|
||||||
|
|
@ -81,5 +81,8 @@ pub mod commands {
|
||||||
pub const SWITCH_NEXT_TAB_IN_TAB_MODE: [u8; 1] = [108]; // l
|
pub const SWITCH_NEXT_TAB_IN_TAB_MODE: [u8; 1] = [108]; // l
|
||||||
pub const SWITCH_PREV_TAB_IN_TAB_MODE: [u8; 1] = [104]; // h
|
pub const SWITCH_PREV_TAB_IN_TAB_MODE: [u8; 1] = [104]; // h
|
||||||
pub const CLOSE_TAB_IN_TAB_MODE: [u8; 1] = [120]; // x
|
pub const CLOSE_TAB_IN_TAB_MODE: [u8; 1] = [120]; // x
|
||||||
|
|
||||||
|
pub const BRACKETED_PASTE_START: [u8; 6] = [27, 91, 50, 48, 48, 126]; // \u{1b}[200~
|
||||||
|
pub const BRACKETED_PASTE_END: [u8; 6] = [27, 91, 50, 48, 49, 126]; // \u{1b}[201
|
||||||
pub const SLEEP: [u8; 0] = [];
|
pub const SLEEP: [u8; 0] = [];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue