Repository: kkawakam/rustyline (rust)

Issue:
Crash when searching for a value
This code:
```
fn main() {
    use std::io::{stdin, stdout, Write};

    use rustyline::error::ReadlineError;
    use rustyline::Editor;

    loop {
        let mut editor = Editor::<()>::new();
        if editor.load_history("history.log").is_err() {
        }
        let result = editor.readline("Enter expression (ctrl-C to quit): ");
        match result {
            Ok(mut line) => {
                editor.add_history_entry(line.as_str());
                line = line.trim().to_string();
                if line.len() > 0 {
                    println!("You entered: {}", line);
                }
            },
            Err(ReadlineError::Interrupted) => {
                break
            },
            Err(ReadlineError::Eof) => {
            },
            Err(err) => {
                println!("Error: {:?}", err);
                break
            }
        }
        editor.save_history("history.log").unwrap();
    }
}
```

Fails when run if you do a `ctrl-R` to search for a previous value (which _is_ found). The failure enters when you press Enter on the found value. Console listing with backtrace (Ubuntu-x64, rust 1.38.0):

```
$ RUST_BACKTRACE=full target/debug/expr_repl
Enter expression (ctrl-C to quit): abc
You entered: abc
(reverse-i-search)`ab': abc           thread 'main' panicked at 'assertion failed: self.layout.cursor <= self.layout.end', /home/vinay/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-5.0.3/src/edit.rs:125:13
stack backtrace:
   0:     0x5565d5ee898b - backtrace::backtrace::libunwind::trace::h89fcc71e59e3bc5b
                               at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.34/src/backtrace/libunwind.rs:88
   1:     0x5565d5ee898b - backtrace::backtrace::trace_unsynchronized::h0bad9be1379e729a
                               at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.34/src/backtrace/mod.rs:66
   2:     0x5565d5ee898b - std::sys_common::backtrace::_print::hd3382a1f33c473da
                               at src/libstd/sys_common/backtrace.rs:47
   3:     0x5565d5ee898b - std::sys_common::backtrace::print::h0ec6f03cfb8e76a6
                               at src/libstd/sys_common/backtrace.rs:36
   4:     0x5565d5ee898b - std::panicking::default_hook::{{closure}}::h96cbf7b454e3f557
                               at src/libstd/panicking.rs:200
   5:     0x5565d5ee8666 - std::panicking::default_hook::h95a8f00337383d83
                               at src/libstd/panicking.rs:214
   6:     0x5565d5ee909d - std::panicking::rust_panic_with_hook::h92f98b46e22f14ed
                               at src/libstd/panicking.rs:477
   7:     0x5565d5ed3df5 - std::panicking::begin_panic::h96738e055e5f4d65
                               at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libstd/panicking.rs:411
   8:     0x5565d5e678ec - rustyline::edit::State<H>::move_cursor::hdcb71b31d457715f
                               at /home/vinay/projects/rsconfig/<::std::macros::panic macros>:3
   9:     0x5565d5e67e05 - rustyline::edit::State<H>::edit_move_end::hf8b47e6f67dc66dc
                               at /home/vinay/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-5.0.3/src/edit.rs:406
  10:     0x5565d5e5be9d - rustyline::readline_edit::ha6e91a48dad7739e
                               at /home/vinay/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-5.0.3/src/lib.rs:529
  11:     0x5565d5e55950 - rustyline::readline_raw::h8d10993a62be80cd
                               at /home/vinay/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-5.0.3/src/lib.rs:626
  12:     0x5565d5e5f2c2 - rustyline::Editor<H>::readline_with::h243448f87a7c18a2
                               at /home/vinay/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-5.0.3/src/lib.rs:755
  13:     0x5565d5e5f5ad - rustyline::Editor<H>::readline::h0d20b4b1450ecb3c
                               at /home/vinay/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-5.0.3/src/lib.rs:731
  14:     0x5565d5e62d19 - expr_repl::main::ha34c1f1010ec7764
                               at src/bin/expr_repl.rs:31
  15:     0x5565d5e72ab0 - std::rt::lang_start::{{closure}}::h39369991cd93ebad
                               at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libstd/rt.rs:64
  16:     0x5565d5ee8aa3 - std::rt::lang_start_internal::{{closure}}::h4e93c1949c7a1955
                               at src/libstd/rt.rs:49
  17:     0x5565d5ee8aa3 - std::panicking::try::do_call::h9440ccd4dc467eaa
                               at src/libstd/panicking.rs:296
  18:     0x5565d5eeaaba - __rust_maybe_catch_panic
                               at src/libpanic_unwind/lib.rs:80
  19:     0x5565d5ee95ad - std::panicking::try::hc046e7ee42ee744f
                               at src/libstd/panicking.rs:275
  20:     0x5565d5ee95ad - std::panic::catch_unwind::h27dfc457c200aee0
                               at src/libstd/panic.rs:394
  21:     0x5565d5ee95ad - std::rt::lang_start_internal::hea1b49a567afe309
                               at src/libstd/rt.rs:48
  22:     0x5565d5e72a89 - std::rt::lang_start::hbc1f50dfdb872b3d
                               at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libstd/rt.rs:64
  23:     0x5565d5e6328a - main
  24:     0x7f4952b44b97 - __libc_start_main
  25:     0x5565d5e520da - _start
  26:                0x0 - <unknown>
