Merge pull request #150 from gwenn/misc

Misc
diff --git a/Cargo.toml b/Cargo.toml
index 30a2fa1..a8ed288 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "rustyline"
-version = "2.0.1"
+version = "2.1.0"
 authors = ["Katsu Kawakami <kkawa1570@gmail.com>"]
 description = "Rustyline, a readline implementation based on Antirez's Linenoise"
 documentation = "http://docs.rs/rustyline"
diff --git a/README.md b/README.md
index 5468a87..ea66ccc 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,7 @@
 
 ```toml
 [dependencies]
-rustyline = "2.0.1"
+rustyline = "2.1.0"
 ```
 
 ## Features
diff --git a/TODO.md b/TODO.md
index d4cd263..75b2667 100644
--- a/TODO.md
+++ b/TODO.md
@@ -9,7 +9,7 @@
 Color
 - [X] ANSI Colors & Windows 10+
 - [ ] ANSI Colors & Windows <10 (https://docs.rs/console/0.6.1/console/fn.strip_ansi_codes.html ? https://github.com/mattn/go-colorable/blob/master/colorable_windows.go)
-- [ ] Syntax highlighting
+- [ ] Syntax highlighting (https://github.com/trishume/syntect/)
 - [ ] clicolors spec (https://docs.rs/console/0.6.1/console/fn.colors_enabled.html)
 
 Completion
@@ -76,6 +76,8 @@
 Unix
 - [ ] Terminfo (https://github.com/Stebalien/term)
 - [ ] [ncurses](https://crates.io/crates/ncurses) alternative backend ?
+- [ ] [bracketed paste mode](https://cirw.in/blog/bracketed-paste)
+- [ ] async stdin (https://github.com/Rufflewind/tokio-file-unix)
 
 Windows
 - [ ] is_atty is not working with cygwin/msys (https://github.com/softprops/atty works but then how to make `enable_raw_mode` works ?)
diff --git a/src/edit.rs b/src/edit.rs
index f9fcca8..7d706f5 100644
--- a/src/edit.rs
+++ b/src/edit.rs
@@ -32,6 +32,7 @@
     pub changes: Rc<RefCell<Changeset>>, // changes to line, for undo/redo
     pub hinter: Option<&'out Hinter>,
     pub highlighter: Option<&'out Highlighter>,
+    no_hint: bool, // `false` if an hint has been displayed
 }
 
 impl<'out, 'prompt> State<'out, 'prompt> {
@@ -57,6 +58,7 @@
             changes: Rc::new(RefCell::new(Changeset::new())),
             hinter,
             highlighter,
+            no_hint: true,
         }
     }
 
@@ -130,10 +132,12 @@
         Ok(())
     }
 
-    fn hint(&self) -> Option<String> {
+    fn hint(&mut self) -> Option<String> {
         if let Some(hinter) = self.hinter {
+            self.no_hint = false;
             hinter.hint(self.line.as_str(), self.line.pos())
         } else {
+            self.no_hint = true;
             None
         }
     }
@@ -186,10 +190,11 @@
         if let Some(push) = self.line.insert(ch, n) {
             if push {
                 let prompt_size = self.prompt_size;
+                let no_previous_hint = self.no_hint;
                 let hint = self.hint();
                 if n == 1
                     && self.cursor.col + ch.width().unwrap_or(0) < self.out.get_columns()
-                    && hint.is_none() // TODO refresh only current line
+                    && (hint.is_none() && no_previous_hint) // TODO refresh only current line
                     && !self.highlighter.map_or(true, |h| h.highlight_char(ch.encode_utf8(&mut self.byte_buffer)))
                 {
                     // Avoid a full update of the line in the trivial case.
@@ -322,13 +327,12 @@
     }
 
     pub fn edit_insert_text(&mut self, text: &str) -> Result<()> {
-        if !text.is_empty() {
-            let cursor = self.line.pos();
-            self.line.insert_str(cursor, text);
-            self.refresh_line()
-        } else {
-            Ok(())
+        if text.is_empty() {
+            return Ok(());
         }
+        let cursor = self.line.pos();
+        self.line.insert_str(cursor, text);
+        self.refresh_line()
     }
 
     pub fn edit_delete(&mut self, n: RepeatCount) -> Result<()> {
@@ -506,6 +510,7 @@
         changes: Rc::new(RefCell::new(Changeset::new())),
         hinter: None,
         highlighter: None,
+        no_hint: true,
     }
 }
 
diff --git a/src/error.rs b/src/error.rs
index f7c1abd..c49595b 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -10,6 +10,7 @@
 
 /// The error type for Rustyline errors that can arise from
 /// I/O related errors or Errno when using the nix-rust library
+/// #[non_exhaustive]
 #[derive(Debug)]
 pub enum ReadlineError {
     /// I/O Error
diff --git a/src/keymap.rs b/src/keymap.rs
index 877a12d..9841f78 100644
--- a/src/keymap.rs
+++ b/src/keymap.rs
@@ -5,13 +5,14 @@
 use super::Result;
 use config::Config;
 use config::EditMode;
-use consts::KeyPress;
+use keys::KeyPress;
 use tty::RawReader;
 
 /// The number of times one command should be repeated.
 pub type RepeatCount = usize;
 
 /// Commands
+/// #[non_exhaustive]
 #[derive(Debug, Clone, PartialEq)]
 pub enum Cmd {
     /// abort
@@ -479,20 +480,17 @@
         loop {
             try!(wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args)));
             let key = try!(rdr.next_key(false));
-            match key {
-                KeyPress::Char(digit @ '0'...'9') => {
-                    if self.num_args.abs() < 1000 {
-                        // shouldn't ever need more than 4 digits
-                        self.num_args = self
-                            .num_args
-                            .saturating_mul(10)
-                            .saturating_add(digit.to_digit(10).unwrap() as i16);
-                    }
+            if let KeyPress::Char(digit @ '0'...'9') = key {
+                if self.num_args.abs() < 1000 {
+                    // shouldn't ever need more than 4 digits
+                    self.num_args = self
+                        .num_args
+                        .saturating_mul(10)
+                        .saturating_add(digit.to_digit(10).unwrap() as i16);
                 }
-                _ => {
-                    try!(wrt.refresh_line());
-                    return Ok(key);
-                }
+            } else {
+                try!(wrt.refresh_line());
+                return Ok(key);
             };
         }
     }
diff --git a/src/consts.rs b/src/keys.rs
similarity index 94%
rename from src/consts.rs
rename to src/keys.rs
index 5841016..c355e5a 100644
--- a/src/consts.rs
+++ b/src/keys.rs
@@ -1,9 +1,11 @@
 //! Key constants
 
+/// #[non_exhaustive]
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum KeyPress {
     UnknownEscSeq,
-    Backspace,
+    Backspace, // Ctrl('H')
+    BackTab,
     Char(char),
     ControlDown,
     ControlLeft,
@@ -14,7 +16,7 @@
     Down,
     End,
     Enter, // Ctrl('M')
-    Esc,
+    Esc,   // Ctrl('[')
     F(u8),
     Home,
     Insert,
@@ -32,7 +34,7 @@
     Up,
 }
 
-#[allow(match_same_arms)]
+//#[allow(clippy::match_same_arms)]
 pub fn char_to_key_press(c: char) -> KeyPress {
     if !c.is_control() {
         return KeyPress::Char(c);
diff --git a/src/kill_ring.rs b/src/kill_ring.rs
index b5d8778..03a202f 100644
--- a/src/kill_ring.rs
+++ b/src/kill_ring.rs
@@ -41,34 +41,31 @@
 
     /// Add `text` to the kill-ring.
     pub fn kill(&mut self, text: &str, dir: Mode) {
-        match self.last_action {
-            Action::Kill => {
-                if self.slots.capacity() == 0 {
-                    // disabled
-                    return;
-                }
-                match dir {
-                    Mode::Append => self.slots[self.index].push_str(text),
-                    Mode::Prepend => self.slots[self.index].insert_str(0, text),
-                };
+        if let Action::Kill = self.last_action {
+            if self.slots.capacity() == 0 {
+                // disabled
+                return;
             }
-            _ => {
-                self.last_action = Action::Kill;
-                if self.slots.capacity() == 0 {
-                    // disabled
-                    return;
-                }
-                if self.index == self.slots.capacity() - 1 {
-                    // full
-                    self.index = 0;
-                } else if !self.slots.is_empty() {
-                    self.index += 1;
-                }
-                if self.index == self.slots.len() {
-                    self.slots.push(String::from(text))
-                } else {
-                    self.slots[self.index] = String::from(text);
-                }
+            match dir {
+                Mode::Append => self.slots[self.index].push_str(text),
+                Mode::Prepend => self.slots[self.index].insert_str(0, text),
+            };
+        } else {
+            self.last_action = Action::Kill;
+            if self.slots.capacity() == 0 {
+                // disabled
+                return;
+            }
+            if self.index == self.slots.capacity() - 1 {
+                // full
+                self.index = 0;
+            } else if !self.slots.is_empty() {
+                self.index += 1;
+            }
+            if self.index == self.slots.len() {
+                self.slots.push(String::from(text))
+            } else {
+                self.slots[self.index] = String::from(text);
             }
         }
     }
diff --git a/src/lib.rs b/src/lib.rs
index f6f68ea..6546ec1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -16,6 +16,8 @@
 //! }
 //! ```
 #![allow(unknown_lints)]
+// #![feature(non_exhaustive)]
+// #![feature(tool_lints)]
 
 extern crate dirs;
 extern crate libc;
@@ -33,13 +35,13 @@
 
 pub mod completion;
 pub mod config;
-mod consts;
 mod edit;
 pub mod error;
 pub mod highlight;
 pub mod hint;
 pub mod history;
 mod keymap;
+mod keys;
 mod kill_ring;
 pub mod line_buffer;
 mod undo;
@@ -58,13 +60,13 @@
 
 use completion::{longest_common_prefix, Candidate, Completer};
 pub use config::{ColorMode, CompletionType, Config, EditMode, HistoryDuplicates};
-pub use consts::KeyPress;
 use edit::State;
 use highlight::Highlighter;
 use hint::Hinter;
 use history::{Direction, History};
 pub use keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
 use keymap::{InputState, Refresher};
+pub use keys::KeyPress;
 use kill_ring::{KillRing, Mode};
 use line_buffer::WordAction;
 
@@ -675,7 +677,7 @@
     custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
 }
 
-#[allow(new_without_default)]
+//#[allow(clippy::new_without_default)]
 impl<H: Helper> Editor<H> {
     /// Create an editor with the default configuration
     pub fn new() -> Editor<H> {
diff --git a/src/line_buffer.rs b/src/line_buffer.rs
index e17fd62..ae3fe70 100644
--- a/src/line_buffer.rs
+++ b/src/line_buffer.rs
@@ -364,25 +364,19 @@
             sow = 0;
             let mut gj = gis.next();
             'inner: loop {
-                match gj {
-                    Some((j, y)) => {
-                        let gi = gis.next();
-                        match gi {
-                            Some((_, x)) => {
-                                if is_start_of_word(word_def, x, y) {
-                                    sow = j;
-                                    break 'inner;
-                                }
-                                gj = gi;
-                            }
-                            None => {
-                                break 'outer;
-                            }
+                if let Some((j, y)) = gj {
+                    let gi = gis.next();
+                    if let Some((_, x)) = gi {
+                        if is_start_of_word(word_def, x, y) {
+                            sow = j;
+                            break 'inner;
                         }
-                    }
-                    None => {
+                        gj = gi;
+                    } else {
                         break 'outer;
                     }
+                } else {
+                    break 'outer;
                 }
             }
         }
@@ -428,32 +422,26 @@
             wp = 0;
             gi = gis.next();
             'inner: loop {
-                match gi {
-                    Some((i, x)) => {
-                        let gj = gis.next();
-                        match gj {
-                            Some((j, y)) => {
-                                if at == At::Start && is_start_of_word(word_def, x, y) {
-                                    wp = j;
-                                    break 'inner;
-                                } else if at != At::Start && is_end_of_word(word_def, x, y) {
-                                    if word_def == Word::Emacs || at == At::AfterEnd {
-                                        wp = j;
-                                    } else {
-                                        wp = i;
-                                    }
-                                    break 'inner;
-                                }
-                                gi = gj;
+                if let Some((i, x)) = gi {
+                    let gj = gis.next();
+                    if let Some((j, y)) = gj {
+                        if at == At::Start && is_start_of_word(word_def, x, y) {
+                            wp = j;
+                            break 'inner;
+                        } else if at != At::Start && is_end_of_word(word_def, x, y) {
+                            if word_def == Word::Emacs || at == At::AfterEnd {
+                                wp = j;
+                            } else {
+                                wp = i;
                             }
-                            None => {
-                                break 'outer;
-                            }
+                            break 'inner;
                         }
-                    }
-                    None => {
+                        gi = gj;
+                    } else {
                         break 'outer;
                     }
+                } else {
+                    break 'outer;
                 }
             }
         }
diff --git a/src/test/common.rs b/src/test/common.rs
index 7fe8b17..29bde74 100644
--- a/src/test/common.rs
+++ b/src/test/common.rs
@@ -1,8 +1,8 @@
 ///! Basic commands tests.
 use super::{assert_cursor, assert_line, assert_line_with_initial, init_editor};
 use config::EditMode;
-use consts::KeyPress;
 use error::ReadlineError;
+use keys::KeyPress;
 
 #[test]
 fn home_key() {
diff --git a/src/test/emacs.rs b/src/test/emacs.rs
index 67a1b10..a4c738e 100644
--- a/src/test/emacs.rs
+++ b/src/test/emacs.rs
@@ -1,7 +1,7 @@
 //! Emacs specific key bindings
 use super::{assert_cursor, assert_history};
 use config::EditMode;
-use consts::KeyPress;
+use keys::KeyPress;
 
 #[test]
 fn ctrl_a() {
diff --git a/src/test/history.rs b/src/test/history.rs
index faa7a3f..dc3f8f0 100644
--- a/src/test/history.rs
+++ b/src/test/history.rs
@@ -1,7 +1,7 @@
 //! History related commands tests
 use super::assert_history;
 use config::EditMode;
-use consts::KeyPress;
+use keys::KeyPress;
 
 #[test]
 fn down_key() {
diff --git a/src/test/mod.rs b/src/test/mod.rs
index 1ce9881..8642b03 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -4,9 +4,9 @@
 use super::{Editor, Result};
 use completion::Completer;
 use config::{Config, EditMode};
-use consts::KeyPress;
 use edit::init_state;
 use keymap::{Cmd, InputState};
+use keys::KeyPress;
 use tty::Sink;
 
 mod common;
diff --git a/src/test/vi_cmd.rs b/src/test/vi_cmd.rs
index 1c1e4e9..d8ec793 100644
--- a/src/test/vi_cmd.rs
+++ b/src/test/vi_cmd.rs
@@ -1,7 +1,7 @@
 //! Vi command mode specific key bindings
 use super::{assert_cursor, assert_history};
 use config::EditMode;
-use consts::KeyPress;
+use keys::KeyPress;
 
 #[test]
 fn dollar() {
diff --git a/src/test/vi_insert.rs b/src/test/vi_insert.rs
index cf86680..fa3c881 100644
--- a/src/test/vi_insert.rs
+++ b/src/test/vi_insert.rs
@@ -1,7 +1,7 @@
 //! Vi insert mode specific key bindings
 use super::assert_cursor;
 use config::EditMode;
-use consts::KeyPress;
+use keys::KeyPress;
 
 #[test]
 fn insert_mode_by_default() {
diff --git a/src/tty/mod.rs b/src/tty/mod.rs
index d6767fc..e36ff80 100644
--- a/src/tty/mod.rs
+++ b/src/tty/mod.rs
@@ -4,8 +4,8 @@
 use unicode_width::UnicodeWidthStr;
 
 use config::{ColorMode, Config};
-use consts::KeyPress;
 use highlight::Highlighter;
+use keys::KeyPress;
 use line_buffer::LineBuffer;
 use Result;
 
diff --git a/src/tty/test.rs b/src/tty/test.rs
index 75844be..b1a47d9 100644
--- a/src/tty/test.rs
+++ b/src/tty/test.rs
@@ -5,9 +5,9 @@
 
 use super::{truncate, Position, RawMode, RawReader, Renderer, Term};
 use config::{ColorMode, Config};
-use consts::KeyPress;
 use error::ReadlineError;
 use highlight::Highlighter;
+use keys::KeyPress;
 use line_buffer::LineBuffer;
 use Result;
 
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
index 5a8b20f..ed7980f 100644
--- a/src/tty/unix.rs
+++ b/src/tty/unix.rs
@@ -15,9 +15,9 @@
 
 use super::{truncate, width, Position, RawMode, RawReader, Renderer, Term};
 use config::{ColorMode, Config};
-use consts::{self, KeyPress};
 use error;
 use highlight::Highlighter;
+use keys::{self, KeyPress};
 use line_buffer::LineBuffer;
 use Result;
 
@@ -27,7 +27,7 @@
 /// Unsupported Terminals that don't support RAW mode
 static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];
 
-#[allow(identity_conversion)]
+//#[allow(clippy::identity_conversion)]
 fn get_win_size() -> (usize, usize) {
     use std::mem::zeroed;
 
@@ -163,6 +163,20 @@
                     self.extended_escape(seq2)
                 }
             }
+        } else if seq2 == '[' {
+            let seq3 = try!(self.next_char());
+            // Linux console
+            Ok(match seq3 {
+                'A' => KeyPress::F(1),
+                'B' => KeyPress::F(2),
+                'C' => KeyPress::F(3),
+                'D' => KeyPress::F(4),
+                'E' => KeyPress::F(5),
+                _ => {
+                    debug!(target: "rustyline", "unsupported esc sequence: ESC [ [ {:?}", seq3);
+                    KeyPress::UnknownEscSeq
+                }
+            })
         } else {
             // ANSI
             Ok(match seq2 {
@@ -172,6 +186,7 @@
                 'D' => KeyPress::Left,  // kcub1
                 'F' => KeyPress::End,
                 'H' => KeyPress::Home, // khome
+                'Z' => KeyPress::BackTab,
                 _ => {
                     debug!(target: "rustyline", "unsupported esc sequence: ESC [ {:?}", seq2);
                     KeyPress::UnknownEscSeq
@@ -251,13 +266,13 @@
                         ('2', 'D') => KeyPress::ShiftLeft,
                         _ => {
                             debug!(target: "rustyline",
-                                   "unsupported esc sequence: ESC [ {} ; {} {}", seq2, seq4, seq5);
+                                   "unsupported esc sequence: ESC [ 1 ; {} {:?}", seq4, seq5);
                             KeyPress::UnknownEscSeq
                         }
                     })
                 } else {
                     debug!(target: "rustyline",
-                           "unsupported esc sequence: ESC [ {} ; {} {}", seq2, seq4, seq5);
+                           "unsupported esc sequence: ESC [ {} ; {} {:?}", seq2, seq4, seq5);
                     Ok(KeyPress::UnknownEscSeq)
                 }
             } else {
@@ -296,8 +311,8 @@
             'S' => KeyPress::F(4),  // kf4
             'a' => KeyPress::ControlUp,
             'b' => KeyPress::ControlDown,
-            'c' => KeyPress::ControlRight,
-            'd' => KeyPress::ControlLeft,
+            'c' => KeyPress::ControlRight, // rxvt
+            'd' => KeyPress::ControlLeft,  // rxvt
             _ => {
                 debug!(target: "rustyline", "unsupported esc sequence: ESC O {:?}", seq2);
                 KeyPress::UnknownEscSeq
@@ -310,7 +325,7 @@
     fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyPress> {
         let c = try!(self.next_char());
 
-        let mut key = consts::char_to_key_press(c);
+        let mut key = keys::char_to_key_press(c);
         if key == KeyPress::Esc {
             let timeout_ms = if single_esc_abort && self.timeout_ms == -1 {
                 0
@@ -559,7 +574,7 @@
 }
 
 static SIGWINCH_ONCE: sync::Once = sync::ONCE_INIT;
-static SIGWINCH: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;
+static SIGWINCH: atomic::AtomicBool = atomic::AtomicBool::new(false);
 
 fn install_sigwinch_handler() {
     SIGWINCH_ONCE.call_once(|| unsafe {
@@ -645,8 +660,9 @@
             | InputFlags::ISTRIP
             | InputFlags::IXON);
         // we don't want raw output, it turns newlines into straight linefeeds
-        // raw.c_oflag = raw.c_oflag & !(OutputFlags::OPOST); // disable all output
-        // processing
+        // disable all output processing
+        // raw.c_oflag = raw.c_oflag & !(OutputFlags::OPOST);
+
         // character-size mark (8 bits)
         raw.control_flags |= ControlFlags::CS8;
         // disable echoing, canonical mode, extended input processing and signals
diff --git a/src/tty/windows.rs b/src/tty/windows.rs
index 103ec44..65b699f 100644
--- a/src/tty/windows.rs
+++ b/src/tty/windows.rs
@@ -10,9 +10,9 @@
 
 use super::{truncate, Position, RawMode, RawReader, Renderer, Term};
 use config::{ColorMode, Config};
-use consts::{self, KeyPress};
 use error;
 use highlight::Highlighter;
+use keys::{self, KeyPress};
 use line_buffer::LineBuffer;
 use Result;
 
@@ -229,7 +229,13 @@
                 if meta {
                     return Ok(KeyPress::Meta(c));
                 } else {
-                    return Ok(consts::char_to_key_press(c));
+                    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(' ');
+                    }
+                    return Ok(key);
                 }
             }
         }