From cf438969a2aeb5dc099310f50d501cfc3efde572 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 6 Feb 2022 14:13:47 +0800 Subject: [PATCH 1/5] Add bracketed paste to Windows. --- src/tty/windows.rs | 88 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 920729e18..37e1a442a 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -92,6 +92,7 @@ impl RawMode for ConsoleMode { if let Some(original_stdstream_mode) = self.original_stdstream_mode { if let Some(out) = self.out { write_and_flush(out, BRACKETED_PASTE_OFF)?; + debug!(target: "rustyline", "Turned bracketed paste off"); } check(unsafe { consoleapi::SetConsoleMode(self.stdstream_handle, original_stdstream_mode) @@ -104,12 +105,16 @@ impl RawMode for ConsoleMode { /// Console input reader pub struct ConsoleRawReader { handle: HANDLE, + enable_bracketed_paste: bool, } impl ConsoleRawReader { - pub fn create() -> Result { + pub fn create(enable_bracketed_paste: bool) -> Result { let handle = get_std_handle(STDIN_FILENO)?; - Ok(ConsoleRawReader { handle }) + Ok(ConsoleRawReader { + handle, + enable_bracketed_paste, + }) } } @@ -124,6 +129,9 @@ impl RawReader for ConsoleRawReader { let mut rec: wincon::INPUT_RECORD = unsafe { mem::zeroed() }; let mut count = 0; let mut surrogate = 0; + let mut esc_seq_len = 0; + let mut esc_seq = ['\0'; 5]; + loop { // TODO GetNumberOfConsoleInputEvents check(unsafe { consoleapi::ReadConsoleInputW(self.handle, &mut rec, 1, &mut count) })?; @@ -203,7 +211,7 @@ impl RawReader for ConsoleRawReader { K::UnknownEscSeq } }; - let key = if key_code != K::UnknownEscSeq { + let mut key = if key_code != K::UnknownEscSeq { KeyEvent(key_code, mods) } else if utf16 == 27 { KeyEvent(K::Esc, mods) // FIXME dead code ? @@ -225,13 +233,78 @@ impl RawReader for ConsoleRawReader { let c = rc?; KeyEvent::new(c, mods) }; + debug!(target: "rustyline", "wVirtualKeyCode: {:#x}, utf16: {:#x}, dwControlKeyState: {:#x} => key: {:?}", key_event.wVirtualKeyCode, utf16, key_event.dwControlKeyState, key); + + if self.enable_bracketed_paste { + if esc_seq_len == 0 && key == KeyEvent(K::Esc, M::NONE) { + esc_seq[esc_seq_len] = '\x1b'; + esc_seq_len += 1; + continue; + } else if esc_seq_len > 0 { + match (esc_seq, key) { + ( + ['\x1b', '\0', '\0', '\0', '\0'], + KeyEvent(K::Char(ch @ '['), M::NONE), + ) + | (['\x1b', '[', '\0', '\0', '\0'], KeyEvent(K::Char(ch @ '2'), M::NONE)) + | (['\x1b', '[', '2', '\0', '\0'], KeyEvent(K::Char(ch @ '0'), M::NONE)) + | (['\x1b', '[', '2', '0', '\0'], KeyEvent(K::Char(ch @ '0'), M::NONE)) + | (['\x1b', '[', '2', '0', '\0'], KeyEvent(K::Char(ch @ '1'), M::NONE)) => { + esc_seq[esc_seq_len] = ch; + esc_seq_len += 1; + continue; + } + (['\x1b', '[', '2', '0', '0'], KeyEvent(K::Char('~'), M::NONE)) => { + debug!(target: "rustyline", "Bracketed paste start"); + key = KeyEvent(K::BracketedPasteStart, M::NONE); + } + (['\x1b', '[', '2', '0', '1'], KeyEvent(K::Char('~'), M::NONE)) => { + debug!(target: "rustyline", "Bracketed paste end"); + key = KeyEvent(K::BracketedPasteEnd, M::NONE); + } + (_, KeyEvent(K::Char(ch), M::NONE)) => { + debug!(target: "rustyline", "unsupported esc sequence: \\E{}{}", esc_seq[1..esc_seq_len].iter().cloned().collect::(), ch); + key = KeyEvent(K::UnknownEscSeq, M::NONE); + } + _ => { + debug!(target: "rustyline", "unsupported esc sequence: \\E{}", esc_seq[1..esc_seq_len].iter().cloned().collect::()); + key = KeyEvent(K::UnknownEscSeq, M::NONE); + } + } + } + } + return Ok(key); } } fn read_pasted_text(&mut self) -> Result { - Ok(clipboard_win::get_clipboard_string()?) + if self.enable_bracketed_paste { + let mut buffer = String::new(); + + loop { + match self.next_key(true)? { + KeyEvent(K::BracketedPasteEnd, _) => { + let buffer = buffer.replace("\r\n", "\n"); + let buffer = buffer.replace('\r', "\n"); + return Ok(buffer); + } + KeyEvent(K::Char(ch), M::NONE) => { + buffer.push(ch); + } + KeyEvent(K::Char('M'), M::CTRL) => { + buffer.push('\r'); + } + KeyEvent(K::Char('J'), M::CTRL) => { + buffer.push('\n'); + } + _ => (), + } + } + } else { + Ok(clipboard_win::get_clipboard_string()?) + } } fn find_binding(&self, _: &KeyEvent) -> Option { @@ -624,7 +697,6 @@ impl Term for Console { raw |= wincon::ENABLE_INSERT_MODE; raw |= wincon::ENABLE_QUICK_EDIT_MODE; raw |= wincon::ENABLE_WINDOW_INPUT; - check(unsafe { consoleapi::SetConsoleMode(self.stdin_handle, raw) })?; let mut out = None; let original_stdstream_mode = if self.stdstream_isatty { @@ -658,14 +730,18 @@ impl Term for Console { debug!(target: "rustyline", "ansi_colors_supported: {}", self.ansi_colors_supported); } if self.ansi_colors_supported && self.enable_bracketed_paste { + raw |= wincon::ENABLE_VIRTUAL_TERMINAL_INPUT; write_and_flush(self.stream_type, BRACKETED_PASTE_ON)?; out = Some(self.stream_type); + debug!(target: "rustyline", "Turned bracketed paste on"); } Some(original_stdstream_mode) } else { None }; + check(unsafe { consoleapi::SetConsoleMode(self.stdin_handle, raw) })?; + Ok(( ConsoleMode { original_stdin_mode, @@ -679,7 +755,7 @@ impl Term for Console { } fn create_reader(&self, _: &Config, _: ConsoleKeyMap) -> Result { - ConsoleRawReader::create() + ConsoleRawReader::create(self.enable_bracketed_paste) } fn create_writer(&self) -> ConsoleRenderer { From 5c48c16b76c871c6b3110ed529ca777e1811d9d5 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 6 Feb 2022 16:12:06 +0800 Subject: [PATCH 2/5] Add escape codes for Windows bracketed paste mode. --- src/config.rs | 2 +- src/tty/windows.rs | 250 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 224 insertions(+), 28 deletions(-) diff --git a/src/config.rs b/src/config.rs index f7bb1e47d..8833fa257 100644 --- a/src/config.rs +++ b/src/config.rs @@ -211,7 +211,7 @@ impl Default for Config { tab_stop: 8, indent_size: 2, check_cursor_position: false, - enable_bracketed_paste: true, + enable_bracketed_paste: !cfg!(windows), } } } diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 37e1a442a..3cceb6e5c 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -21,7 +21,7 @@ use super::{ }; use crate::config::{BellStyle, ColorMode, Config, OutputStreamType}; use crate::highlight::Highlighter; -use crate::keys::{KeyCode as K, KeyEvent, Modifiers as M}; +use crate::keys::{KeyCode as K, KeyEvent as E, Modifiers as M}; use crate::layout::{Layout, Position}; use crate::line_buffer::LineBuffer; use crate::{error, Cmd, Result}; @@ -30,6 +30,41 @@ const STDIN_FILENO: DWORD = winbase::STD_INPUT_HANDLE; const STDOUT_FILENO: DWORD = winbase::STD_OUTPUT_HANDLE; const STDERR_FILENO: DWORD = winbase::STD_ERROR_HANDLE; +const XX: char = '\0'; +const ESC: char = '\x1b'; + +const UP: char = 'A'; +const DOWN: char = 'B'; +const RIGHT: char = 'C'; +const LEFT: char = 'D'; +const END: char = 'F'; +const HOME: char = 'H'; +const INS: char = '2'; +const DEL: char = '3'; +const PGUP: char = '5'; +const PGDN: char = '6'; + +const SHIFT: char = '2'; +const ALT: char = '3'; +const ALT_SHIFT: char = '4'; +const CTRL: char = '5'; +const CTRL_SHIFT: char = '6'; +const CTRL_ALT: char = '7'; +const CTRL_ALT_SHIFT: char = '8'; + +fn map_escape_meta(ch: char) -> M { + match ch { + SHIFT => M::SHIFT, + ALT => M::ALT, + ALT_SHIFT => M::ALT_SHIFT, + CTRL => M::CTRL, + CTRL_SHIFT => M::CTRL_SHIFT, + CTRL_ALT => M::CTRL_ALT, + CTRL_ALT_SHIFT => M::CTRL_ALT_SHIFT, + _ => unreachable!(), + } +} + fn get_std_handle(fd: DWORD) -> Result { let handle = unsafe { processenv::GetStdHandle(fd) }; if handle == INVALID_HANDLE_VALUE { @@ -119,7 +154,7 @@ impl ConsoleRawReader { } impl RawReader for ConsoleRawReader { - fn next_key(&mut self, _: bool) -> Result { + fn next_key(&mut self, _: bool) -> Result { use std::char::decode_utf16; use winapi::um::wincon::{ LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, @@ -130,7 +165,7 @@ impl RawReader for ConsoleRawReader { let mut count = 0; let mut surrogate = 0; let mut esc_seq_len = 0; - let mut esc_seq = ['\0'; 5]; + let mut esc_seq = [XX; 6]; loop { // TODO GetNumberOfConsoleInputEvents @@ -212,9 +247,9 @@ impl RawReader for ConsoleRawReader { } }; let mut key = if key_code != K::UnknownEscSeq { - KeyEvent(key_code, mods) + E(key_code, mods) } else if utf16 == 27 { - KeyEvent(K::Esc, mods) // FIXME dead code ? + E(K::Esc, mods) // FIXME dead code ? } else { if (0xD800..0xDC00).contains(&utf16) { surrogate = utf16; @@ -231,45 +266,206 @@ impl RawReader for ConsoleRawReader { return Err(error::ReadlineError::Eof); }; let c = rc?; - KeyEvent::new(c, mods) + E::new(c, mods) }; debug!(target: "rustyline", "wVirtualKeyCode: {:#x}, utf16: {:#x}, dwControlKeyState: {:#x} => key: {:?}", key_event.wVirtualKeyCode, utf16, key_event.dwControlKeyState, key); if self.enable_bracketed_paste { - if esc_seq_len == 0 && key == KeyEvent(K::Esc, M::NONE) { - esc_seq[esc_seq_len] = '\x1b'; + if esc_seq_len == 0 && key == E(K::Esc, M::NONE) { + esc_seq[esc_seq_len] = ESC; esc_seq_len += 1; continue; } else if esc_seq_len > 0 { match (esc_seq, key) { - ( - ['\x1b', '\0', '\0', '\0', '\0'], - KeyEvent(K::Char(ch @ '['), M::NONE), + ([ESC, XX, XX, XX, XX, XX], E(K::Char(ch @ ('[' | 'O')), M::NONE)) + | ([ESC, '[', XX, XX, XX, XX], E(K::Char(ch @ ('1' | '2')), M::NONE)) + | ([ESC, '[', '1', XX, XX, XX], E(K::Char(ch @ ';'), M::NONE)) + | ([ESC, '[', '1', ';', XX, XX], E(K::Char(ch @ '2'..='8'), M::NONE)) + | ( + [ESC, '[', '1', XX, XX, XX], + E(K::Char(ch @ ('5' | '7' | '8' | '9')), M::NONE), + ) + | ( + [ESC, '[', '2', XX, XX, XX], + E(K::Char(ch @ ('0' | '1' | '3' | '4')), M::NONE), ) - | (['\x1b', '[', '\0', '\0', '\0'], KeyEvent(K::Char(ch @ '2'), M::NONE)) - | (['\x1b', '[', '2', '\0', '\0'], KeyEvent(K::Char(ch @ '0'), M::NONE)) - | (['\x1b', '[', '2', '0', '\0'], KeyEvent(K::Char(ch @ '0'), M::NONE)) - | (['\x1b', '[', '2', '0', '\0'], KeyEvent(K::Char(ch @ '1'), M::NONE)) => { + | ( + [ESC, '[', '1', '5' | '7' | '8' | '9', XX, XX] + | [ESC, '[', '2', '0' | '1' | '3' | '4', XX, XX], + E(K::Char(ch @ ';'), M::NONE), + ) + | ( + [ESC, '[', '1', '5' | '7' | '8' | '9', ';', XX] + | [ESC, '[', '2', '0' | '1' | '3' | '4', ';', XX], + E(K::Char(ch @ ('2'..='8')), M::NONE), + ) + | ([ESC, '[', '2', '0', XX, XX], E(K::Char(ch @ '0'), M::NONE)) + | ([ESC, '[', '2', '0', XX, XX], E(K::Char(ch @ '1'), M::NONE)) => { esc_seq[esc_seq_len] = ch; esc_seq_len += 1; continue; } - (['\x1b', '[', '2', '0', '0'], KeyEvent(K::Char('~'), M::NONE)) => { + // \E[... + ( + [ESC, '[', XX, XX, XX, XX], + E( + K::Char( + ch @ (UP | DOWN | RIGHT | LEFT | END | HOME | DEL | INS | PGUP + | PGDN), + ), + M::NONE, + ), + ) => { + key = E( + match ch { + UP => K::Up, + DOWN => K::Down, + RIGHT => K::Right, + LEFT => K::Left, + END => K::End, + HOME => K::Home, + DEL => K::Delete, + INS => K::Insert, + PGUP => K::PageUp, + PGDN => K::PageDown, + _ => unreachable!(), + }, + M::NONE, + ); + debug!(target: "rustyline", "Key = {:?}", key); + } + // \E[1;{2345678}... + ( + [ESC, '[', '1', ';', meta @ ('2'..='8'), XX], + E( + K::Char( + ch @ (UP + | DOWN + | RIGHT + | LEFT + | END + | HOME + | DEL + | INS + | PGUP + | PGDN + | 'p'..='y' + | 'P'..='S'), + ), + M::NONE, + ), + ) => { + key = E( + match ch { + UP => K::Up, + DOWN => K::Down, + RIGHT => K::Right, + LEFT => K::Left, + END => K::End, + HOME => K::Home, + DEL => K::Delete, + INS => K::Insert, + PGUP => K::PageUp, + PGDN => K::PageDown, + 'P' => K::F(1), + 'Q' => K::F(2), + 'R' => K::F(3), + 'S' => K::F(4), + 'p' => K::Char('0'), + 'q' => K::Char('1'), + 'r' => K::Char('2'), + 's' => K::Char('3'), + 't' => K::Char('4'), + 'u' => K::Char('5'), + 'v' => K::Char('6'), + 'w' => K::Char('7'), + 'x' => K::Char('8'), + 'y' => K::Char('9'), + _ => unreachable!(), + }, + map_escape_meta(meta), + ); + debug!(target: "rustyline", "Key = {:?}", key); + } + // \EO{PQRS} + ( + [ESC, 'O', XX, XX, XX, XX], + E(K::Char(ch @ ('P' | 'Q' | 'R' | 'S')), M::NONE), + ) => { + key = E( + match ch { + 'P' => K::F(1), + 'Q' => K::F(2), + 'R' => K::F(3), + 'S' => K::F(4), + _ => unreachable!(), + }, + M::NONE, + ); + debug!(target: "rustyline", "Key = {:?}", key); + } + // \E[1{5789}~ or \E[2{0134}~ + ( + [ESC, '[', x @ '1', ch @ ('5' | '7' | '8' | '9'), XX, XX] + | [ESC, '[', x @ '2', ch @ ('0' | '1' | '3' | '4'), XX, XX], + E(K::Char('~'), M::NONE), + ) => { + key = E( + match (x, ch) { + ('1', '5') => K::F(5), + ('1', '7') => K::F(6), + ('1', '8') => K::F(7), + ('1', '9') => K::F(8), + ('2', '0') => K::F(9), + ('2', '1') => K::F(10), + ('2', '3') => K::F(11), + ('2', '4') => K::F(12), + _ => unreachable!(), + }, + M::NONE, + ); + debug!(target: "rustyline", "Key = {:?}", key); + } + // \E[1{5789};{2345678} or \E[2{0134};{2345678} + ( + [ESC, '[', x @ '1', ch @ ('5' | '7' | '8' | '9'), ';', meta @ ('2'..='8')] + | [ESC, '[', x @ '2', ch @ ('0' | '1' | '3' | '4'), ';', meta @ ('2'..='8')], + E(K::Char('~'), M::NONE), + ) => { + key = E( + match (x, ch) { + ('1', '5') => K::F(5), + ('1', '7') => K::F(6), + ('1', '8') => K::F(7), + ('1', '9') => K::F(8), + ('2', '0') => K::F(9), + ('2', '1') => K::F(10), + ('2', '3') => K::F(11), + ('2', '4') => K::F(12), + _ => unreachable!(), + }, + map_escape_meta(meta), + ); + debug!(target: "rustyline", "Key = {:?}", key); + } + // \E[200~ + ([ESC, '[', '2', '0', '0', XX], E(K::Char('~'), M::NONE)) => { debug!(target: "rustyline", "Bracketed paste start"); - key = KeyEvent(K::BracketedPasteStart, M::NONE); + key = E(K::BracketedPasteStart, M::NONE); } - (['\x1b', '[', '2', '0', '1'], KeyEvent(K::Char('~'), M::NONE)) => { + // \E[201~ + ([ESC, '[', '2', '0', '1', XX], E(K::Char('~'), M::NONE)) => { debug!(target: "rustyline", "Bracketed paste end"); - key = KeyEvent(K::BracketedPasteEnd, M::NONE); + key = E(K::BracketedPasteEnd, M::NONE); } - (_, KeyEvent(K::Char(ch), M::NONE)) => { + (_, E(K::Char(ch), M::NONE)) => { debug!(target: "rustyline", "unsupported esc sequence: \\E{}{}", esc_seq[1..esc_seq_len].iter().cloned().collect::(), ch); - key = KeyEvent(K::UnknownEscSeq, M::NONE); + key = E(K::UnknownEscSeq, M::NONE); } _ => { debug!(target: "rustyline", "unsupported esc sequence: \\E{}", esc_seq[1..esc_seq_len].iter().cloned().collect::()); - key = KeyEvent(K::UnknownEscSeq, M::NONE); + key = E(K::UnknownEscSeq, M::NONE); } } } @@ -285,18 +481,18 @@ impl RawReader for ConsoleRawReader { loop { match self.next_key(true)? { - KeyEvent(K::BracketedPasteEnd, _) => { + E(K::BracketedPasteEnd, _) => { let buffer = buffer.replace("\r\n", "\n"); let buffer = buffer.replace('\r', "\n"); return Ok(buffer); } - KeyEvent(K::Char(ch), M::NONE) => { + E(K::Char(ch), M::NONE) => { buffer.push(ch); } - KeyEvent(K::Char('M'), M::CTRL) => { + E(K::Char('M'), M::CTRL) => { buffer.push('\r'); } - KeyEvent(K::Char('J'), M::CTRL) => { + E(K::Char('J'), M::CTRL) => { buffer.push('\n'); } _ => (), @@ -307,7 +503,7 @@ impl RawReader for ConsoleRawReader { } } - fn find_binding(&self, _: &KeyEvent) -> Option { + fn find_binding(&self, _: &E) -> Option { None } } From 9e73611ba5287e85e7acfeb52eacf2c3cb13fb4f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 6 Feb 2022 17:34:34 +0800 Subject: [PATCH 3/5] Extract escape code building into separate type. --- src/tty/windows.rs | 416 ++++++++++++++++++++++++--------------------- 1 file changed, 219 insertions(+), 197 deletions(-) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 3cceb6e5c..d5cb3f068 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -164,8 +164,7 @@ impl RawReader for ConsoleRawReader { let mut rec: wincon::INPUT_RECORD = unsafe { mem::zeroed() }; let mut count = 0; let mut surrogate = 0; - let mut esc_seq_len = 0; - let mut esc_seq = [XX; 6]; + let mut esc = EscapeCodeBuilder::new(); loop { // TODO GetNumberOfConsoleInputEvents @@ -272,202 +271,10 @@ impl RawReader for ConsoleRawReader { debug!(target: "rustyline", "wVirtualKeyCode: {:#x}, utf16: {:#x}, dwControlKeyState: {:#x} => key: {:?}", key_event.wVirtualKeyCode, utf16, key_event.dwControlKeyState, key); if self.enable_bracketed_paste { - if esc_seq_len == 0 && key == E(K::Esc, M::NONE) { - esc_seq[esc_seq_len] = ESC; - esc_seq_len += 1; + if let Some(k) = esc.on_key(key) { + key = k; + } else { continue; - } else if esc_seq_len > 0 { - match (esc_seq, key) { - ([ESC, XX, XX, XX, XX, XX], E(K::Char(ch @ ('[' | 'O')), M::NONE)) - | ([ESC, '[', XX, XX, XX, XX], E(K::Char(ch @ ('1' | '2')), M::NONE)) - | ([ESC, '[', '1', XX, XX, XX], E(K::Char(ch @ ';'), M::NONE)) - | ([ESC, '[', '1', ';', XX, XX], E(K::Char(ch @ '2'..='8'), M::NONE)) - | ( - [ESC, '[', '1', XX, XX, XX], - E(K::Char(ch @ ('5' | '7' | '8' | '9')), M::NONE), - ) - | ( - [ESC, '[', '2', XX, XX, XX], - E(K::Char(ch @ ('0' | '1' | '3' | '4')), M::NONE), - ) - | ( - [ESC, '[', '1', '5' | '7' | '8' | '9', XX, XX] - | [ESC, '[', '2', '0' | '1' | '3' | '4', XX, XX], - E(K::Char(ch @ ';'), M::NONE), - ) - | ( - [ESC, '[', '1', '5' | '7' | '8' | '9', ';', XX] - | [ESC, '[', '2', '0' | '1' | '3' | '4', ';', XX], - E(K::Char(ch @ ('2'..='8')), M::NONE), - ) - | ([ESC, '[', '2', '0', XX, XX], E(K::Char(ch @ '0'), M::NONE)) - | ([ESC, '[', '2', '0', XX, XX], E(K::Char(ch @ '1'), M::NONE)) => { - esc_seq[esc_seq_len] = ch; - esc_seq_len += 1; - continue; - } - // \E[... - ( - [ESC, '[', XX, XX, XX, XX], - E( - K::Char( - ch @ (UP | DOWN | RIGHT | LEFT | END | HOME | DEL | INS | PGUP - | PGDN), - ), - M::NONE, - ), - ) => { - key = E( - match ch { - UP => K::Up, - DOWN => K::Down, - RIGHT => K::Right, - LEFT => K::Left, - END => K::End, - HOME => K::Home, - DEL => K::Delete, - INS => K::Insert, - PGUP => K::PageUp, - PGDN => K::PageDown, - _ => unreachable!(), - }, - M::NONE, - ); - debug!(target: "rustyline", "Key = {:?}", key); - } - // \E[1;{2345678}... - ( - [ESC, '[', '1', ';', meta @ ('2'..='8'), XX], - E( - K::Char( - ch @ (UP - | DOWN - | RIGHT - | LEFT - | END - | HOME - | DEL - | INS - | PGUP - | PGDN - | 'p'..='y' - | 'P'..='S'), - ), - M::NONE, - ), - ) => { - key = E( - match ch { - UP => K::Up, - DOWN => K::Down, - RIGHT => K::Right, - LEFT => K::Left, - END => K::End, - HOME => K::Home, - DEL => K::Delete, - INS => K::Insert, - PGUP => K::PageUp, - PGDN => K::PageDown, - 'P' => K::F(1), - 'Q' => K::F(2), - 'R' => K::F(3), - 'S' => K::F(4), - 'p' => K::Char('0'), - 'q' => K::Char('1'), - 'r' => K::Char('2'), - 's' => K::Char('3'), - 't' => K::Char('4'), - 'u' => K::Char('5'), - 'v' => K::Char('6'), - 'w' => K::Char('7'), - 'x' => K::Char('8'), - 'y' => K::Char('9'), - _ => unreachable!(), - }, - map_escape_meta(meta), - ); - debug!(target: "rustyline", "Key = {:?}", key); - } - // \EO{PQRS} - ( - [ESC, 'O', XX, XX, XX, XX], - E(K::Char(ch @ ('P' | 'Q' | 'R' | 'S')), M::NONE), - ) => { - key = E( - match ch { - 'P' => K::F(1), - 'Q' => K::F(2), - 'R' => K::F(3), - 'S' => K::F(4), - _ => unreachable!(), - }, - M::NONE, - ); - debug!(target: "rustyline", "Key = {:?}", key); - } - // \E[1{5789}~ or \E[2{0134}~ - ( - [ESC, '[', x @ '1', ch @ ('5' | '7' | '8' | '9'), XX, XX] - | [ESC, '[', x @ '2', ch @ ('0' | '1' | '3' | '4'), XX, XX], - E(K::Char('~'), M::NONE), - ) => { - key = E( - match (x, ch) { - ('1', '5') => K::F(5), - ('1', '7') => K::F(6), - ('1', '8') => K::F(7), - ('1', '9') => K::F(8), - ('2', '0') => K::F(9), - ('2', '1') => K::F(10), - ('2', '3') => K::F(11), - ('2', '4') => K::F(12), - _ => unreachable!(), - }, - M::NONE, - ); - debug!(target: "rustyline", "Key = {:?}", key); - } - // \E[1{5789};{2345678} or \E[2{0134};{2345678} - ( - [ESC, '[', x @ '1', ch @ ('5' | '7' | '8' | '9'), ';', meta @ ('2'..='8')] - | [ESC, '[', x @ '2', ch @ ('0' | '1' | '3' | '4'), ';', meta @ ('2'..='8')], - E(K::Char('~'), M::NONE), - ) => { - key = E( - match (x, ch) { - ('1', '5') => K::F(5), - ('1', '7') => K::F(6), - ('1', '8') => K::F(7), - ('1', '9') => K::F(8), - ('2', '0') => K::F(9), - ('2', '1') => K::F(10), - ('2', '3') => K::F(11), - ('2', '4') => K::F(12), - _ => unreachable!(), - }, - map_escape_meta(meta), - ); - debug!(target: "rustyline", "Key = {:?}", key); - } - // \E[200~ - ([ESC, '[', '2', '0', '0', XX], E(K::Char('~'), M::NONE)) => { - debug!(target: "rustyline", "Bracketed paste start"); - key = E(K::BracketedPasteStart, M::NONE); - } - // \E[201~ - ([ESC, '[', '2', '0', '1', XX], E(K::Char('~'), M::NONE)) => { - debug!(target: "rustyline", "Bracketed paste end"); - key = E(K::BracketedPasteEnd, M::NONE); - } - (_, E(K::Char(ch), M::NONE)) => { - debug!(target: "rustyline", "unsupported esc sequence: \\E{}{}", esc_seq[1..esc_seq_len].iter().cloned().collect::(), ch); - key = E(K::UnknownEscSeq, M::NONE); - } - _ => { - debug!(target: "rustyline", "unsupported esc sequence: \\E{}", esc_seq[1..esc_seq_len].iter().cloned().collect::()); - key = E(K::UnknownEscSeq, M::NONE); - } - } } } @@ -489,6 +296,9 @@ impl RawReader for ConsoleRawReader { E(K::Char(ch), M::NONE) => { buffer.push(ch); } + E(K::Char('I'), M::CTRL) => { + buffer.push('\t'); + } E(K::Char('M'), M::CTRL) => { buffer.push('\r'); } @@ -508,6 +318,218 @@ impl RawReader for ConsoleRawReader { } } +struct EscapeCodeBuilder { + esc_seq_len: usize, + esc_seq: [char; 6], +} + +impl EscapeCodeBuilder { + fn new() -> Self { + Self { + esc_seq_len: 0, + esc_seq: [XX; 6], + } + } + + fn on_key(&mut self, key: E) -> Option { + if self.esc_seq_len == 0 { + return if key == E(K::Esc, M::NONE) { + self.esc_seq[self.esc_seq_len] = ESC; + self.esc_seq_len += 1; + None + } else { + Some(key) + }; + } + + match (self.esc_seq, key) { + // Incomplete + ([ESC, XX, XX, XX, XX, XX], E(K::Char(ch @ ('[' | 'O')), M::NONE)) + | ([ESC, '[', XX, XX, XX, XX], E(K::Char(ch @ ('1' | '2')), M::NONE)) + | ([ESC, '[', '1', XX, XX, XX], E(K::Char(ch @ ';'), M::NONE)) + | ([ESC, '[', '1', ';', XX, XX], E(K::Char(ch @ '2'..='8'), M::NONE)) + | ([ESC, '[', '1', XX, XX, XX], E(K::Char(ch @ ('5' | '7' | '8' | '9')), M::NONE)) + | ([ESC, '[', '2', XX, XX, XX], E(K::Char(ch @ ('0' | '1' | '3' | '4')), M::NONE)) + | ( + [ESC, '[', '1', '5' | '7' | '8' | '9', XX, XX] + | [ESC, '[', '2', '0' | '1' | '3' | '4', XX, XX], + E(K::Char(ch @ ';'), M::NONE), + ) + | ( + [ESC, '[', '1', '5' | '7' | '8' | '9', ';', XX] + | [ESC, '[', '2', '0' | '1' | '3' | '4', ';', XX], + E(K::Char(ch @ ('2'..='8')), M::NONE), + ) + | ([ESC, '[', '2', '0', XX, XX], E(K::Char(ch @ '0'), M::NONE)) + | ([ESC, '[', '2', '0', XX, XX], E(K::Char(ch @ '1'), M::NONE)) => { + self.esc_seq[self.esc_seq_len] = ch; + self.esc_seq_len += 1; + None + } + + // \E[... + ( + [ESC, '[', XX, XX, XX, XX], + E( + K::Char(ch @ (UP | DOWN | RIGHT | LEFT | END | HOME | DEL | INS | PGUP | PGDN)), + M::NONE, + ), + ) => { + let key = E( + match ch { + UP => K::Up, + DOWN => K::Down, + RIGHT => K::Right, + LEFT => K::Left, + END => K::End, + HOME => K::Home, + DEL => K::Delete, + INS => K::Insert, + PGUP => K::PageUp, + PGDN => K::PageDown, + _ => unreachable!(), + }, + M::NONE, + ); + debug!(target: "rustyline", "Key = {:?}", key); + Some(key) + } + // \E[1;{2345678}... + ( + [ESC, '[', '1', ';', meta @ ('2'..='8'), XX], + E( + K::Char( + ch @ (UP + | DOWN + | RIGHT + | LEFT + | END + | HOME + | DEL + | INS + | PGUP + | PGDN + | 'p'..='y' + | 'P'..='S'), + ), + M::NONE, + ), + ) => { + let key = E( + match ch { + UP => K::Up, + DOWN => K::Down, + RIGHT => K::Right, + LEFT => K::Left, + END => K::End, + HOME => K::Home, + DEL => K::Delete, + INS => K::Insert, + PGUP => K::PageUp, + PGDN => K::PageDown, + 'P' => K::F(1), + 'Q' => K::F(2), + 'R' => K::F(3), + 'S' => K::F(4), + 'p' => K::Char('0'), + 'q' => K::Char('1'), + 'r' => K::Char('2'), + 's' => K::Char('3'), + 't' => K::Char('4'), + 'u' => K::Char('5'), + 'v' => K::Char('6'), + 'w' => K::Char('7'), + 'x' => K::Char('8'), + 'y' => K::Char('9'), + _ => unreachable!(), + }, + map_escape_meta(meta), + ); + debug!(target: "rustyline", "Key = {:?}", key); + Some(key) + } + // \EO{PQRS} + ([ESC, 'O', XX, XX, XX, XX], E(K::Char(ch @ ('P' | 'Q' | 'R' | 'S')), M::NONE)) => { + let key = E( + match ch { + 'P' => K::F(1), + 'Q' => K::F(2), + 'R' => K::F(3), + 'S' => K::F(4), + _ => unreachable!(), + }, + M::NONE, + ); + debug!(target: "rustyline", "Key = {:?}", key); + Some(key) + } + // \E[1{5789}~ or \E[2{0134}~ + ( + [ESC, '[', x @ '1', ch @ ('5' | '7' | '8' | '9'), XX, XX] + | [ESC, '[', x @ '2', ch @ ('0' | '1' | '3' | '4'), XX, XX], + E(K::Char('~'), M::NONE), + ) => { + let key = E( + match (x, ch) { + ('1', '5') => K::F(5), + ('1', '7') => K::F(6), + ('1', '8') => K::F(7), + ('1', '9') => K::F(8), + ('2', '0') => K::F(9), + ('2', '1') => K::F(10), + ('2', '3') => K::F(11), + ('2', '4') => K::F(12), + _ => unreachable!(), + }, + M::NONE, + ); + debug!(target: "rustyline", "Key = {:?}", key); + Some(key) + } + // \E[1{5789};{2345678} or \E[2{0134};{2345678} + ( + [ESC, '[', x @ '1', ch @ ('5' | '7' | '8' | '9'), ';', meta @ ('2'..='8')] + | [ESC, '[', x @ '2', ch @ ('0' | '1' | '3' | '4'), ';', meta @ ('2'..='8')], + E(K::Char('~'), M::NONE), + ) => { + let key = E( + match (x, ch) { + ('1', '5') => K::F(5), + ('1', '7') => K::F(6), + ('1', '8') => K::F(7), + ('1', '9') => K::F(8), + ('2', '0') => K::F(9), + ('2', '1') => K::F(10), + ('2', '3') => K::F(11), + ('2', '4') => K::F(12), + _ => unreachable!(), + }, + map_escape_meta(meta), + ); + debug!(target: "rustyline", "Key = {:?}", key); + Some(key) + } + // \E[200~ + ([ESC, '[', '2', '0', '0', XX], E(K::Char('~'), M::NONE)) => { + debug!(target: "rustyline", "Bracketed paste start"); + Some(E(K::BracketedPasteStart, M::NONE)) + } + // \E[201~ + ([ESC, '[', '2', '0', '1', XX], E(K::Char('~'), M::NONE)) => { + debug!(target: "rustyline", "Bracketed paste end"); + Some(E(K::BracketedPasteEnd, M::NONE)) + } + (_, E(K::Char(ch), M::NONE)) => { + debug!(target: "rustyline", "unsupported esc sequence: \\E{}{}", self.esc_seq[1..self.esc_seq_len].iter().cloned().collect::(), ch); + Some(E(K::UnknownEscSeq, M::NONE)) + } + _ => { + debug!(target: "rustyline", "unsupported esc sequence: \\E{}", self.esc_seq[1..self.esc_seq_len].iter().cloned().collect::()); + Some(E(K::UnknownEscSeq, M::NONE)) + } + } + } +} pub struct ConsoleRenderer { out: OutputStreamType, handle: HANDLE, From 4bf3b02dc53c3fe19d1a5a6c47b76ffee0e48b2d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 6 Feb 2022 18:08:13 +0800 Subject: [PATCH 4/5] Fix Windows escape codes. --- src/tty/windows.rs | 70 +++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index d5cb3f068..27901b12a 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -346,22 +346,18 @@ impl EscapeCodeBuilder { // Incomplete ([ESC, XX, XX, XX, XX, XX], E(K::Char(ch @ ('[' | 'O')), M::NONE)) | ([ESC, '[', XX, XX, XX, XX], E(K::Char(ch @ ('1' | '2')), M::NONE)) + | ([ESC, '[', XX, XX, XX, XX], E(K::Char(ch @ (PGUP | PGDN | DEL /*| INS*/)), M::NONE)) | ([ESC, '[', '1', XX, XX, XX], E(K::Char(ch @ ';'), M::NONE)) | ([ESC, '[', '1', ';', XX, XX], E(K::Char(ch @ '2'..='8'), M::NONE)) | ([ESC, '[', '1', XX, XX, XX], E(K::Char(ch @ ('5' | '7' | '8' | '9')), M::NONE)) | ([ESC, '[', '2', XX, XX, XX], E(K::Char(ch @ ('0' | '1' | '3' | '4')), M::NONE)) - | ( - [ESC, '[', '1', '5' | '7' | '8' | '9', XX, XX] - | [ESC, '[', '2', '0' | '1' | '3' | '4', XX, XX], - E(K::Char(ch @ ';'), M::NONE), - ) - | ( - [ESC, '[', '1', '5' | '7' | '8' | '9', ';', XX] - | [ESC, '[', '2', '0' | '1' | '3' | '4', ';', XX], - E(K::Char(ch @ ('2'..='8')), M::NONE), - ) - | ([ESC, '[', '2', '0', XX, XX], E(K::Char(ch @ '0'), M::NONE)) - | ([ESC, '[', '2', '0', XX, XX], E(K::Char(ch @ '1'), M::NONE)) => { + | ([ESC, '[', PGUP | PGDN | DEL | INS, XX, XX, XX], E(K::Char(ch @ ';'), M::NONE)) + | ([ESC, '[', PGUP | PGDN | DEL | INS, ';', XX, XX], E(K::Char(ch @ '2'..='8'), M::NONE)) + | ([ESC, '[', '1', '5' | '7' | '8' | '9', XX, XX], E(K::Char(ch @ ';'), M::NONE)) + | ([ESC, '[', '2', '0' | '1' | '3' | '4', XX, XX], E(K::Char(ch @ ';'), M::NONE)) + | ([ESC, '[', '1', '5' | '7' | '8' | '9', ';', XX], E(K::Char(ch @ ('2'..='8')), M::NONE)) + | ([ESC, '[', '2', '0' | '1' | '3' | '4', ';', XX], E(K::Char(ch @ ('2'..='8')), M::NONE)) + | ([ESC, '[', '2', '0', XX, XX], E(K::Char(ch @ ('0' | '1')), M::NONE)) => { self.esc_seq[self.esc_seq_len] = ch; self.esc_seq_len += 1; None @@ -370,10 +366,7 @@ impl EscapeCodeBuilder { // \E[... ( [ESC, '[', XX, XX, XX, XX], - E( - K::Char(ch @ (UP | DOWN | RIGHT | LEFT | END | HOME | DEL | INS | PGUP | PGDN)), - M::NONE, - ), + E(K::Char(ch @ (UP | DOWN | RIGHT | LEFT | END | HOME)), M::NONE), ) => { let key = E( match ch { @@ -383,6 +376,17 @@ impl EscapeCodeBuilder { LEFT => K::Left, END => K::End, HOME => K::Home, + _ => unreachable!(), + }, + M::NONE, + ); + debug!(target: "rustyline", "Key = {:?}", key); + Some(key) + } + // \E[...~ + ([ESC, '[', ch @ (PGUP | PGDN | DEL | INS), XX, XX, XX], E(K::Char('~'), M::NONE)) => { + let key = E( + match ch { DEL => K::Delete, INS => K::Insert, PGUP => K::PageUp, @@ -399,18 +403,9 @@ impl EscapeCodeBuilder { [ESC, '[', '1', ';', meta @ ('2'..='8'), XX], E( K::Char( - ch @ (UP - | DOWN - | RIGHT - | LEFT - | END - | HOME - | DEL - | INS - | PGUP - | PGDN - | 'p'..='y' - | 'P'..='S'), + ch @ (UP | DOWN | RIGHT | LEFT | END | HOME | PGUP | PGDN | DEL | INS) + | ch @ 'p'..='y' + | ch @ 'P'..='S', ), M::NONE, ), @@ -509,6 +504,24 @@ impl EscapeCodeBuilder { debug!(target: "rustyline", "Key = {:?}", key); Some(key) } + // \E[...;{2345678} + ( + [ESC, '[', ch, ';', meta @ ('2'..='8'), XX], + E(K::Char('~'), M::NONE), + ) => { + let key = E( + match ch { + DEL => K::Delete, + INS => K::Insert, + PGUP => K::PageUp, + PGDN => K::PageDown, + _ => unreachable!(), + }, + map_escape_meta(meta), + ); + debug!(target: "rustyline", "Key = {:?}", key); + Some(key) + } // \E[200~ ([ESC, '[', '2', '0', '0', XX], E(K::Char('~'), M::NONE)) => { debug!(target: "rustyline", "Bracketed paste start"); @@ -530,6 +543,7 @@ impl EscapeCodeBuilder { } } } + pub struct ConsoleRenderer { out: OutputStreamType, handle: HANDLE, From 43f1c1ae23cbe66eb1d4559e412a95bd8a50f143 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 6 Feb 2022 21:05:25 +0800 Subject: [PATCH 5/5] Detect lone Escape. --- src/tty/windows.rs | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 27901b12a..b27c064e4 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -271,10 +271,38 @@ impl RawReader for ConsoleRawReader { debug!(target: "rustyline", "wVirtualKeyCode: {:#x}, utf16: {:#x}, dwControlKeyState: {:#x} => key: {:?}", key_event.wVirtualKeyCode, utf16, key_event.dwControlKeyState, key); if self.enable_bracketed_paste { - if let Some(k) = esc.on_key(key) { - key = k; + let esc_processing = if !esc.is_processing() && key == E(K::Esc, M::NONE) { + // Check if it is a stand-alone Escape + check(unsafe { + consoleapi::GetNumberOfConsoleInputEvents(self.handle, &mut count) + })?; + if count > 0 { + check(unsafe { + consoleapi::PeekConsoleInputA(self.handle, &mut rec, 1, &mut count) + })?; + + if count > 0 && rec.EventType == wincon::KEY_EVENT { + let key_event = unsafe { rec.Event.KeyEvent() }; + // It is a stand-alone Escape if the next event is key-up of that Esc key! + key_event.bKeyDown != 0 || key_event.wVirtualKeyCode != 0x1b + } else { + // If the next event is not a key event then it is not an escape sequence + false + } + } else { + // If there is no next event, then that key-press is likely a stand-alone Escape + false + } } else { - continue; + true + }; + + if esc_processing { + if let Some(k) = esc.on_key(key) { + key = k; + } else { + continue; + } } } @@ -331,8 +359,12 @@ impl EscapeCodeBuilder { } } + fn is_processing(&self) -> bool { + self.esc_seq_len > 0 + } + fn on_key(&mut self, key: E) -> Option { - if self.esc_seq_len == 0 { + if !self.is_processing() { return if key == E(K::Esc, M::NONE) { self.esc_seq[self.esc_seq_len] = ESC; self.esc_seq_len += 1;