```



Pull request:
Fix reverse history search bug
Fix #294 

Patch:
diff --git a/src/edit.rs b/src/edit.rs
index 82eaa60..fb46cde 100644
--- a/src/edit.rs
+++ b/src/edit.rs
@@ -210,6 +210,10 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
             false
         }
     }
+
+    pub fn is_default_prompt(&self) -> bool {
+        self.layout.default_prompt
+    }
 }
 
 impl<'out, 'prompt, H: Helper> Refresher for State<'out, 'prompt, H> {
diff --git a/src/lib.rs b/src/lib.rs
index e052c61..45b3c25 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -526,12 +526,12 @@ fn readline_edit<H: Helper>(
                     editor.term.cursor = s.layout.cursor.col;
                 }
                 // Accept the line regardless of where the cursor is.
-                s.edit_move_end()?;
-                if s.has_hint() {
+                if s.has_hint() || !s.is_default_prompt() {
                     // Force a refresh without hints to leave the previous
                     // line as the user typed it after a newline.
                     s.refresh_line_with_msg(None)?;
                 }
+                s.edit_move_end()?;
                 break;
             }
             Cmd::BeginningOfHistory => {
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
index a98c7e1..3edde60 100644
--- a/src/tty/unix.rs
+++ b/src/tty/unix.rs
@@ -265,14 +265,9 @@ impl PosixRawReader {
             } else if seq4 == ';' {
                 let seq5 = self.next_char()?;
                 if seq5.is_digit(10) {
-                    let seq6 = self.next_char()?;
-                    if seq6.is_digit(10) {
-                        self.next_char()?; // 'R' expected
-                    } else if seq6 == 'R' {
-                    } else {
-                        debug!(target: "rustyline",
-                               "unsupported esc sequence: ESC [ {}{} ; {} {}", seq2, seq3, seq5, seq6);
-                    }
+                    let seq6 = self.next_char()?; // '~' expected
+                    debug!(target: "rustyline",
+                           "unsupported esc sequence: ESC [ {}{} ; {} {}", seq2, seq3, seq5, seq6);
                 } else {
                     debug!(target: "rustyline",
                            "unsupported esc sequence: ESC [ {}{} ; {:?}", seq2, seq3, seq5);
@@ -304,10 +299,7 @@ impl PosixRawReader {
             let seq4 = self.next_char()?;
             if seq4.is_digit(10) {
                 let seq5 = self.next_char()?;
-                if seq5.is_digit(10) {
-                    self.next_char()?; // 'R' expected
-                    Ok(KeyPress::UnknownEscSeq)
-                } else if seq2 == '1' {
+                if seq2 == '1' {
                     Ok(match (seq4, seq5) {
                         ('5', 'A') => KeyPress::ControlUp,
                         ('5', 'B') => KeyPress::ControlDown,
@@ -688,18 +680,21 @@ impl Renderer for PosixRenderer {
         }
         /* Report cursor location */
         self.write_and_flush(b"\x1b[6n")?;
-        /* Read the response: ESC [ rows ; cols R */
-        if rdr.poll(100)? == 0
-            || rdr.next_char()? != '\x1b'
-            || rdr.next_char()? != '['
-            || read_digits_until(rdr, ';')?.is_none()
-        {
+        if rdr.poll(100)? == 0 {
             warn!(target: "rustyline", "cannot read initial cursor location");
             return Ok(());
         }
+        /* Read the response: ESC [ rows ; cols R */
+        if rdr.next_char()? != '\x1b' {
+            return Err(error::ReadlineError::from(io::ErrorKind::InvalidData));
+        }
+        if rdr.next_char()? != '[' {
+            return Err(error::ReadlineError::from(io::ErrorKind::InvalidData));
+        }
+        read_digits_until(rdr, ';')?;
         let col = read_digits_until(rdr, 'R')?;
         debug!(target: "rustyline", "initial cursor location: {:?}", col);
-        if col.is_some() && col != Some(1) {
+        if col != 1 {
             self.write_and_flush(b"\n")?;
         }
         Ok(())
@@ -736,7 +731,7 @@ fn width(s: &str, esc_seq: &mut u8) -> usize {
     }
 }
 
-fn read_digits_until(rdr: &mut PosixRawReader, sep: char) -> Result<Option<u32>> {
+fn read_digits_until(rdr: &mut PosixRawReader, sep: char) -> Result<u32> {
     let mut num: u32 = 0;
     loop {
         match rdr.next_char()? {
@@ -747,10 +742,10 @@ fn read_digits_until(rdr: &mut PosixRawReader, sep: char) -> Result<Option<u32>>
                 continue;
             }
             c if c == sep => break,
-            _ => return Ok(None),
+            _ => return Err(error::ReadlineError::from(io::ErrorKind::InvalidData)),
         }
     }
-    Ok(Some(num))
+    Ok(num)
 }
 
 static SIGWINCH_ONCE: sync::Once = sync::Once::new();


Test patch:
diff --git a/src/test/emacs.rs b/src/test/emacs.rs
index 6101405..dbd9d33 100644
--- a/src/test/emacs.rs
+++ b/src/test/emacs.rs
@@ -159,6 +159,7 @@ fn ctrl_n() {
             KeyPress::Ctrl('N'),
             KeyPress::Enter,
         ],
+        "",
         ("line2", ""),
     );
 }
@@ -169,6 +170,7 @@ fn ctrl_p() {
         EditMode::Emacs,
         &["line1"],
         &[KeyPress::Ctrl('P'), KeyPress::Enter],
+        "",
         ("line1", ""),
     );
 }
diff --git a/src/test/history.rs b/src/test/history.rs
index c53706f..c6df132 100644
--- a/src/test/history.rs
+++ b/src/test/history.rs
@@ -10,12 +10,14 @@ fn down_key() {
             *mode,
             &["line1"],
             &[KeyPress::Down, KeyPress::Enter],
+            "",
             ("", ""),
         );
         assert_history(
             *mode,
             &["line1", "line2"],
             &[KeyPress::Up, KeyPress::Up, KeyPress::Down, KeyPress::Enter],
+            "",
             ("line2", ""),
         );
         assert_history(
@@ -27,6 +29,7 @@ fn down_key() {
                 KeyPress::Down, // restore original line
                 KeyPress::Enter,
             ],
+            "",
             ("a", ""),
         );
         assert_history(
@@ -37,6 +40,7 @@ fn down_key() {
                 KeyPress::Down, // noop
                 KeyPress::Enter,
             ],
+            "",
             ("a", ""),
         );
     }
@@ -45,17 +49,19 @@ fn down_key() {
 #[test]
 fn up_key() {
     for mode in &[EditMode::Emacs, EditMode::Vi] {
-        assert_history(*mode, &[], &[KeyPress::Up, KeyPress::Enter], ("", ""));
+        assert_history(*mode, &[], &[KeyPress::Up, KeyPress::Enter], "", ("", ""));
         assert_history(
             *mode,
             &["line1"],
             &[KeyPress::Up, KeyPress::Enter],
+            "",
             ("line1", ""),
         );
         assert_history(
             *mode,
             &["line1", "line2"],
             &[KeyPress::Up, KeyPress::Up, KeyPress::Enter],
+            "",
             ("line1", ""),
         );
     }
@@ -68,6 +74,7 @@ fn ctrl_r() {
             *mode,
             &[],
             &[KeyPress::Ctrl('R'), KeyPress::Char('o'), KeyPress::Enter],
+            "",
             ("o", ""),
         );
         assert_history(
@@ -79,6 +86,7 @@ fn ctrl_r() {
                 KeyPress::Right, // just to assert cursor pos
                 KeyPress::Enter,
             ],
+            "",
             ("cargo", ""),
         );
         assert_history(
@@ -90,6 +98,7 @@ fn ctrl_r() {
                 KeyPress::Right, // just to assert cursor pos
                 KeyPress::Enter,
             ],
+            "",
             ("ru", "stc"),
         );
         assert_history(
@@ -102,6 +111,7 @@ fn ctrl_r() {
                 KeyPress::Right, // just to assert cursor pos
                 KeyPress::Enter,
             ],
+            "",
             ("r", "ustc"),
         );
         assert_history(
@@ -114,6 +124,7 @@ fn ctrl_r() {
                 KeyPress::Right, // just to assert cursor pos
                 KeyPress::Enter,
             ],
+            "",
             ("r", "ustc"),
         );
         assert_history(
@@ -126,6 +137,7 @@ fn ctrl_r() {
                 KeyPress::Right,     // just to assert cursor pos
                 KeyPress::Enter,
             ],
+            "",
             ("car", "go"),
         );
         assert_history(
@@ -138,11 +150,29 @@ fn ctrl_r() {
                 KeyPress::Ctrl('G'), // abort (FIXME: doesn't work with vi mode)
                 KeyPress::Enter,
             ],
+            "",
             ("a", ""),
         );
     }
 }
 
+#[test]
+fn ctrl_r_with_long_prompt() {
+    for mode in &[EditMode::Emacs, EditMode::Vi] {
+        assert_history(
+            *mode,
+            &["rustc", "cargo"],
+            &[
+                KeyPress::Ctrl('R'),
+                KeyPress::Char('o'),
+                KeyPress::Enter,
+            ],
+            ">>>>>>>>>>>>>>>>>>>>>>>>>>> ",
+            ("cargo", ""),
+        );
+    }
+}
+
 #[test]
 fn ctrl_s() {
     for mode in &[EditMode::Emacs, EditMode::Vi] {
@@ -157,6 +187,7 @@ fn ctrl_s() {
                 KeyPress::Right, // just to assert cursor pos
                 KeyPress::Enter,
             ],
+            "",
             ("car", "go"),
         );
     }
@@ -168,12 +199,14 @@ fn meta_lt() {
         EditMode::Emacs,
         &[""],
         &[KeyPress::Meta('<'), KeyPress::Enter],
+        "",
         ("", ""),
     );
     assert_history(
         EditMode::Emacs,
         &["rustc", "cargo"],
         &[KeyPress::Meta('<'), KeyPress::Enter],
+        "",
         ("rustc", ""),
     );
 }
@@ -184,12 +217,14 @@ fn meta_gt() {
         EditMode::Emacs,
         &[""],
         &[KeyPress::Meta('>'), KeyPress::Enter],
+        "",
         ("", ""),
     );
     assert_history(
         EditMode::Emacs,
         &["rustc", "cargo"],
         &[KeyPress::Meta('<'), KeyPress::Meta('>'), KeyPress::Enter],
+        "",
         ("", ""),
     );
     assert_history(
@@ -201,6 +236,7 @@ fn meta_gt() {
             KeyPress::Meta('>'), // restore original line
             KeyPress::Enter,
         ],
+        "",
         ("a", ""),
     );
 }
diff --git a/src/test/mod.rs b/src/test/mod.rs
index 395fa72..b995b90 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -94,14 +94,22 @@ fn assert_cursor(mode: EditMode, initial: (&str, &str), keys: &[KeyPress], expec
 // `entries`: history entries before `keys` pressed
 // `keys`: keys to press
 // `expected`: line status before enter key: strings before and after cursor
-fn assert_history(mode: EditMode, entries: &[&str], keys: &[KeyPress], expected: (&str, &str)) {
+fn assert_history(
+    mode: EditMode,
+    entries: &[&str],
+    keys: &[KeyPress],
+    prompt: &str,
+    expected: (&str, &str),
+) {
     let mut editor = init_editor(mode, keys);
     for entry in entries {
         editor.history.add(*entry);
     }
-    let actual_line = editor.readline("").unwrap();
+    let actual_line = editor.readline(prompt).unwrap();
     assert_eq!(expected.0.to_owned() + expected.1, actual_line);
-    assert_eq!(expected.0.len(), editor.term.cursor);
+    if prompt.is_empty() {
+        assert_eq!(expected.0.len(), editor.term.cursor);
+    }
 }
 
 #[test]
diff --git a/src/test/vi_cmd.rs b/src/test/vi_cmd.rs
index f3ceada..1c2c143 100644
--- a/src/test/vi_cmd.rs
+++ b/src/test/vi_cmd.rs
@@ -441,6 +441,7 @@ fn j() {
                 *key,
                 KeyPress::Enter,
             ],
+            "",
             ("line2", ""),
         );
     }
@@ -457,6 +458,7 @@ fn k() {
             EditMode::Vi,
             &["line1"],
             &[KeyPress::Esc, *key, KeyPress::Enter],
+            "",
             ("line1", ""),
         );
     }