diff --git a/src/history.rs b/src/history.rs index 212d2990af0073e8e4b50a59d1df09e7aab4bac9..94ed8dc8b5502568d8ad65df9b54ccd9cdca6dfc 100644 --- a/src/history.rs +++ b/src/history.rs @@ -7,6 +7,12 @@ use std::path::Path; use super::Result; use config::{Config, HistoryDuplicates}; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Direction { + Forward, + Reverse, +} + pub struct History { entries: VecDeque<String>, max_len: usize, @@ -119,20 +125,23 @@ impl History { /// Return the absolute index of the nearest history entry that matches `term`. /// 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, reverse: bool) -> Option<usize> { + pub fn search(&self, term: &str, start: usize, dir: Direction) -> Option<usize> { if term.is_empty() || start >= self.len() { return None; } - if reverse { - let index = self.entries - .iter() - .rev() - .skip(self.entries.len() - 1 - start) - .position(|entry| entry.contains(term)); - index.and_then(|index| Some(start - index)) - } else { - let index = self.entries.iter().skip(start).position(|entry| entry.contains(term)); - index.and_then(|index| Some(index + start)) + match dir { + Direction::Reverse => { + let index = self.entries + .iter() + .rev() + .skip(self.entries.len() - 1 - start) + .position(|entry| entry.contains(term)); + index.and_then(|index| Some(start - index)) + } + Direction::Forward => { + let index = self.entries.iter().skip(start).position(|entry| entry.contains(term)); + index.and_then(|index| Some(index + start)) + } } } } @@ -141,10 +150,11 @@ impl History { mod tests { extern crate tempdir; use std::path::Path; + use super::{Direction, History}; use config::Config; fn init() -> super::History { - let mut history = super::History::new(Config::default()); + let mut history = History::new(Config::default()); assert!(history.add("line1")); assert!(history.add("line2")); assert!(history.add("line3")); @@ -154,7 +164,7 @@ mod tests { #[test] fn new() { let config = Config::default(); - let history = super::History::new(config); + let history = History::new(config); assert_eq!(config.max_history_size(), history.max_len); assert_eq!(0, history.entries.len()); } @@ -164,7 +174,7 @@ mod tests { let config = Config::builder() .history_ignore_space(true) .build(); - let mut history = super::History::new(config); + let mut history = History::new(config); assert!(history.add("line1")); assert!(history.add("line2")); assert!(!history.add("line2")); @@ -194,24 +204,24 @@ mod tests { #[test] fn search() { let history = init(); - assert_eq!(None, history.search("", 0, false)); - assert_eq!(None, history.search("none", 0, false)); - assert_eq!(None, history.search("line", 3, false)); + assert_eq!(None, history.search("", 0, Direction::Forward)); + assert_eq!(None, history.search("none", 0, Direction::Forward)); + assert_eq!(None, history.search("line", 3, Direction::Forward)); - assert_eq!(Some(0), history.search("line", 0, false)); - assert_eq!(Some(1), history.search("line", 1, false)); - assert_eq!(Some(2), history.search("line3", 1, false)); + assert_eq!(Some(0), history.search("line", 0, Direction::Forward)); + assert_eq!(Some(1), history.search("line", 1, Direction::Forward)); + assert_eq!(Some(2), history.search("line3", 1, Direction::Forward)); } #[test] fn reverse_search() { let history = init(); - assert_eq!(None, history.search("", 2, true)); - assert_eq!(None, history.search("none", 2, true)); - assert_eq!(None, history.search("line", 3, true)); + assert_eq!(None, history.search("", 2, Direction::Reverse)); + assert_eq!(None, history.search("none", 2, Direction::Reverse)); + assert_eq!(None, history.search("line", 3, Direction::Reverse)); - assert_eq!(Some(2), history.search("line", 2, true)); - assert_eq!(Some(1), history.search("line", 1, true)); - assert_eq!(Some(0), history.search("line1", 1, true)); + assert_eq!(Some(2), history.search("line", 2, Direction::Reverse)); + assert_eq!(Some(1), history.search("line", 1, Direction::Reverse)); + assert_eq!(Some(0), history.search("line1", 1, Direction::Reverse)); } } diff --git a/src/kill_ring.rs b/src/kill_ring.rs index f897597c65e4a856069844e025459bb2e9487c34..cbbdbfbe721341082ebf377c2e9455ba360f3d9b 100644 --- a/src/kill_ring.rs +++ b/src/kill_ring.rs @@ -7,6 +7,12 @@ enum Action { Other, } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Mode { + Append, + Prepend, +} + pub struct KillRing { slots: Vec<String>, index: usize, @@ -29,20 +35,19 @@ impl KillRing { } /// Add `text` to the kill-ring. - pub fn kill(&mut self, text: &str, forward: bool) { + pub fn kill(&mut self, text: &str, dir: Mode) { match self.last_action { Action::Kill => { if self.slots.capacity() == 0 { // disabled return; } - if forward { - // append - self.slots[self.index].push_str(text); - } else { - // prepend - self.slots[self.index] = String::from(text) + &self.slots[self.index]; - } + match dir { + Mode::Append => self.slots[self.index].push_str(text), + Mode::Prepend => { + self.slots[self.index] = String::from(text) + &self.slots[self.index] + } + }; } _ => { self.last_action = Action::Kill; @@ -99,12 +104,12 @@ impl KillRing { #[cfg(test)] mod tests { - use super::{Action, KillRing}; + use super::{Action, Mode, KillRing}; #[test] fn disabled() { let mut kill_ring = KillRing::new(0); - kill_ring.kill("text", true); + kill_ring.kill("text", Mode::Append); assert!(kill_ring.slots.is_empty()); assert_eq!(0, kill_ring.index); assert_eq!(Action::Kill, kill_ring.last_action); @@ -116,7 +121,7 @@ mod tests { #[test] fn one_kill() { let mut kill_ring = KillRing::new(2); - kill_ring.kill("word1", true); + kill_ring.kill("word1", Mode::Append); assert_eq!(0, kill_ring.index); assert_eq!(1, kill_ring.slots.len()); assert_eq!("word1", kill_ring.slots[0]); @@ -124,10 +129,10 @@ mod tests { } #[test] - fn kill_kill_forward() { + fn kill_append() { let mut kill_ring = KillRing::new(2); - kill_ring.kill("word1", true); - kill_ring.kill(" word2", true); + kill_ring.kill("word1", Mode::Append); + kill_ring.kill(" word2", Mode::Append); assert_eq!(0, kill_ring.index); assert_eq!(1, kill_ring.slots.len()); assert_eq!("word1 word2", kill_ring.slots[0]); @@ -135,10 +140,10 @@ mod tests { } #[test] - fn kill_kill_backward() { + fn kill_backward() { let mut kill_ring = KillRing::new(2); - kill_ring.kill("word1", false); - kill_ring.kill("word2 ", false); + kill_ring.kill("word1", Mode::Prepend); + kill_ring.kill("word2 ", Mode::Prepend); assert_eq!(0, kill_ring.index); assert_eq!(1, kill_ring.slots.len()); assert_eq!("word2 word1", kill_ring.slots[0]); @@ -148,9 +153,9 @@ mod tests { #[test] fn kill_other_kill() { let mut kill_ring = KillRing::new(2); - kill_ring.kill("word1", true); + kill_ring.kill("word1", Mode::Append); kill_ring.reset(); - kill_ring.kill("word2", true); + kill_ring.kill("word2", Mode::Append); assert_eq!(1, kill_ring.index); assert_eq!(2, kill_ring.slots.len()); assert_eq!("word1", kill_ring.slots[0]); @@ -161,13 +166,13 @@ mod tests { #[test] fn many_kill() { let mut kill_ring = KillRing::new(2); - kill_ring.kill("word1", true); + kill_ring.kill("word1", Mode::Append); kill_ring.reset(); - kill_ring.kill("word2", true); + kill_ring.kill("word2", Mode::Append); kill_ring.reset(); - kill_ring.kill("word3", true); + kill_ring.kill("word3", Mode::Append); kill_ring.reset(); - kill_ring.kill("word4", true); + kill_ring.kill("word4", Mode::Append); assert_eq!(1, kill_ring.index); assert_eq!(2, kill_ring.slots.len()); assert_eq!("word3", kill_ring.slots[0]); @@ -178,9 +183,9 @@ mod tests { #[test] fn yank() { let mut kill_ring = KillRing::new(2); - kill_ring.kill("word1", true); + kill_ring.kill("word1", Mode::Append); kill_ring.reset(); - kill_ring.kill("word2", true); + kill_ring.kill("word2", Mode::Append); assert_eq!(Some(&"word2".to_string()), kill_ring.yank()); assert_eq!(Action::Yank(5), kill_ring.last_action); @@ -191,9 +196,9 @@ mod tests { #[test] fn yank_pop() { let mut kill_ring = KillRing::new(2); - kill_ring.kill("word1", true); + kill_ring.kill("word1", Mode::Append); kill_ring.reset(); - kill_ring.kill("longword2", true); + kill_ring.kill("longword2", Mode::Append); assert_eq!(None, kill_ring.yank_pop()); kill_ring.yank(); diff --git a/src/lib.rs b/src/lib.rs index 1b0202d83eca6cb79dec43ba051e7a6f57b145c9..1b33d30c28ea7705c0efd9bd3a01c16df52043c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,9 +51,9 @@ use tty::Terminal; use encode_unicode::CharExt; use completion::{Completer, longest_common_prefix}; use consts::KeyPress; -use history::History; +use history::{Direction, History}; use line_buffer::{LineBuffer, MAX_LINE, WordAction}; -use kill_ring::KillRing; +use kill_ring::{Mode, KillRing}; pub use config::{CompletionType, Config, HistoryDuplicates}; /// The error type for I/O and Linux Syscalls (Errno) @@ -705,7 +705,7 @@ fn reverse_incremental_search<R: Read>(rdr: &mut tty::RawReader<R>, let mut search_buf = String::new(); let mut history_idx = history.len() - 1; - let mut reverse = true; + let mut direction = Direction::Reverse; let mut success = true; let mut key; @@ -729,7 +729,7 @@ fn reverse_incremental_search<R: Read>(rdr: &mut tty::RawReader<R>, continue; } KeyPress::Ctrl('R') => { - reverse = true; + direction = Direction::Reverse; if history_idx > 0 { history_idx -= 1; } else { @@ -738,7 +738,7 @@ fn reverse_incremental_search<R: Read>(rdr: &mut tty::RawReader<R>, } } KeyPress::Ctrl('S') => { - reverse = false; + direction = Direction::Forward; if history_idx < history.len() - 1 { history_idx += 1; } else { @@ -755,7 +755,7 @@ fn reverse_incremental_search<R: Read>(rdr: &mut tty::RawReader<R>, _ => break, } } - success = match history.search(&search_buf, history_idx, reverse) { + success = match history.search(&search_buf, history_idx, direction) { Some(idx) => { history_idx = idx; let entry = history.get(idx).unwrap(); @@ -876,7 +876,7 @@ fn readline_edit<C: Completer>(prompt: &str, KeyPress::Ctrl('K') => { // Kill the text from point to the end of the line. if let Some(text) = try!(edit_kill_line(&mut s)) { - editor.kill_ring.kill(&text, true) + editor.kill_ring.kill(&text, Mode::Append) } } KeyPress::Ctrl('L') => { @@ -904,7 +904,7 @@ fn readline_edit<C: Completer>(prompt: &str, KeyPress::Ctrl('U') => { // Kill backward from point to the beginning of the line. if let Some(text) = try!(edit_discard_line(&mut s)) { - editor.kill_ring.kill(&text, false) + editor.kill_ring.kill(&text, Mode::Prepend) } } #[cfg(unix)] @@ -917,7 +917,7 @@ fn readline_edit<C: Completer>(prompt: &str, KeyPress::Ctrl('W') => { // Kill the word behind point, using white space as a word boundary if let Some(text) = try!(edit_delete_prev_word(&mut s, char::is_whitespace)) { - editor.kill_ring.kill(&text, false) + editor.kill_ring.kill(&text, Mode::Prepend) } } KeyPress::Ctrl('Y') => { @@ -947,7 +947,7 @@ fn readline_edit<C: Completer>(prompt: &str, // Kill from the cursor to the start of the current word, or, if between words, to the start of the previous word. if let Some(text) = try!(edit_delete_prev_word(&mut s, |ch| !ch.is_alphanumeric())) { - editor.kill_ring.kill(&text, false) + editor.kill_ring.kill(&text, Mode::Prepend) } } KeyPress::Meta('<') => { @@ -973,7 +973,7 @@ fn readline_edit<C: Completer>(prompt: &str, KeyPress::Meta('D') => { // kill one word forward if let Some(text) = try!(edit_delete_word(&mut s)) { - editor.kill_ring.kill(&text, true) + editor.kill_ring.kill(&text, Mode::Append) } } KeyPress::Meta('F') => {