Add support to history-search-backward/forward
diff --git a/examples/example.rs b/examples/example.rs
index 9f047c6..8f02337 100644
--- a/examples/example.rs
+++ b/examples/example.rs
@@ -6,7 +6,7 @@
use rustyline::completion::FilenameCompleter;
use rustyline::error::ReadlineError;
-use rustyline::{Config, CompletionType, Editor, EditMode};
+use rustyline::{Cmd, Config, CompletionType, Editor, EditMode, KeyPress};
// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
@@ -27,6 +27,8 @@
let c = FilenameCompleter::new();
let mut rl = Editor::with_config(config);
rl.set_completer(Some(c));
+ rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward);
+ rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward);
if rl.load_history("history.txt").is_err() {
println!("No previous history.");
}
diff --git a/src/consts.rs b/src/consts.rs
index ed38db6..130ebc1 100644
--- a/src/consts.rs
+++ b/src/consts.rs
@@ -55,7 +55,7 @@
'\x1a' => KeyPress::Ctrl('Z'),
'\x1b' => KeyPress::Esc,
'\x1f' => KeyPress::Ctrl('_'),
- '\x7f' => KeyPress::Backspace, // TODO Validate
+ '\x7f' => KeyPress::Backspace,
_ => KeyPress::Null,
}
}
diff --git a/src/history.rs b/src/history.rs
index 054d181..9b3e6db 100644
--- a/src/history.rs
+++ b/src/history.rs
@@ -120,7 +120,7 @@
fix_perm(&file);
let mut wtr = BufWriter::new(file);
for entry in &self.entries {
- try!(wtr.write_all(&entry.as_bytes()));
+ try!(wtr.write_all(entry.as_bytes()));
try!(wtr.write_all(b"\n"));
}
Ok(())
@@ -151,6 +151,18 @@
/// Return None if no entry contains `term` between [start, len -1] for forward search
/// or between [0, start] for reverse search.
pub fn search(&self, term: &str, start: usize, dir: Direction) -> Option<usize> {
+ let test = |entry: &String| entry.contains(term);
+ self.search_match(term, start, dir, test)
+ }
+
+ pub fn starts_with(&self, term: &str, start: usize, dir: Direction) -> Option<usize> {
+ let test = |entry: &String| entry.starts_with(term);
+ self.search_match(term, start, dir, test)
+ }
+
+ fn search_match<F>(&self, term: &str, start: usize, dir: Direction, test: F) -> Option<usize>
+ where F: Fn(&String) -> bool
+ {
if term.is_empty() || start >= self.len() {
return None;
}
@@ -160,14 +172,11 @@
.iter()
.rev()
.skip(self.entries.len() - 1 - start)
- .position(|entry| entry.contains(term));
+ .position(test);
index.and_then(|index| Some(start - index))
}
Direction::Forward => {
- let index = self.entries
- .iter()
- .skip(start)
- .position(|entry| entry.contains(term));
+ let index = self.entries.iter().skip(start).position(test);
index.and_then(|index| Some(index + start))
}
}
diff --git a/src/keymap.rs b/src/keymap.rs
index caf0722..448409e 100644
--- a/src/keymap.rs
+++ b/src/keymap.rs
@@ -23,6 +23,8 @@
EndOfFile,
EndOfHistory,
ForwardSearchHistory,
+ HistorySearchBackward,
+ HistorySearchForward,
Insert(RepeatCount, String),
Interrupt,
Kill(Movement),
diff --git a/src/lib.rs b/src/lib.rs
index 94df981..883f086 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -564,6 +564,36 @@
s.refresh_line()
}
+fn edit_history_search(s: &mut State, history: &History, dir: Direction) -> Result<()> {
+ if history.is_empty() {
+ return Ok(());
+ }
+ if s.history_index == history.len() {
+ if dir == Direction::Reverse {
+ // Save the current edited line before to overwrite it
+ s.snapshot();
+ } else {
+ return Ok(());
+ }
+ } else if s.history_index == 0 && dir == Direction::Reverse {
+ return Ok(());
+ }
+ if dir == Direction::Reverse {
+ s.history_index -= 1;
+ } else {
+ s.history_index += 1;
+ }
+ if let Some(history_index) =
+ history.starts_with(&s.line.as_str()[..s.line.pos()], s.history_index, dir) {
+ s.history_index = history_index;
+ let buf = history.get(history_index).unwrap();
+ s.line.update(buf, buf.len());
+ s.refresh_line()
+ } else {
+ Ok(())
+ }
+}
+
/// Substitute the currently edited line with the first/last history entry.
fn edit_history(s: &mut State, history: &History, first: bool) -> Result<()> {
if history.is_empty() {
@@ -972,6 +1002,12 @@
// Fetch the previous command from the history list.
try!(edit_history_next(&mut s, &editor.history, true))
}
+ Cmd::HistorySearchBackward => {
+ try!(edit_history_search(&mut s, &editor.history, Direction::Reverse))
+ }
+ Cmd::HistorySearchForward => {
+ try!(edit_history_search(&mut s, &editor.history, Direction::Forward))
+ }
Cmd::TransposeChars => {
// Exchange the char before cursor with the character at cursor.
try!(edit_transpose_chars(&mut s))
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
index e04328a..197e86a 100644
--- a/src/tty/unix.rs
+++ b/src/tty/unix.rs
@@ -160,8 +160,6 @@
}
})
} else {
- // TODO ESC-N (n): search history forward not interactively
- // TODO ESC-P (p): search history backward not interactively
// TODO ESC-R (r): Undo all changes made to this line.
Ok(match seq1 {
'\x08' => KeyPress::Meta('\x08'), // Backspace
@@ -174,6 +172,8 @@
'd' | 'D' => KeyPress::Meta('D'),
'f' | 'F' => KeyPress::Meta('F'),
'l' | 'L' => KeyPress::Meta('L'),
+ 'n' | 'N' => KeyPress::Meta('N'),
+ 'p' | 'P' => KeyPress::Meta('P'),
't' | 'T' => KeyPress::Meta('T'),
'u' | 'U' => KeyPress::Meta('U'),
'y' | 'Y' => KeyPress::Meta('Y'),
diff --git a/src/tty/windows.rs b/src/tty/windows.rs
index 269dee4..389346b 100644
--- a/src/tty/windows.rs
+++ b/src/tty/windows.rs
@@ -156,6 +156,8 @@
'd' | 'D' => KeyPress::Meta('D'),
'f' | 'F' => KeyPress::Meta('F'),
'l' | 'L' => KeyPress::Meta('L'),
+ 'n' | 'N' => KeyPress::Meta('N'),
+ 'p' | 'P' => KeyPress::Meta('P'),
't' | 'T' => KeyPress::Meta('T'),
'u' | 'U' => KeyPress::Meta('U'),
'y' | 'Y' => KeyPress::Meta('Y'),