diff --git a/examples/example.rs b/examples/example.rs index 25c6ad3ace9801cc5e5f9c5e5f99577d8f73d693..9f047c64d9e7fd86851017cda8519e96977383c7 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -70,7 +70,7 @@ impl log::Log for Logger { fn init_logger() -> Result<(), SetLoggerError> { log::set_logger(|max_log_level| { - max_log_level.set(LogLevelFilter::Info); - Box::new(Logger) - }) + max_log_level.set(LogLevelFilter::Info); + Box::new(Logger) + }) } diff --git a/src/history.rs b/src/history.rs index c4c5e9a9d8b1fdbbea9ced3c0ab78c97e2e0f1ec..054d181ead2b8850467c70b130406b5ee3826669 100644 --- a/src/history.rs +++ b/src/history.rs @@ -56,7 +56,11 @@ impl History { return false; } if line.as_ref().is_empty() || - (self.ignore_space && line.as_ref().chars().next().map_or(true, |c| c.is_whitespace())) { + (self.ignore_space && + line.as_ref() + .chars() + .next() + .map_or(true, |c| c.is_whitespace())) { return false; } if self.ignore_dups { @@ -160,7 +164,10 @@ impl History { 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(|entry| entry.contains(term)); index.and_then(|index| Some(index + start)) } } @@ -260,9 +267,7 @@ mod tests { #[test] fn add() { - let config = Config::builder() - .history_ignore_space(true) - .build(); + let config = Config::builder().history_ignore_space(true).build(); let mut history = History::with_config(config); assert_eq!(config.max_history_size(), history.max_len); assert!(history.add("line1")); @@ -277,7 +282,7 @@ mod tests { let mut history = init(); history.set_max_len(1); assert_eq!(1, history.entries.len()); - assert_eq!(Some(&"line3".to_string()), history.last()); + assert_eq!(Some(&"line3".to_owned()), history.last()); } #[test] diff --git a/src/keymap.rs b/src/keymap.rs index 3287f53552fb0b258adb0a21d8a18f854ac7cf85..a63be00e2f42ff72bc72f48e83150330f1af9384 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -119,6 +119,15 @@ pub enum CharSearch { } impl CharSearch { + pub fn is_backward(&self) -> bool { + match *self { + CharSearch::Forward(_) => false, + CharSearch::ForwardBefore(_) => false, + CharSearch::Backward(_) => true, + CharSearch::BackwardAfter(_) => true, + } + } + fn opposite(&self) -> CharSearch { match *self { CharSearch::Forward(c) => CharSearch::Backward(c), diff --git a/src/kill_ring.rs b/src/kill_ring.rs index cbbdbfbe721341082ebf377c2e9455ba360f3d9b..cbe2aa12aa5eec5b692014919a9f6152c81ab1e5 100644 --- a/src/kill_ring.rs +++ b/src/kill_ring.rs @@ -187,9 +187,9 @@ mod tests { kill_ring.reset(); kill_ring.kill("word2", Mode::Append); - assert_eq!(Some(&"word2".to_string()), kill_ring.yank()); + assert_eq!(Some(&"word2".to_owned()), kill_ring.yank()); assert_eq!(Action::Yank(5), kill_ring.last_action); - assert_eq!(Some(&"word2".to_string()), kill_ring.yank()); + assert_eq!(Some(&"word2".to_owned()), kill_ring.yank()); assert_eq!(Action::Yank(5), kill_ring.last_action); } @@ -202,8 +202,8 @@ mod tests { assert_eq!(None, kill_ring.yank_pop()); kill_ring.yank(); - assert_eq!(Some((9, &"word1".to_string())), kill_ring.yank_pop()); - assert_eq!(Some((5, &"longword2".to_string())), kill_ring.yank_pop()); - assert_eq!(Some((9, &"word1".to_string())), kill_ring.yank_pop()); + assert_eq!(Some((9, &"word1".to_owned())), kill_ring.yank_pop()); + assert_eq!(Some((5, &"longword2".to_owned())), kill_ring.yank_pop()); + assert_eq!(Some((9, &"word1".to_owned())), kill_ring.yank_pop()); } } diff --git a/src/lib.rs b/src/lib.rs index c2946c8324ef39ccceabce23b04885d36c5cee12..0718d734d36da0cbd01f36f2ff97df6b519a673f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,11 +43,13 @@ mod undo; mod tty; +use std::cell::RefCell; use std::fmt; use std::io::{self, Write}; use std::mem; use std::path::Path; use std::result; +use std::rc::Rc; use unicode_segmentation::UnicodeSegmentation; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; @@ -55,7 +57,7 @@ use tty::{RawMode, RawReader, Terminal, Term}; use completion::{Completer, longest_common_prefix}; use history::{Direction, History}; -use line_buffer::{LineBuffer, MAX_LINE, WordAction}; +use line_buffer::{ChangeListener, LineBuffer, MAX_LINE, WordAction}; use keymap::{Anchor, At, CharSearch, Cmd, EditState, Movement, RepeatCount, Word}; use kill_ring::{Mode, KillRing}; pub use config::{CompletionType, Config, EditMode, HistoryDuplicates}; @@ -78,7 +80,6 @@ struct State<'out, 'prompt> { term: Terminal, // terminal byte_buffer: [u8; 4], edit_state: EditState, - changes: Changeset, } #[derive(Copy, Clone, Debug, Default)] @@ -110,7 +111,6 @@ impl<'out, 'prompt> State<'out, 'prompt> { term: term, byte_buffer: [0; 4], edit_state: EditState::new(config), - changes: Changeset::new(), } } @@ -128,6 +128,7 @@ impl<'out, 'prompt> State<'out, 'prompt> { fn snapshot(&mut self) { mem::swap(&mut self.line, &mut self.snapshot); + // TODO swap ChangeListener ? } fn backup(&mut self) { @@ -209,9 +210,9 @@ impl<'out, 'prompt> State<'out, 'prompt> { info.dwCursorPosition.Y -= self.cursor.row as i16; try!(self.term.set_console_cursor_position(info.dwCursorPosition)); let mut _count = 0; - try!(self.term - .fill_console_output_character((info.dwSize.X * (self.old_rows as i16 + 1)) as u32, - info.dwCursorPosition)); + try!(self.term.fill_console_output_character((info.dwSize.X * (self.old_rows as i16 + 1)) as + u32, + info.dwCursorPosition)); let mut ab = String::new(); // display the prompt ab.push_str(prompt); // TODO handle ansi escape code (SetConsoleTextAttribute) @@ -419,7 +420,7 @@ fn edit_delete(s: &mut State, n: RepeatCount) -> Result<()> { /// Backspace implementation. fn edit_backspace(s: &mut State, n: RepeatCount) -> Result<()> { - if s.line.backspace(n).is_some() { + if s.line.backspace(n) { s.refresh_line() } else { Ok(()) @@ -427,22 +428,20 @@ fn edit_backspace(s: &mut State, n: RepeatCount) -> Result<()> { } /// Kill the text from point to the end of the line. -fn edit_kill_line(s: &mut State) -> Result<Option<String>> { - if let Some(text) = s.line.kill_line() { - try!(s.refresh_line()); - Ok(Some(text)) +fn edit_kill_line(s: &mut State) -> Result<()> { + if s.line.kill_line() { + s.refresh_line() } else { - Ok(None) + Ok(()) } } /// Kill backward from point to the beginning of the line. -fn edit_discard_line(s: &mut State) -> Result<Option<String>> { - if let Some(text) = s.line.discard_line() { - try!(s.refresh_line()); - Ok(Some(text)) +fn edit_discard_line(s: &mut State) -> Result<()> { + if s.line.discard_line() { + s.refresh_line() } else { - Ok(None) + Ok(()) } } @@ -465,12 +464,11 @@ fn edit_move_to_prev_word(s: &mut State, word_def: Word, n: RepeatCount) -> Resu /// Delete the previous word, maintaining the cursor at the start of the /// current word. -fn edit_delete_prev_word(s: &mut State, word_def: Word, n: RepeatCount) -> Result<Option<String>> { - if let Some(text) = s.line.delete_prev_word(word_def, n) { - try!(s.refresh_line()); - Ok(Some(text)) +fn edit_delete_prev_word(s: &mut State, word_def: Word, n: RepeatCount) -> Result<()> { + if s.line.delete_prev_word(word_def, n) { + s.refresh_line() } else { - Ok(None) + Ok(()) } } @@ -491,25 +489,19 @@ fn edit_move_to(s: &mut State, cs: CharSearch, n: RepeatCount) -> Result<()> { } /// Kill from the cursor to the end of the current word, or, if between words, to the end of the next word. -fn edit_delete_word(s: &mut State, - at: At, - word_def: Word, - n: RepeatCount) - -> Result<Option<String>> { - if let Some(text) = s.line.delete_word(at, word_def, n) { - try!(s.refresh_line()); - Ok(Some(text)) +fn edit_delete_word(s: &mut State, at: At, word_def: Word, n: RepeatCount) -> Result<()> { + if s.line.delete_word(at, word_def, n) { + s.refresh_line() } else { - Ok(None) + Ok(()) } } -fn edit_delete_to(s: &mut State, cs: CharSearch, n: RepeatCount) -> Result<Option<String>> { - if let Some(text) = s.line.delete_to(cs, n) { - try!(s.refresh_line()); - Ok(Some(text)) +fn edit_delete_to(s: &mut State, cs: CharSearch, n: RepeatCount) -> Result<()> { + if s.line.delete_to(cs, n) { + s.refresh_line() } else { - Ok(None) + Ok(()) } } @@ -849,12 +841,14 @@ fn readline_edit<C: Completer>(prompt: &str, let mut stdout = editor.term.create_writer(); - editor.kill_ring.reset(); + editor.reset_kill_ring(); + editor.clear_changes(); let mut s = State::new(&mut stdout, editor.term.clone(), &editor.config, prompt, editor.history.len()); + s.line.bind(Some(editor.listener.clone())); try!(s.refresh_line()); let mut rdr = try!(s.term.create_reader(&editor.config)); @@ -867,7 +861,7 @@ fn readline_edit<C: Completer>(prompt: &str, if cmd == Cmd::Complete && completer.is_some() { let next = try!(complete_line(&mut rdr, &mut s, completer.unwrap(), &editor.config)); if next.is_some() { - editor.kill_ring.reset(); + editor.reset_kill_ring(); cmd = next.unwrap(); } else { continue; @@ -875,11 +869,11 @@ fn readline_edit<C: Completer>(prompt: &str, } if let Cmd::SelfInsert(n, c) = cmd { - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_insert(&mut s, c, n)); continue; } else if let Cmd::Insert(n, text) = cmd { - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_yank(&mut s, &text, Anchor::Before, n)); continue; } @@ -896,31 +890,31 @@ fn readline_edit<C: Completer>(prompt: &str, match cmd { Cmd::BeginningOfLine => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Move to the beginning of line. try!(edit_move_home(&mut s)) } Cmd::ViFirstPrint => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_move_home(&mut s)); try!(edit_move_to_next_word(&mut s, At::Start, Word::Big, 1)) } Cmd::BackwardChar(n) => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Move back a character. try!(edit_move_backward(&mut s, n)) } Cmd::Kill(Movement::ForwardChar(n)) => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Delete (forward) one character at point. try!(edit_delete(&mut s, n)) } Cmd::Replace(n, c) => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_replace_char(&mut s, c, n)); } Cmd::EndOfFile => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); if !s.edit_state.is_emacs_mode() && !s.line.is_empty() { try!(edit_move_end(&mut s)); break; @@ -931,31 +925,29 @@ fn readline_edit<C: Completer>(prompt: &str, } } Cmd::EndOfLine => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Move to the end of line. try!(edit_move_end(&mut s)) } Cmd::ForwardChar(n) => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Move forward a character. try!(edit_move_forward(&mut s, n)) } Cmd::Kill(Movement::BackwardChar(n)) => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Delete one character backward. try!(edit_backspace(&mut s, n)) } Cmd::Kill(Movement::EndOfLine) => { // 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, Mode::Append) - } + editor.set_kill_ring_mode(Mode::Append); + try!(edit_kill_line(&mut s)) } Cmd::Kill(Movement::WholeLine) => { try!(edit_move_home(&mut s)); - if let Some(text) = try!(edit_kill_line(&mut s)) { - editor.kill_ring.kill(&text, Mode::Append) - } + editor.set_kill_ring_mode(Mode::Append); + try!(edit_kill_line(&mut s)) } Cmd::ClearScreen => { // Clear the screen leaving the current line at the top of the screen. @@ -963,126 +955,126 @@ fn readline_edit<C: Completer>(prompt: &str, try!(s.refresh_line()) } Cmd::NextHistory => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Fetch the next command from the history list. try!(edit_history_next(&mut s, &editor.history, false)) } Cmd::PreviousHistory => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Fetch the previous command from the history list. try!(edit_history_next(&mut s, &editor.history, true)) } Cmd::TransposeChars => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Exchange the char before cursor with the character at cursor. try!(edit_transpose_chars(&mut s)) } Cmd::Kill(Movement::BeginningOfLine) => { // 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, Mode::Prepend) - } + editor.set_kill_ring_mode(Mode::Prepend); + try!(edit_discard_line(&mut s)) } #[cfg(unix)] Cmd::QuotedInsert => { // Quoted insert - editor.kill_ring.reset(); + editor.reset_kill_ring(); let c = try!(rdr.next_char()); try!(edit_insert(&mut s, c, 1)) // FIXME } Cmd::Yank(n, anchor) => { // retrieve (yank) last item killed - if let Some(text) = editor.kill_ring.yank() { + if let Some(text) = editor.listener.borrow_mut().kill_ring.yank() { try!(edit_yank(&mut s, text, anchor, n)) } } Cmd::ViYankTo(mvt) => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); if let Some(text) = s.line.copy(mvt) { - editor.kill_ring.kill(&text, Mode::Append) + editor.kill(&text, Mode::Append) } } // TODO CTRL-_ // undo Cmd::AcceptLine => { // Accept the line regardless of where the cursor is. - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_move_end(&mut s)); break; } Cmd::Kill(Movement::BackwardWord(n, word_def)) => { // kill one word backward (until start of word) - if let Some(text) = try!(edit_delete_prev_word(&mut s, word_def, n)) { - editor.kill_ring.kill(&text, Mode::Prepend) - } + editor.set_kill_ring_mode(Mode::Prepend); + try!(edit_delete_prev_word(&mut s, word_def, n)) } Cmd::BeginningOfHistory => { // move to first entry in history - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_history(&mut s, &editor.history, true)) } Cmd::EndOfHistory => { // move to last entry in history - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_history(&mut s, &editor.history, false)) } Cmd::BackwardWord(n, word_def) => { // move backwards one word - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_move_to_prev_word(&mut s, word_def, n)) } Cmd::CapitalizeWord => { // capitalize word after point - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_word(&mut s, WordAction::CAPITALIZE)) } Cmd::Kill(Movement::ForwardWord(n, at, word_def)) => { // kill one word forward (until start/end of word) - if let Some(text) = try!(edit_delete_word(&mut s, at, word_def, n)) { - editor.kill_ring.kill(&text, Mode::Append) - } + editor.set_kill_ring_mode(Mode::Append); + try!(edit_delete_word(&mut s, at, word_def, n)) } Cmd::ForwardWord(n, at, word_def) => { // move forwards one word - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_move_to_next_word(&mut s, at, word_def, n)) } Cmd::DowncaseWord => { // lowercase word after point - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_word(&mut s, WordAction::LOWERCASE)) } Cmd::TransposeWords(n) => { // transpose words - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_transpose_words(&mut s, n)) } Cmd::UpcaseWord => { // uppercase word after point - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_word(&mut s, WordAction::UPPERCASE)) } Cmd::YankPop => { // yank-pop - if let Some((yank_size, text)) = editor.kill_ring.yank_pop() { + if let Some((yank_size, text)) = editor.listener.borrow_mut().kill_ring.yank_pop() { try!(edit_yank_pop(&mut s, yank_size, text)) } } Cmd::ViCharSearch(n, cs) => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); try!(edit_move_to(&mut s, cs, n)) } Cmd::Kill(Movement::ViCharSearch(n, cs)) => { - if let Some(text) = try!(edit_delete_to(&mut s, cs, n)) { - editor.kill_ring.kill(&text, Mode::Append) + if cs.is_backward() { + editor.set_kill_ring_mode(Mode::Prepend); + } else { + editor.set_kill_ring_mode(Mode::Append); } + try!(edit_delete_to(&mut s, cs, n)) } Cmd::Undo => { - if s.changes.undo(&mut s.line) { + if editor.undo(&mut s.line) { try!(s.refresh_line()); } } Cmd::Interrupt => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); return Err(error::ReadlineError::Interrupted); } #[cfg(unix)] @@ -1095,7 +1087,7 @@ fn readline_edit<C: Completer>(prompt: &str, } Cmd::Noop => {} _ => { - editor.kill_ring.reset(); + editor.reset_kill_ring(); // Ignore the character typed. } } @@ -1138,10 +1130,71 @@ pub struct Editor<C: Completer> { term: Terminal, history: History, completer: Option<C>, - kill_ring: KillRing, + listener: Rc<RefCell<Listener>>, config: Config, } +struct Listener { + kill_ring: KillRing, + mode: Option<Mode>, + changes: Changeset, // FIXME useful only during one line edition (versus kill_ring) + undoing: bool, +} + +impl Listener { + fn new() -> Rc<RefCell<Listener>> { + let l = Listener { + kill_ring: KillRing::new(60), + mode: None, + changes: Changeset::new(), + undoing: false, + }; + Rc::new(RefCell::new(l)) + } + + fn reset_kill_ring(&mut self) { + self.kill_ring.reset(); + self.mode = None; + } + fn set_kill_ring_mode(&mut self, mode: Mode) { + self.mode = Some(mode); + } + + fn undo(&mut self, line: &mut LineBuffer) -> bool { + self.undoing = true; + let ok = self.changes.undo(line); + self.undoing = false; + ok + } + fn clear_changes(&mut self) { + self.changes.clear(); + } +} +impl ChangeListener for Listener { + fn insert_char(&mut self, idx: usize, c: char) { + if self.undoing { + return; + } + self.changes.insert(idx, c); + } + fn insert_str(&mut self, idx: usize, string: &str) { + if self.undoing { + return; + } + self.changes.insert_str(idx, string); + } + fn delete(&mut self, idx: usize, string: &str) { + if self.mode.is_some() { + self.kill_ring.kill(string, self.mode.unwrap()); + self.mode = None; + } + if self.undoing { + return; + } + self.changes.delete(idx, string); + } +} + impl<C: Completer> Editor<C> { pub fn new() -> Editor<C> { Self::with_config(Config::default()) @@ -1153,7 +1206,7 @@ impl<C: Completer> Editor<C> { term: term, history: History::with_config(config), completer: None, - kill_ring: KillRing::new(60), + listener: Listener::new(), config: config, } } @@ -1222,6 +1275,23 @@ impl<C: Completer> Editor<C> { prompt: prompt, } } + + fn reset_kill_ring(&self) { + self.listener.borrow_mut().reset_kill_ring(); + } + fn set_kill_ring_mode(&self, mode: Mode) { + self.listener.borrow_mut().set_kill_ring_mode(mode); + } + fn kill(&self, text: &str, dir: Mode) { + self.listener.borrow_mut().kill_ring.kill(text, dir) + } + + fn undo(&self, line: &mut LineBuffer) -> bool { + self.listener.borrow_mut().undo(line) + } + fn clear_changes(&self) { + self.listener.borrow_mut().clear_changes() + } } impl<C: Completer> fmt::Debug for Editor<C> { @@ -1267,7 +1337,6 @@ mod test { use keymap::{Cmd, EditState}; use super::{Editor, Position, Result, State}; use tty::{Terminal, Term}; - use undo::Changeset; fn init_state<'out>(out: &'out mut Write, line: &str, @@ -1280,7 +1349,7 @@ mod test { out: out, prompt: "", prompt_size: Position::default(), - line: LineBuffer::init(line, pos), + line: LineBuffer::init(line, pos, None), cursor: Position::default(), cols: cols, old_rows: 0, @@ -1289,7 +1358,6 @@ mod test { term: term, byte_buffer: [0; 4], edit_state: EditState::new(&config), - changes: Changeset::new(), } } @@ -1340,7 +1408,7 @@ mod test { struct SimpleCompleter; impl Completer for SimpleCompleter { fn complete(&self, line: &str, _pos: usize) -> Result<(usize, Vec<String>)> { - Ok((0, vec![line.to_string() + "t"])) + Ok((0, vec![line.to_owned() + "t"])) } } diff --git a/src/line_buffer.rs b/src/line_buffer.rs index 56747bc2919d8c9b274439ba80b28a9ac9e27fdd..7d8000da84a8b29d8179797291bd2b4e3951612a 100644 --- a/src/line_buffer.rs +++ b/src/line_buffer.rs @@ -1,6 +1,10 @@ //! Line buffer with current cursor position +use std::cell::RefCell; +use std::fmt; use std::iter; use std::ops::{Deref, Range}; +use std::rc::Rc; +use std::string::Drain; use std_unicode::str::UnicodeStr; use unicode_segmentation::UnicodeSegmentation; use keymap::{At, CharSearch, Movement, RepeatCount, Word}; @@ -14,10 +18,25 @@ pub enum WordAction { UPPERCASE, } -#[derive(Debug)] +pub trait ChangeListener { + fn insert_char(&mut self, idx: usize, c: char); + fn insert_str(&mut self, idx: usize, string: &str); + fn delete(&mut self, idx: usize, string: &str); +} + pub struct LineBuffer { buf: String, // Edited line buffer pos: usize, // Current cursor position (byte position) + cl: Option<Rc<RefCell<ChangeListener>>>, +} + +impl fmt::Debug for LineBuffer { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("LineBuffer") + .field("buf", &self.buf) + .field("pos", &self.pos) + .finish() + } } impl LineBuffer { @@ -26,17 +45,23 @@ impl LineBuffer { LineBuffer { buf: String::with_capacity(capacity), pos: 0, + cl: None, } } #[cfg(test)] - pub fn init(line: &str, pos: usize) -> LineBuffer { + pub fn init(line: &str, pos: usize, cl: Option<Rc<RefCell<ChangeListener>>>) -> LineBuffer { let mut lb = Self::with_capacity(MAX_LINE); assert!(lb.insert_str(0, line)); lb.set_pos(pos); + lb.cl = cl; lb } + pub fn bind(&mut self, cl: Option<Rc<RefCell<ChangeListener>>>) { + self.cl = cl; + } + /// Extracts a string slice containing the entire buffer. pub fn as_str(&self) -> &str { &self.buf @@ -68,25 +93,27 @@ impl LineBuffer { /// Set line content (`buf`) and cursor position (`pos`). pub fn update(&mut self, buf: &str, pos: usize) { assert!(pos <= buf.len()); - self.buf.clear(); + let end = self.len(); + self.drain(0..end); let max = self.buf.capacity(); if buf.len() > max { - self.buf.push_str(&buf[..max]); + self.insert_str(0, &buf[..max]); if pos > max { self.pos = max; } else { self.pos = pos; } } else { - self.buf.push_str(buf); + self.insert_str(0, buf); self.pos = pos; } } /// Backup `src` pub fn backup(&mut self, src: &LineBuffer) { - self.buf.clear(); - self.buf.push_str(&src.buf); + let end = self.len(); + self.drain(0..end); + self.insert_str(0, &src.buf); self.pos = src.pos; } @@ -132,13 +159,11 @@ impl LineBuffer { return None; } let push = self.pos == self.buf.len(); - if push { - self.buf.reserve(shift); - for _ in 0..n { - self.buf.push(ch); - } - } else if n == 1 { + if n == 1 { self.buf.insert(self.pos, ch); + for cl in &self.cl { + cl.borrow_mut().insert_char(self.pos, ch); + } } else { let text = iter::repeat(ch).take(n).collect::<String>(); let pos = self.pos; @@ -157,14 +182,11 @@ impl LineBuffer { return None; } let push = self.pos == self.buf.len(); - if push { - self.buf.reserve(shift); - for _ in 0..n { - self.buf.push_str(text); - } + let pos = self.pos; + if n == 1 { + self.insert_str(pos, text); } else { let text = iter::repeat(text).take(n).collect::<String>(); - let pos = self.pos; self.insert_str(pos, &text); } self.pos += shift; @@ -173,7 +195,9 @@ impl LineBuffer { /// Delete previously yanked text and yank/paste `text` at current position. pub fn yank_pop(&mut self, yank_size: usize, text: &str) -> Option<bool> { - self.buf.drain((self.pos - yank_size)..self.pos); + let end = self.pos; + let start = end - yank_size; + self.drain(start..end); self.pos -= yank_size; self.yank(text, 1) } @@ -226,7 +250,8 @@ impl LineBuffer { pub fn delete(&mut self, n: RepeatCount) -> Option<String> { match self.next_pos(n) { Some(pos) => { - let chars = self.buf.drain(self.pos..pos).collect::<String>(); + let start = self.pos; + let chars = self.drain(start..pos).collect::<String>(); Some(chars) } None => None, @@ -235,35 +260,39 @@ impl LineBuffer { /// Delete the character at the left of the cursor. /// Basically that is what happens with the "Backspace" keyboard key. - pub fn backspace(&mut self, n: RepeatCount) -> Option<String> { + pub fn backspace(&mut self, n: RepeatCount) -> bool { match self.prev_pos(n) { Some(pos) => { - let chars = self.buf.drain(pos..self.pos).collect::<String>(); + let end = self.pos; + self.drain(pos..end); self.pos = pos; - Some(chars) + true } - None => None, + None => false, } } /// Kill the text from point to the end of the line. - pub fn kill_line(&mut self) -> Option<String> { + pub fn kill_line(&mut self) -> bool { if !self.buf.is_empty() && self.pos < self.buf.len() { - let text = self.buf.drain(self.pos..).collect(); - Some(text) + let start = self.pos; + let end = self.buf.len(); + self.drain(start..end); + true } else { - None + false } } /// Kill backward from point to the beginning of the line. - pub fn discard_line(&mut self) -> Option<String> { + pub fn discard_line(&mut self) -> bool { if self.pos > 0 && !self.buf.is_empty() { - let text = self.buf.drain(..self.pos).collect(); + let end = self.pos; + self.drain(0..end); self.pos = 0; - Some(text) + true } else { - None + false } } @@ -288,9 +317,7 @@ impl LineBuffer { return None; } let mut sow = 0; - let mut gis = self.buf[..pos] - .grapheme_indices(true) - .rev(); + let mut gis = self.buf[..pos].grapheme_indices(true).rev(); 'outer: for _ in 0..n { let mut gj = gis.next(); 'inner: loop { @@ -331,13 +358,14 @@ impl LineBuffer { /// Delete the previous word, maintaining the cursor at the start of the /// current word. - pub fn delete_prev_word(&mut self, word_def: Word, n: RepeatCount) -> Option<String> { + pub fn delete_prev_word(&mut self, word_def: Word, n: RepeatCount) -> bool { if let Some(pos) = self.prev_word_pos(self.pos, word_def, n) { - let word = self.buf.drain(pos..self.pos).collect(); + let end = self.pos; + self.drain(pos..end); self.pos = pos; - Some(word) + true } else { - None + false } } @@ -443,13 +471,18 @@ impl LineBuffer { }; if let Some(pos) = search_result { Some(match *cs { - CharSearch::Backward(_) => pos, - CharSearch::BackwardAfter(c) => pos + c.len_utf8(), - CharSearch::Forward(_) => shift + pos, - CharSearch::ForwardBefore(_) => { - shift + pos - self.buf[..shift + pos].chars().next_back().unwrap().len_utf8() - } - }) + CharSearch::Backward(_) => pos, + CharSearch::BackwardAfter(c) => pos + c.len_utf8(), + CharSearch::Forward(_) => shift + pos, + CharSearch::ForwardBefore(_) => { + shift + pos - + self.buf[..shift + pos] + .chars() + .next_back() + .unwrap() + .len_utf8() + } + }) } else { None } @@ -466,34 +499,41 @@ impl LineBuffer { /// Kill from the cursor to the end of the current word, /// or, if between words, to the end of the next word. - pub fn delete_word(&mut self, at: At, word_def: Word, n: RepeatCount) -> Option<String> { + pub fn delete_word(&mut self, at: At, word_def: Word, n: RepeatCount) -> bool { if let Some(pos) = self.next_word_pos(self.pos, at, word_def, n) { - let word = self.buf.drain(self.pos..pos).collect(); - Some(word) + let start = self.pos; + self.drain(start..pos); + true } else { - None + false } } - pub fn delete_to(&mut self, cs: CharSearch, n: RepeatCount) -> Option<String> { + pub fn delete_to(&mut self, cs: CharSearch, n: RepeatCount) -> bool { let search_result = match cs { CharSearch::ForwardBefore(c) => self.search_char_pos(&CharSearch::Forward(c), n), _ => self.search_char_pos(&cs, n), }; if let Some(pos) = search_result { - let chunk = match cs { + match cs { CharSearch::Backward(_) | CharSearch::BackwardAfter(_) => { let end = self.pos; self.pos = pos; - self.buf.drain(pos..end).collect() + self.drain(pos..end); + } + CharSearch::ForwardBefore(_) => { + let start = self.pos; + self.drain(start..pos); + } + CharSearch::Forward(c) => { + let start = self.pos; + self.drain(start..pos + c.len_utf8()); } - CharSearch::ForwardBefore(_) => self.buf.drain(self.pos..pos).collect(), - CharSearch::Forward(c) => self.buf.drain(self.pos..pos + c.len_utf8()).collect(), }; - Some(chunk) + true } else { - None + false } } @@ -515,7 +555,7 @@ impl LineBuffer { if start == end { return false; } - let word = self.buf.drain(start..end).collect::<String>(); + let word = self.drain(start..end).collect::<String>(); let result = match a { WordAction::CAPITALIZE => { let ch = (&word).graphemes(true).next().unwrap(); @@ -548,12 +588,12 @@ impl LineBuffer { return false; } - let w1 = self.buf[w1_beg..w1_end].to_string(); + let w1 = self.buf[w1_beg..w1_end].to_owned(); - let w2 = self.buf.drain(w2_beg..w2_end).collect::<String>(); + let w2 = self.drain(w2_beg..w2_end).collect::<String>(); self.insert_str(w2_beg, &w1); - self.buf.drain(w1_beg..w1_end); + self.drain(w1_beg..w1_end); self.insert_str(w1_beg, &w2); self.pos = w2_end; @@ -564,12 +604,15 @@ impl LineBuffer { /// and positions the cursor to the end of text. pub fn replace(&mut self, range: Range<usize>, text: &str) { let start = range.start; - self.buf.drain(range); + self.drain(range); self.insert_str(start, text); self.pos = start + text.len(); } pub fn insert_str(&mut self, idx: usize, s: &str) -> bool { + for cl in &self.cl { + cl.borrow_mut().insert_str(idx, s); + } if idx == self.buf.len() { self.buf.push_str(s); true @@ -580,7 +623,14 @@ impl LineBuffer { } pub fn delete_range(&mut self, range: Range<usize>) { - self.buf.drain(range); + self.drain(range); + } + + fn drain(&mut self, range: Range<usize>) -> Drain { + for cl in &self.cl { + cl.borrow_mut().delete(range.start, &self.buf[range.start..range.end]); + } + self.buf.drain(range) } pub fn copy(&self, mvt: Movement) -> Option<String> { @@ -593,26 +643,26 @@ impl LineBuffer { if self.pos == 0 { None } else { - Some(self.buf[..self.pos].to_string()) + Some(self.buf[..self.pos].to_owned()) } } Movement::EndOfLine => { if self.pos == self.buf.len() { None } else { - Some(self.buf[self.pos..].to_string()) + Some(self.buf[self.pos..].to_owned()) } } Movement::BackwardWord(n, word_def) => { if let Some(pos) = self.prev_word_pos(self.pos, word_def, n) { - Some(self.buf[pos..self.pos].to_string()) + Some(self.buf[pos..self.pos].to_owned()) } else { None } } Movement::ForwardWord(n, at, word_def) => { if let Some(pos) = self.next_word_pos(self.pos, at, word_def, n) { - Some(self.buf[self.pos..pos].to_string()) + Some(self.buf[self.pos..pos].to_owned()) } else { None } @@ -626,27 +676,27 @@ impl LineBuffer { }; if let Some(pos) = search_result { Some(match cs { - CharSearch::Backward(_) | - CharSearch::BackwardAfter(_) => self.buf[pos..self.pos].to_string(), - CharSearch::ForwardBefore(_) => self.buf[self.pos..pos].to_string(), - CharSearch::Forward(c) => { - self.buf[self.pos..pos + c.len_utf8()].to_string() - } - }) + CharSearch::Backward(_) | + CharSearch::BackwardAfter(_) => self.buf[pos..self.pos].to_owned(), + CharSearch::ForwardBefore(_) => self.buf[self.pos..pos].to_owned(), + CharSearch::Forward(c) => { + self.buf[self.pos..pos + c.len_utf8()].to_owned() + } + }) } else { None } } Movement::BackwardChar(n) => { if let Some(pos) = self.prev_pos(n) { - Some(self.buf[pos..self.pos].to_string()) + Some(self.buf[pos..self.pos].to_owned()) } else { None } } Movement::ForwardChar(n) => { if let Some(pos) = self.next_pos(n) { - Some(self.buf[self.pos..pos].to_string()) + Some(self.buf[self.pos..pos].to_owned()) } else { None } @@ -688,29 +738,55 @@ fn is_other_char(grapheme: &str) -> bool { #[cfg(test)] mod test { + use std::cell::RefCell; + use std::rc::Rc; use keymap::{At, CharSearch, Word}; - use super::{LineBuffer, MAX_LINE, WordAction}; + use super::{ChangeListener, LineBuffer, MAX_LINE, WordAction}; + + struct Listener { + deleted_str: Option<String>, + } + + impl Listener { + fn new() -> Rc<RefCell<Listener>> { + let l = Listener { deleted_str: None }; + Rc::new(RefCell::new(l)) + } + + fn assert_deleted_str_eq(&self, expected: &str) { + let actual = self.deleted_str.as_ref().expect("no deleted string"); + assert_eq!(expected, actual) + } + } + + impl ChangeListener for Listener { + fn insert_char(&mut self, _: usize, _: char) {} + fn insert_str(&mut self, _: usize, _: &str) {} + fn delete(&mut self, _: usize, string: &str) { + self.deleted_str = Some(string.to_owned()); + } + } #[test] fn next_pos() { - let s = LineBuffer::init("ö̲g̈", 0); + let s = LineBuffer::init("ö̲g̈", 0, None); assert_eq!(7, s.len()); let pos = s.next_pos(1); assert_eq!(Some(4), pos); - let s = LineBuffer::init("ö̲g̈", 4); + let s = LineBuffer::init("ö̲g̈", 4, None); let pos = s.next_pos(1); assert_eq!(Some(7), pos); } #[test] fn prev_pos() { - let s = LineBuffer::init("ö̲g̈", 4); + let s = LineBuffer::init("ö̲g̈", 4, None); assert_eq!(7, s.len()); let pos = s.prev_pos(1); assert_eq!(Some(0), pos); - let s = LineBuffer::init("ö̲g̈", 7); + let s = LineBuffer::init("ö̲g̈", 7, None); let pos = s.prev_pos(1); assert_eq!(Some(4), pos); } @@ -737,7 +813,7 @@ mod test { #[test] fn yank_after() { - let mut s = LineBuffer::init("αß", 2); + let mut s = LineBuffer::init("αß", 2, None); s.move_forward(1); let ok = s.yank("γδε", 1); assert_eq!(Some(true), ok); @@ -747,7 +823,7 @@ mod test { #[test] fn yank_before() { - let mut s = LineBuffer::init("αε", 2); + let mut s = LineBuffer::init("αε", 2, None); let ok = s.yank("ßγδ", 1); assert_eq!(Some(false), ok); assert_eq!("αßγδε", s.buf); @@ -756,7 +832,7 @@ mod test { #[test] fn moves() { - let mut s = LineBuffer::init("αß", 4); + let mut s = LineBuffer::init("αß", 4, None); let ok = s.move_backward(1); assert_eq!("αß", s.buf); assert_eq!(2, s.pos); @@ -780,7 +856,7 @@ mod test { #[test] fn move_grapheme() { - let mut s = LineBuffer::init("ag̈", 4); + let mut s = LineBuffer::init("ag̈", 4, None); assert_eq!(4, s.len()); let ok = s.move_backward(1); assert_eq!(true, ok); @@ -793,36 +869,41 @@ mod test { #[test] fn delete() { - let mut s = LineBuffer::init("αß", 2); + let cl = Listener::new(); + let mut s = LineBuffer::init("αß", 2, Some(cl.clone())); let chars = s.delete(1); assert_eq!("α", s.buf); assert_eq!(2, s.pos); - assert_eq!(Some("ß".to_string()), chars); + assert_eq!(Some("ß".to_owned()), chars); - let chars = s.backspace(1); + let ok = s.backspace(1); assert_eq!("", s.buf); assert_eq!(0, s.pos); - assert_eq!(Some("α".to_string()), chars); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("α"); } #[test] fn kill() { - let mut s = LineBuffer::init("αßγδε", 6); - let text = s.kill_line(); + let cl = Listener::new(); + let mut s = LineBuffer::init("αßγδε", 6, Some(cl.clone())); + let ok = s.kill_line(); assert_eq!("αßγ", s.buf); assert_eq!(6, s.pos); - assert_eq!(Some("δε".to_string()), text); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("δε"); s.pos = 4; - let text = s.discard_line(); + let ok = s.discard_line(); assert_eq!("γ", s.buf); assert_eq!(0, s.pos); - assert_eq!(Some("αß".to_string()), text); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("αß"); } #[test] fn transpose() { - let mut s = LineBuffer::init("aßc", 1); + let mut s = LineBuffer::init("aßc", 1, None); let ok = s.transpose_chars(); assert_eq!("ßac", s.buf); assert_eq!(3, s.pos); @@ -845,7 +926,7 @@ mod test { #[test] fn move_to_prev_word() { - let mut s = LineBuffer::init("a ß c", 6); + let mut s = LineBuffer::init("a ß c", 6, None); let ok = s.move_to_prev_word(Word::Emacs, 1); assert_eq!("a ß c", s.buf); assert_eq!(2, s.pos); @@ -854,7 +935,7 @@ mod test { #[test] fn move_to_prev_vi_word() { - let mut s = LineBuffer::init("alpha ,beta/rho; mu", 19); + let mut s = LineBuffer::init("alpha ,beta/rho; mu", 19, None); let ok = s.move_to_prev_word(Word::Vi, 1); assert!(ok); assert_eq!(17, s.pos); @@ -882,7 +963,7 @@ mod test { #[test] fn move_to_prev_big_word() { - let mut s = LineBuffer::init("alpha ,beta/rho; mu", 19); + let mut s = LineBuffer::init("alpha ,beta/rho; mu", 19, None); let ok = s.move_to_prev_word(Word::Big, 1); assert!(ok); assert_eq!(17, s.pos); @@ -898,17 +979,17 @@ mod test { #[test] fn move_to_forward() { - let mut s = LineBuffer::init("αßγδε", 2); + let mut s = LineBuffer::init("αßγδε", 2, None); let ok = s.move_to(CharSearch::ForwardBefore('ε'), 1); assert_eq!(true, ok); assert_eq!(6, s.pos); - let mut s = LineBuffer::init("αßγδε", 2); + let mut s = LineBuffer::init("αßγδε", 2, None); let ok = s.move_to(CharSearch::Forward('ε'), 1); assert_eq!(true, ok); assert_eq!(8, s.pos); - let mut s = LineBuffer::init("αßγδε", 2); + let mut s = LineBuffer::init("αßγδε", 2, None); let ok = s.move_to(CharSearch::Forward('ε'), 10); assert_eq!(true, ok); assert_eq!(8, s.pos); @@ -916,12 +997,12 @@ mod test { #[test] fn move_to_backward() { - let mut s = LineBuffer::init("αßγδε", 8); + let mut s = LineBuffer::init("αßγδε", 8, None); let ok = s.move_to(CharSearch::BackwardAfter('ß'), 1); assert_eq!(true, ok); assert_eq!(4, s.pos); - let mut s = LineBuffer::init("αßγδε", 8); + let mut s = LineBuffer::init("αßγδε", 8, None); let ok = s.move_to(CharSearch::Backward('ß'), 1); assert_eq!(true, ok); assert_eq!(2, s.pos); @@ -929,16 +1010,18 @@ mod test { #[test] fn delete_prev_word() { - let mut s = LineBuffer::init("a ß c", 6); - let text = s.delete_prev_word(Word::Big, 1); + let cl = Listener::new(); + let mut s = LineBuffer::init("a ß c", 6, Some(cl.clone())); + let ok = s.delete_prev_word(Word::Big, 1); assert_eq!("a c", s.buf); assert_eq!(2, s.pos); - assert_eq!(Some("ß ".to_string()), text); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("ß "); } #[test] fn move_to_next_word() { - let mut s = LineBuffer::init("a ß c", 1); + let mut s = LineBuffer::init("a ß c", 1, None); let ok = s.move_to_next_word(At::AfterEnd, Word::Emacs, 1); assert_eq!("a ß c", s.buf); assert_eq!(4, s.pos); @@ -947,7 +1030,7 @@ mod test { #[test] fn move_to_end_of_word() { - let mut s = LineBuffer::init("a ßeta c", 1); + let mut s = LineBuffer::init("a ßeta c", 1, None); let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1); assert_eq!("a ßeta c", s.buf); assert_eq!(6, s.pos); @@ -956,7 +1039,7 @@ mod test { #[test] fn move_to_end_of_vi_word() { - let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0); + let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0, None); let ok = s.move_to_next_word(At::BeforeEnd, Word::Vi, 1); assert!(ok); assert_eq!(4, s.pos); @@ -984,7 +1067,7 @@ mod test { #[test] fn move_to_end_of_big_word() { - let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0); + let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0, None); let ok = s.move_to_next_word(At::BeforeEnd, Word::Big, 1); assert!(ok); assert_eq!(4, s.pos); @@ -1000,7 +1083,7 @@ mod test { #[test] fn move_to_start_of_word() { - let mut s = LineBuffer::init("a ß c", 2); + let mut s = LineBuffer::init("a ß c", 2, None); let ok = s.move_to_next_word(At::Start, Word::Emacs, 1); assert_eq!("a ß c", s.buf); assert_eq!(6, s.pos); @@ -1009,7 +1092,7 @@ mod test { #[test] fn move_to_start_of_vi_word() { - let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0); + let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0, None); let ok = s.move_to_next_word(At::Start, Word::Vi, 1); assert!(ok); assert_eq!(6, s.pos); @@ -1037,7 +1120,7 @@ mod test { #[test] fn move_to_start_of_big_word() { - let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0); + let mut s = LineBuffer::init("alpha ,beta/rho; mu", 0, None); let ok = s.move_to_next_word(At::Start, Word::Big, 1); assert!(ok); assert_eq!(6, s.pos); @@ -1053,76 +1136,87 @@ mod test { #[test] fn delete_word() { - let mut s = LineBuffer::init("a ß c", 1); - let text = s.delete_word(At::AfterEnd, Word::Emacs, 1); + let cl = Listener::new(); + let mut s = LineBuffer::init("a ß c", 1, Some(cl.clone())); + let ok = s.delete_word(At::AfterEnd, Word::Emacs, 1); assert_eq!("a c", s.buf); assert_eq!(1, s.pos); - assert_eq!(Some(" ß".to_string()), text); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq(" ß"); - let mut s = LineBuffer::init("test", 0); - let text = s.delete_word(At::AfterEnd, Word::Vi, 1); + let mut s = LineBuffer::init("test", 0, Some(cl.clone())); + let ok = s.delete_word(At::AfterEnd, Word::Vi, 1); assert_eq!("", s.buf); assert_eq!(0, s.pos); - assert_eq!(Some("test".to_string()), text); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("test"); } #[test] fn delete_til_start_of_word() { - let mut s = LineBuffer::init("a ß c", 2); - let text = s.delete_word(At::Start, Word::Emacs, 1); + let cl = Listener::new(); + let mut s = LineBuffer::init("a ß c", 2, Some(cl.clone())); + let ok = s.delete_word(At::Start, Word::Emacs, 1); assert_eq!("a c", s.buf); assert_eq!(2, s.pos); - assert_eq!(Some("ß ".to_string()), text); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("ß "); } #[test] fn delete_to_forward() { - let mut s = LineBuffer::init("αßγδε", 2); - let text = s.delete_to(CharSearch::ForwardBefore('ε'), 1); - assert_eq!(Some("ßγδ".to_string()), text); + let cl = Listener::new(); + let mut s = LineBuffer::init("αßγδε", 2, Some(cl.clone())); + let ok = s.delete_to(CharSearch::ForwardBefore('ε'), 1); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("ßγδ"); assert_eq!("αε", s.buf); assert_eq!(2, s.pos); - let mut s = LineBuffer::init("αßγδε", 2); - let text = s.delete_to(CharSearch::Forward('ε'), 1); - assert_eq!(Some("ßγδε".to_string()), text); + let mut s = LineBuffer::init("αßγδε", 2, Some(cl.clone())); + let ok = s.delete_to(CharSearch::Forward('ε'), 1); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("ßγδε"); assert_eq!("α", s.buf); assert_eq!(2, s.pos); } #[test] fn delete_to_backward() { - let mut s = LineBuffer::init("αßγδε", 8); - let text = s.delete_to(CharSearch::BackwardAfter('α'), 1); - assert_eq!(Some("ßγδ".to_string()), text); + let cl = Listener::new(); + let mut s = LineBuffer::init("αßγδε", 8, Some(cl.clone())); + let ok = s.delete_to(CharSearch::BackwardAfter('α'), 1); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("ßγδ"); assert_eq!("αε", s.buf); assert_eq!(2, s.pos); - let mut s = LineBuffer::init("αßγδε", 8); - let text = s.delete_to(CharSearch::Backward('ß'), 1); - assert_eq!(Some("ßγδ".to_string()), text); + let mut s = LineBuffer::init("αßγδε", 8, Some(cl.clone())); + let ok = s.delete_to(CharSearch::Backward('ß'), 1); + assert_eq!(true, ok); + cl.borrow().assert_deleted_str_eq("ßγδ"); assert_eq!("αε", s.buf); assert_eq!(2, s.pos); } #[test] fn edit_word() { - let mut s = LineBuffer::init("a ßeta c", 1); + let mut s = LineBuffer::init("a ßeta c", 1, None); assert!(s.edit_word(WordAction::UPPERCASE)); assert_eq!("a SSETA c", s.buf); assert_eq!(7, s.pos); - let mut s = LineBuffer::init("a ßetA c", 1); + let mut s = LineBuffer::init("a ßetA c", 1, None); assert!(s.edit_word(WordAction::LOWERCASE)); assert_eq!("a ßeta c", s.buf); assert_eq!(7, s.pos); - let mut s = LineBuffer::init("a ßETA c", 1); + let mut s = LineBuffer::init("a ßETA c", 1, None); assert!(s.edit_word(WordAction::CAPITALIZE)); assert_eq!("a SSeta c", s.buf); assert_eq!(7, s.pos); - let mut s = LineBuffer::init("test", 1); + let mut s = LineBuffer::init("test", 1, None); assert!(s.edit_word(WordAction::CAPITALIZE)); assert_eq!("tEst", s.buf); assert_eq!(4, s.pos); @@ -1130,20 +1224,20 @@ mod test { #[test] fn transpose_words() { - let mut s = LineBuffer::init("ßeta / δelta__", 15); + let mut s = LineBuffer::init("ßeta / δelta__", 15, None); assert!(s.transpose_words(1)); assert_eq!("δelta__ / ßeta", s.buf); assert_eq!(16, s.pos); - let mut s = LineBuffer::init("ßeta / δelta", 14); + let mut s = LineBuffer::init("ßeta / δelta", 14, None); assert!(s.transpose_words(1)); assert_eq!("δelta / ßeta", s.buf); assert_eq!(14, s.pos); - let mut s = LineBuffer::init(" / δelta", 8); + let mut s = LineBuffer::init(" / δelta", 8, None); assert!(!s.transpose_words(1)); - let mut s = LineBuffer::init("ßeta / __", 9); + let mut s = LineBuffer::init("ßeta / __", 9, None); assert!(!s.transpose_words(1)); } } diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 7b77b6833cd11450d6c2560f7b7130cc8b9c7ac9..83b9479938bc02e18d4907c481a0df0be6f62841 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -100,9 +100,9 @@ impl PosixRawReader { fn new(config: &Config) -> Result<PosixRawReader> { let stdin = StdinRaw {}; Ok(PosixRawReader { - chars: stdin.chars(), - timeout_ms: config.keyseq_timeout(), - }) + chars: stdin.chars(), + timeout_ms: config.keyseq_timeout(), + }) } fn escape_sequence(&mut self) -> Result<KeyPress> { diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 6ad88444310b3df978ccfac4d4d38f30602a9f18..b03bddf630823c218cfa8385da735323f4c9a592 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -79,9 +79,9 @@ impl ConsoleRawReader { pub fn new() -> Result<ConsoleRawReader> { let handle = try!(get_std_handle(STDIN_FILENO)); Ok(ConsoleRawReader { - handle: handle, - buf: None, - }) + handle: handle, + buf: None, + }) } } @@ -288,9 +288,9 @@ impl Term for Console { let raw = raw | winapi::wincon::ENABLE_WINDOW_INPUT; check!(kernel32::SetConsoleMode(self.stdin_handle, raw)); Ok(Mode { - original_mode: original_mode, - stdin_handle: self.stdin_handle, - }) + original_mode: original_mode, + stdin_handle: self.stdin_handle, + }) } fn create_reader(&self, _: &Config) -> Result<ConsoleRawReader> { diff --git a/src/undo.rs b/src/undo.rs index 5c6128c8da63ff04190e137b50d1494ce43135ac..1b070a89907660e1e980695fae88977d6f935fc6 100644 --- a/src/undo.rs +++ b/src/undo.rs @@ -6,7 +6,7 @@ use unicode_segmentation::UnicodeSegmentation; enum Action { Insert(String), // QuotedInsert, SelfInsert, Yank Delete(String), /* BackwardDeleteChar, BackwardKillWord, DeleteChar, KillLine, KillWholeLine, KillWord, UnixLikeDiscard, ViDeleteTo */ - Replace(String, String), /* CapitalizeWord, Complete, DowncaseWord, Replace, TransposeChars, TransposeWords, UpcaseWord, YankPop */ + //Replace(String, String), /* CapitalizeWord, Complete, DowncaseWord, Replace, TransposeChars, TransposeWords, UpcaseWord, YankPop */ } struct Change { @@ -24,9 +24,9 @@ impl Change { line.insert_str(self.idx, text); line.set_pos(self.idx + text.len()); } - Action::Replace(ref old, ref new) => { + /*Action::Replace(ref old, ref new) => { line.replace(self.idx..self.idx + new.len(), old); - } + }*/ } } @@ -39,9 +39,9 @@ impl Change { Action::Delete(ref text) => { line.delete_range(self.idx..self.idx + text.len()); } - Action::Replace(ref old, ref new) => { + /*Action::Replace(ref old, ref new) => { line.replace(self.idx..self.idx + old.len(), new); - } + }*/ } } @@ -117,9 +117,9 @@ impl Changeset { pub fn insert_str<S: Into<String>>(&mut self, idx: usize, string: S) { self.redos.clear(); self.undos.push(Change { - idx: idx, - action: Action::Insert(string.into()), - }); + idx: idx, + action: Action::Insert(string.into()), + }); } pub fn delete<S: AsRef<str> + Into<String>>(&mut self, idx: usize, string: S) { @@ -127,9 +127,9 @@ impl Changeset { if !Self::single_char(string.as_ref()) { self.undos.push(Change { - idx: idx, - action: Action::Delete(string.into()), - }); + idx: idx, + action: Action::Delete(string.into()), + }); return; } let last_change = self.undos.pop(); @@ -152,16 +152,16 @@ impl Changeset { } else { self.undos.push(last_change); self.undos.push(Change { - idx: idx, - action: Action::Delete(string.into()), - }); + idx: idx, + action: Action::Delete(string.into()), + }); } } None => { self.undos.push(Change { - idx: idx, - action: Action::Delete(string.into()), - }); + idx: idx, + action: Action::Delete(string.into()), + }); } }; } @@ -172,13 +172,13 @@ impl Changeset { graphemes.next().is_none() } - pub fn replace<S: Into<String>>(&mut self, idx: usize, old: String, new: S) { + /*pub fn replace<S: Into<String>>(&mut self, idx: usize, old: String, new: S) { self.redos.clear(); self.undos.push(Change { idx: idx, action: Action::Replace(old.into(), new.into()), }); - } + }*/ pub fn undo(&mut self, line: &mut LineBuffer) -> bool { match self.undos.pop() { @@ -202,6 +202,11 @@ impl Changeset { None => false, } } + + pub fn clear(&mut self) { + self.undos.clear(); + self.redos.clear(); + } } #[cfg(test)] @@ -231,7 +236,7 @@ mod tests { #[test] fn test_undo_insert() { - let mut buf = LineBuffer::init("", 0); + let mut buf = LineBuffer::init("", 0, None); buf.insert_str(0, "Hello"); buf.insert_str(5, ", world!"); let mut cs = Changeset::new(); @@ -252,12 +257,12 @@ mod tests { #[test] fn test_undo_delete() { - let mut buf = LineBuffer::init("", 0); + let mut buf = LineBuffer::init("", 0, None); buf.insert_str(0, "Hello"); let mut cs = Changeset::new(); assert_eq!(buf.as_str(), "Hello"); - cs.delete(5, ", world!".to_string()); + cs.delete(5, ", world!".to_owned()); cs.undo(&mut buf); assert_eq!(buf.as_str(), "Hello, world!"); @@ -268,12 +273,12 @@ mod tests { #[test] fn test_delete_chars() { - let mut buf = LineBuffer::init("", 0); + let mut buf = LineBuffer::init("", 0, None); buf.insert_str(0, "Hlo"); let mut cs = Changeset::new(); - cs.delete(1, "e".to_string()); - cs.delete(1, "l".to_string()); + cs.delete(1, "e".to_owned()); + cs.delete(1, "l".to_owned()); assert_eq!(1, cs.undos.len()); cs.undo(&mut buf); @@ -282,33 +287,33 @@ mod tests { #[test] fn test_backspace_chars() { - let mut buf = LineBuffer::init("", 0); + let mut buf = LineBuffer::init("", 0, None); buf.insert_str(0, "Hlo"); let mut cs = Changeset::new(); - cs.delete(2, "l".to_string()); - cs.delete(1, "e".to_string()); + cs.delete(2, "l".to_owned()); + cs.delete(1, "e".to_owned()); assert_eq!(1, cs.undos.len()); cs.undo(&mut buf); assert_eq!(buf.as_str(), "Hello"); } - #[test] + /*#[test] fn test_undo_replace() { - let mut buf = LineBuffer::init("", 0); + let mut buf = LineBuffer::init("", 0, None); buf.insert_str(0, "Hello, world!"); let mut cs = Changeset::new(); assert_eq!(buf.as_str(), "Hello, world!"); buf.replace(1..5, "i"); assert_eq!(buf.as_str(), "Hi, world!"); - cs.replace(1, "ello".to_string(), "i"); + cs.replace(1, "ello".to_owned(), "i"); cs.undo(&mut buf); assert_eq!(buf.as_str(), "Hello, world!"); cs.redo(&mut buf); assert_eq!(buf.as_str(), "Hi, world!"); - } + }*/ }