From 6354f649efbc53a8c314ea523163327fc07ba95a Mon Sep 17 00:00:00 2001 From: Darren Schroeder Date: Mon, 18 May 2020 13:48:13 -0500 Subject: [PATCH 01/10] Allow windows to use case insensitive path completion. --- src/completion.rs | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/completion.rs b/src/completion.rs index 5c46dcb237..ddc4a3e372 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -342,22 +342,40 @@ fn filename_complete( return Ok(entries); } + if cfg!(unix) {} // if any of the below IO operations have errors, just ignore them if let Ok(read_dir) = dir.read_dir() { for entry in read_dir { if let Ok(entry) = entry { if let Some(s) = entry.file_name().to_str() { - if s.starts_with(file_name) { - if let Ok(metadata) = fs::metadata(entry.path()) { - let mut path = String::from(dir_name) + s; - if metadata.is_dir() { - path.push(sep); - } - entries.push(Pair { - display: String::from(s), - replacement: escape(path, esc_char, break_chars, quote), - }); - } // else ignore PermissionDenied + if cfg!(unix) { + if s.starts_with(file_name) { + if let Ok(metadata) = fs::metadata(entry.path()) { + let mut path = String::from(dir_name) + s; + if metadata.is_dir() { + path.push(sep); + } + entries.push(Pair { + display: String::from(s), + replacement: escape(path, esc_char, break_chars, quote), + }); + } // else ignore PermissionDenied + } + } else { + // Let's do a case insesitive comparison for mac and windows + // For full unicode paths, we may have to use something like `caseless` + if s.to_lowercase().starts_with(&file_name.to_lowercase()) { + if let Ok(metadata) = fs::metadata(entry.path()) { + let mut path = String::from(dir_name) + s; + if metadata.is_dir() { + path.push(sep); + } + entries.push(Pair { + display: String::from(s), + replacement: escape(path, esc_char, break_chars, quote), + }); + } // else ignore PermissionDenied + } } } } From d135ffbf23d0b2c5f4c69a2c51293d92a59c1c9c Mon Sep 17 00:00:00 2001 From: Darren Schroeder Date: Mon, 18 May 2020 13:49:31 -0500 Subject: [PATCH 02/10] removed some debug code. --- src/completion.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/completion.rs b/src/completion.rs index ddc4a3e372..0511e45567 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -342,7 +342,6 @@ fn filename_complete( return Ok(entries); } - if cfg!(unix) {} // if any of the below IO operations have errors, just ignore them if let Ok(read_dir) = dir.read_dir() { for entry in read_dir { From 52a3d4840285aaa614805f0f11ac8b17e05396ce Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Mon, 29 Jun 2020 14:43:14 -0500 Subject: [PATCH 03/10] Use crate to escape/unescape history. --- Cargo.toml | 1 + src/history.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b1059edc0..8d4e671f57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ log = "0.4" unicode-width = "0.1" unicode-segmentation = "1.0" memchr = "2.0" +snailquote = "0.3.0" [target.'cfg(unix)'.dependencies] nix = "0.17" diff --git a/src/history.rs b/src/history.rs index ddf704720e..ac1704bd79 100644 --- a/src/history.rs +++ b/src/history.rs @@ -9,6 +9,7 @@ use std::path::Path; use super::Result; use crate::config::{Config, HistoryDuplicates}; +use snailquote::{escape, unescape}; /// Search direction #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -141,7 +142,8 @@ impl History { wtr.write_all(Self::FILE_VERSION_V2.as_bytes())?; for entry in &self.entries { wtr.write_all(b"\n")?; - wtr.write_all(entry.replace('\\', "\\\\").replace('\n', "\\n").as_bytes())?; + let esc = escape(entry); + wtr.write_all(&esc.as_bytes())?; } wtr.write_all(b"\n")?; // https://github.com/rust-lang/rust/issues/32677#issuecomment-204833485 @@ -170,7 +172,11 @@ impl History { } for line in lines { let line = if v2 { - line?.replace("\\n", "\n").replace("\\\\", "\\") + let li = match unescape(&line.unwrap()) { + Ok(s) => s, + _ => "".to_string() + }; + li } else { line? }; From dc7d30b5afc302e28ef1314fd1ecfdbfe0ef6d36 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Tue, 28 Jul 2020 16:58:08 -0500 Subject: [PATCH 04/10] Changes to allow Backspace changes --- examples/example.rs | 7 +++++-- src/history.rs | 1 - src/keys.rs | 6 ++++++ src/lib.rs | 19 +++++++++++++++++-- src/tty/windows.rs | 21 ++++++++++++++++++++- 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index 2b3363fcd3..a0f37ea626 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -6,7 +6,7 @@ use rustyline::error::ReadlineError; use rustyline::highlight::{Highlighter, MatchingBracketHighlighter}; use rustyline::hint::{Hinter, HistoryHinter}; use rustyline::validate::{self, MatchingBracketValidator, Validator}; -use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, KeyPress}; +use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, KeyPress, Movement, Word}; use rustyline_derive::Helper; #[derive(Helper)] @@ -83,7 +83,7 @@ fn main() -> rustyline::Result<()> { let config = Config::builder() .history_ignore_space(true) .completion_type(CompletionType::List) - .edit_mode(EditMode::Emacs) + .edit_mode(EditMode::Vi) .output_stream(OutputStreamType::Stdout) .build(); let h = MyHelper { @@ -97,6 +97,9 @@ fn main() -> rustyline::Result<()> { rl.set_helper(Some(h)); rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward); rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward); + rl.bind_sequence(KeyPress::ControlBackspace, Cmd::Kill(Movement::BackwardWord(1, Word::Vi))); + rl.bind_sequence(KeyPress::MetaBackspace, Cmd::Kill(Movement::BackwardChar(1))); + rl.bind_sequence(KeyPress::ShiftBackspace, Cmd::Kill(Movement::BackwardChar(1))); if rl.load_history("history.txt").is_err() { println!("No previous history."); } diff --git a/src/history.rs b/src/history.rs index a6866b0ae7..655a59c8d2 100644 --- a/src/history.rs +++ b/src/history.rs @@ -10,7 +10,6 @@ use std::path::Path; use super::Result; use crate::config::{Config, HistoryDuplicates}; -use snailquote::{escape, unescape}; /// Search direction #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/keys.rs b/src/keys.rs index bf066368f1..078faf9691 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -24,6 +24,8 @@ pub enum KeyPress { ControlRight, /// Ctrl-↑ ControlUp, + /// Control Backspace + ControlBackspace, /// Ctrl-char Ctrl(char), /// ⌦ @@ -46,6 +48,8 @@ pub enum KeyPress { Left, /// Escape-char or Alt-char Meta(char), + /// Alt Backspace + MetaBackspace, /// `KeyPress::Char('\0')` Null, /// ⇟ @@ -54,6 +58,8 @@ pub enum KeyPress { PageUp, /// → arrow key Right, + /// Shift Backspace + ShiftBackspace, /// Shift-↓ ShiftDown, /// Shift-← diff --git a/src/lib.rs b/src/lib.rs index 9caa9559c4..b9c24625a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,7 @@ use std::path::Path; use std::result; use std::sync::{Arc, Mutex, RwLock}; -use log::debug; +use log::{warn, debug}; use unicode_width::UnicodeWidthStr; use crate::tty::{RawMode, Renderer, Term, Terminal}; @@ -888,7 +888,22 @@ impl Editor { /// Bind a sequence to a command. pub fn bind_sequence(&mut self, key_seq: KeyPress, cmd: Cmd) -> Option { if let Ok(mut bindings) = self.custom_bindings.write() { - bindings.insert(key_seq, cmd) + let key = if let KeyPress::Char(ref c) = key_seq { + if c.is_control() { + keys::char_to_key_press(*c) + } else { + key_seq + } + } else if let KeyPress::Ctrl(ref c) = key_seq { + if c.is_control() { + keys::char_to_key_press(*c); + warn!(target: "rustyline", "KeyPress::Ctrl({:?}) may not work on unix", c) + } + key_seq + } else { + key_seq + }; + bindings.insert(key, cmd) } else { None } diff --git a/src/tty/windows.rs b/src/tty/windows.rs index ea499c7c25..7654f94b1a 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -179,6 +179,17 @@ impl RawReader for ConsoleRawReader { KeyPress::Down }); } + winuser::VK_BACK => { + return Ok(if ctrl { + KeyPress::ControlBackspace + } else if shift { + KeyPress::ShiftBackspace + } else if meta { + KeyPress::MetaBackspace + } else { + KeyPress::Backspace + }); + } winuser::VK_DELETE => return Ok(KeyPress::Delete), winuser::VK_HOME => return Ok(KeyPress::Home), winuser::VK_END => return Ok(KeyPress::End), @@ -220,13 +231,21 @@ impl RawReader for ConsoleRawReader { }; let c = rc?; if meta { - return Ok(KeyPress::Meta(c)); + let mut key = keys::char_to_key_press(c); + if key == KeyPress::Backspace && meta { + key = KeyPress::MetaBackspace; + } else { + key = KeyPress::Meta(c); + } + return Ok(key); } else { let mut key = keys::char_to_key_press(c); if key == KeyPress::Tab && shift { key = KeyPress::BackTab; } else if key == KeyPress::Char(' ') && ctrl { key = KeyPress::Ctrl(' '); + } else if key == KeyPress::Backspace && ctrl { + key = KeyPress::ControlBackspace; } return Ok(key); } From fee997956bda61aefb9fe73ff498acd4e8cdc913 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Tue, 28 Jul 2020 17:06:11 -0500 Subject: [PATCH 05/10] not sure why snailquote is still needed --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b99e630617..a7b5fcd608 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,6 @@ log = "0.4" unicode-width = "0.1" unicode-segmentation = "1.0" memchr = "2.0" -snailquote = "0.3.0" [target.'cfg(unix)'.dependencies] nix = "0.17" From 7d811ab3ca03a6138fc47257787c45228bed202d Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Wed, 29 Jul 2020 13:42:05 -0500 Subject: [PATCH 06/10] updates requested by gwenn --- examples/example.rs | 5 +---- src/keys.rs | 4 ++-- src/tty/windows.rs | 14 +------------- 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/examples/example.rs b/examples/example.rs index a0f37ea626..843c06c712 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -83,7 +83,7 @@ fn main() -> rustyline::Result<()> { let config = Config::builder() .history_ignore_space(true) .completion_type(CompletionType::List) - .edit_mode(EditMode::Vi) + .edit_mode(EditMode::Emacs) .output_stream(OutputStreamType::Stdout) .build(); let h = MyHelper { @@ -97,9 +97,6 @@ fn main() -> rustyline::Result<()> { rl.set_helper(Some(h)); rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward); rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward); - rl.bind_sequence(KeyPress::ControlBackspace, Cmd::Kill(Movement::BackwardWord(1, Word::Vi))); - rl.bind_sequence(KeyPress::MetaBackspace, Cmd::Kill(Movement::BackwardChar(1))); - rl.bind_sequence(KeyPress::ShiftBackspace, Cmd::Kill(Movement::BackwardChar(1))); if rl.load_history("history.txt").is_err() { println!("No previous history."); } diff --git a/src/keys.rs b/src/keys.rs index 078faf9691..9883b81fca 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -24,7 +24,7 @@ pub enum KeyPress { ControlRight, /// Ctrl-↑ ControlUp, - /// Control Backspace + /// Control Backspace (may not be able to be used on unix) ControlBackspace, /// Ctrl-char Ctrl(char), @@ -48,7 +48,7 @@ pub enum KeyPress { Left, /// Escape-char or Alt-char Meta(char), - /// Alt Backspace + /// Alt Backspace MetaBackspace, /// `KeyPress::Char('\0')` Null, diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 7654f94b1a..f4bcc060ea 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -179,17 +179,6 @@ impl RawReader for ConsoleRawReader { KeyPress::Down }); } - winuser::VK_BACK => { - return Ok(if ctrl { - KeyPress::ControlBackspace - } else if shift { - KeyPress::ShiftBackspace - } else if meta { - KeyPress::MetaBackspace - } else { - KeyPress::Backspace - }); - } winuser::VK_DELETE => return Ok(KeyPress::Delete), winuser::VK_HOME => return Ok(KeyPress::Home), winuser::VK_END => return Ok(KeyPress::End), @@ -230,8 +219,8 @@ impl RawReader for ConsoleRawReader { return Err(error::ReadlineError::Eof); }; let c = rc?; + let mut key = keys::char_to_key_press(c); if meta { - let mut key = keys::char_to_key_press(c); if key == KeyPress::Backspace && meta { key = KeyPress::MetaBackspace; } else { @@ -239,7 +228,6 @@ impl RawReader for ConsoleRawReader { } return Ok(key); } else { - let mut key = keys::char_to_key_press(c); if key == KeyPress::Tab && shift { key = KeyPress::BackTab; } else if key == KeyPress::Char(' ') && ctrl { From 815b2d0c90fa9583c245adc95839762e2f96b408 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Wed, 29 Jul 2020 13:46:07 -0500 Subject: [PATCH 07/10] also removed movement, word from example use --- examples/example.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/example.rs b/examples/example.rs index 843c06c712..2b3363fcd3 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -6,7 +6,7 @@ use rustyline::error::ReadlineError; use rustyline::highlight::{Highlighter, MatchingBracketHighlighter}; use rustyline::hint::{Hinter, HistoryHinter}; use rustyline::validate::{self, MatchingBracketValidator, Validator}; -use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, KeyPress, Movement, Word}; +use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, KeyPress}; use rustyline_derive::Helper; #[derive(Helper)] From f1b7b74686933fcfbd26b8dc03aa11f645c25925 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Wed, 29 Jul 2020 14:53:12 -0500 Subject: [PATCH 08/10] forgot shift, remove ctrl char_to_key_press --- src/lib.rs | 1 - src/tty/windows.rs | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b9c24625a0..92efd35da8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -896,7 +896,6 @@ impl Editor { } } else if let KeyPress::Ctrl(ref c) = key_seq { if c.is_control() { - keys::char_to_key_press(*c); warn!(target: "rustyline", "KeyPress::Ctrl({:?}) may not work on unix", c) } key_seq diff --git a/src/tty/windows.rs b/src/tty/windows.rs index f4bcc060ea..eeda3e6eed 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -234,6 +234,8 @@ impl RawReader for ConsoleRawReader { key = KeyPress::Ctrl(' '); } else if key == KeyPress::Backspace && ctrl { key = KeyPress::ControlBackspace; + } else if key == KeyPress::Backspace && shift { + key = KeyPress::ShiftBackspace; } return Ok(key); } From 1e0c73e6bdf499e3b5f457c8d475c1b01cf17029 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Thu, 30 Jul 2020 14:07:53 -0500 Subject: [PATCH 09/10] removed && meta since meta is already true --- src/tty/windows.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index eeda3e6eed..8196ce0309 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -221,7 +221,7 @@ impl RawReader for ConsoleRawReader { let c = rc?; let mut key = keys::char_to_key_press(c); if meta { - if key == KeyPress::Backspace && meta { + if key == KeyPress::Backspace { key = KeyPress::MetaBackspace; } else { key = KeyPress::Meta(c); From 4b2a29fbc8a59e299a69d7ae705f8b0123b99c82 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Sat, 1 Aug 2020 11:43:40 -0500 Subject: [PATCH 10/10] Update warning message --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 92efd35da8..303fd44c75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -896,7 +896,7 @@ impl Editor { } } else if let KeyPress::Ctrl(ref c) = key_seq { if c.is_control() { - warn!(target: "rustyline", "KeyPress::Ctrl({:?}) may not work on unix", c) + warn!(target: "rustyline", "KeyPress::Ctrl({:?}) will not work on unix", c) } key_seq } else {