From c024d3c71eea66f11a3bd124f5cb8a111e57c080 Mon Sep 17 00:00:00 2001 From: gwenn <gtreguier@gmail.com> Date: Sat, 22 Apr 2017 11:11:52 +0200 Subject: [PATCH] Make replace atomic (relative to ChangeListener) --- src/kill_ring.rs | 6 ++--- src/lib.rs | 9 +++---- src/line_buffer.rs | 61 ++++++++++++++++++++++++++++++------------- src/undo.rs | 64 +++++++++++++++++++++++++++++----------------- 4 files changed, 90 insertions(+), 50 deletions(-) diff --git a/src/kill_ring.rs b/src/kill_ring.rs index d76d94b1..c168570d 100644 --- a/src/kill_ring.rs +++ b/src/kill_ring.rs @@ -1,5 +1,5 @@ //! Kill Ring -use line_buffer::{ChangeListener, Direction}; +use line_buffer::{DeleteListener, Direction}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum Action { @@ -112,9 +112,7 @@ impl KillRing { } } -impl ChangeListener for KillRing { - fn insert_char(&mut self, _: usize, _: char) {} - fn insert_str(&mut self, _: usize, _: &str) {} +impl DeleteListener for KillRing { fn delete(&mut self, _: usize, string: &str, dir: Direction) { if !self.killing { return; diff --git a/src/lib.rs b/src/lib.rs index 6e11c9ea..d3ecc1f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -897,9 +897,8 @@ fn readline_edit<C: Completer>(prompt: &str, editor.history.len(), editor.custom_bindings.clone()); - s.line.bind(editor.kill_ring.clone()); - // must be the last - s.line.bind(s.changes.clone()); + s.line.set_delete_listener(editor.kill_ring.clone()); + s.line.set_change_listener(s.changes.clone()); try!(s.refresh_line()); @@ -1104,11 +1103,11 @@ fn readline_edit<C: Completer>(prompt: &str, editor.kill_ring.borrow_mut().stop_killing(); } Cmd::Undo => { - s.line.unbind(); + s.line.remove_change_listener(); if s.changes.borrow_mut().undo(&mut s.line) { try!(s.refresh_line()); } - s.line.bind(s.changes.clone()); + s.line.set_change_listener(s.changes.clone()); } Cmd::Interrupt => { return Err(error::ReadlineError::Interrupted); diff --git a/src/line_buffer.rs b/src/line_buffer.rs index c9f251fc..c6b1bbcd 100644 --- a/src/line_buffer.rs +++ b/src/line_buffer.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; use std::fmt; use std::iter; -use std::ops::{Deref, Range}; +use std::ops::{Deref, Index, Range}; use std::rc::Rc; use std::string::Drain; use std_unicode::str::UnicodeStr; @@ -30,16 +30,21 @@ impl Default for Direction { } } -pub trait ChangeListener { +pub trait DeleteListener { + fn delete(&mut self, idx: usize, string: &str, dir: Direction); +} + +pub trait ChangeListener: DeleteListener { 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, dir: Direction); + fn replace(&mut self, idx: usize, old: &str, new: &str); } pub struct LineBuffer { buf: String, // Edited line buffer pos: usize, // Current cursor position (byte position) - cl: Vec<Rc<RefCell<ChangeListener>>>, + dl: Option<Rc<RefCell<DeleteListener>>>, + cl: Option<Rc<RefCell<ChangeListener>>>, } impl fmt::Debug for LineBuffer { @@ -57,7 +62,8 @@ impl LineBuffer { LineBuffer { buf: String::with_capacity(capacity), pos: 0, - cl: Vec::new(), + dl: None, + cl: None, } } @@ -66,17 +72,21 @@ impl LineBuffer { let mut lb = Self::with_capacity(MAX_LINE); assert!(lb.insert_str(0, line)); lb.set_pos(pos); - if cl.is_some() { - lb.bind(cl.unwrap()); - } + lb.cl = cl; lb } - pub fn bind(&mut self, cl: Rc<RefCell<ChangeListener>>) { - self.cl.push(cl); + pub fn set_delete_listener(&mut self, dl: Rc<RefCell<DeleteListener>>) { + self.dl = Some(dl); + } + pub fn remove_delete_listener(&mut self) { + self.dl = None; } - pub fn unbind(&mut self) { - self.cl.pop(); + pub fn set_change_listener(&mut self, dl: Rc<RefCell<ChangeListener>>) { + self.cl = Some(dl); + } + pub fn remove_change_listener(&mut self) { + self.cl = None; } /// Extracts a string slice containing the entire buffer. @@ -616,8 +626,16 @@ 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.drain(range, Direction::default()); - self.insert_str(start, text); + for cl in &self.cl { + cl.borrow_mut() + .replace(start, self.buf.index(range.clone()), text); + } + self.buf.drain(range); + if start == self.buf.len() { + self.buf.push_str(text); + } else { + self.buf.insert_str(start, text); + } self.pos = start + text.len(); } @@ -640,6 +658,10 @@ impl LineBuffer { } fn drain(&mut self, range: Range<usize>, dir: Direction) -> Drain { + for dl in &self.dl { + dl.borrow_mut() + .delete(range.start, &self.buf[range.start..range.end], dir); + } for cl in &self.cl { cl.borrow_mut() .delete(range.start, &self.buf[range.start..range.end], dir); @@ -764,7 +786,7 @@ mod test { use std::cell::RefCell; use std::rc::Rc; use keymap::{At, CharSearch, Word}; - use super::{ChangeListener, Direction, LineBuffer, MAX_LINE, WordAction}; + use super::{ChangeListener, DeleteListener, Direction, LineBuffer, MAX_LINE, WordAction}; struct Listener { deleted_str: Option<String>, @@ -782,13 +804,16 @@ mod test { } } - impl ChangeListener for Listener { - fn insert_char(&mut self, _: usize, _: char) {} - fn insert_str(&mut self, _: usize, _: &str) {} + impl DeleteListener for Listener { fn delete(&mut self, _: usize, string: &str, _: Direction) { self.deleted_str = Some(string.to_owned()); } } + impl ChangeListener for Listener { + fn insert_char(&mut self, _: usize, _: char) {} + fn insert_str(&mut self, _: usize, _: &str) {} + fn replace(&mut self, _: usize, _: &str, _: &str) {} + } #[test] fn next_pos() { diff --git a/src/undo.rs b/src/undo.rs index 0afe419f..e2febeb1 100644 --- a/src/undo.rs +++ b/src/undo.rs @@ -1,7 +1,7 @@ //! Undo API use std::fmt::Debug; -use line_buffer::{ChangeListener, Direction, LineBuffer}; +use line_buffer::{ChangeListener, DeleteListener, Direction, LineBuffer}; use std_unicode::str::UnicodeStr; use unicode_segmentation::UnicodeSegmentation; @@ -10,7 +10,11 @@ enum Change { End, Insert { idx: usize, text: String }, // QuotedInsert, SelfInsert, Yank Delete { idx: usize, text: String }, /* BackwardDeleteChar, BackwardKillWord, DeleteChar, KillLine, KillWholeLine, KillWord, UnixLikeDiscard, ViDeleteTo */ - // Replace {idx: usize, old: String, new: String}, /* CapitalizeWord, Complete, DowncaseWord, Replace, TransposeChars, TransposeWords, UpcaseWord, YankPop */ + Replace { + idx: usize, + old: String, + new: String, + }, /* CapitalizeWord, Complete, DowncaseWord, Replace, TransposeChars, TransposeWords, UpcaseWord, YankPop */ } impl Change { @@ -26,9 +30,13 @@ impl Change { line.insert_str(idx, text); line.set_pos(idx + text.len()); } - /*Change::Replace{idx, ref old, ref new} => { + Change::Replace { + idx, + ref old, + ref new, + } => { line.replace(idx..idx + new.len(), old); - }*/ + } } } @@ -44,9 +52,13 @@ impl Change { Change::Delete { idx, ref text } => { line.delete_range(idx..idx + text.len()); } - /*Change::Replace{idx, ref old, ref new} => { + Change::Replace { + idx, + ref old, + ref new, + } => { line.replace(idx..idx + old.len(), new); - }*/ + } } } @@ -208,14 +220,15 @@ 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: S, new: S) { self.redos.clear(); - self.undos.push(Change::Replace { - idx: idx, - old: old.into(), - new: new.into(), - }); - }*/ + self.undos + .push(Change::Replace { + idx: idx, + old: old.into(), + new: new.into(), + }); + } pub fn undo(&mut self, line: &mut LineBuffer) -> bool { debug!(target: "rustyline", "Changeset::undo"); @@ -281,6 +294,11 @@ impl Changeset { } } +impl DeleteListener for Changeset { + fn delete(&mut self, idx: usize, string: &str, _: Direction) { + self.delete(idx, string); + } +} impl ChangeListener for Changeset { fn insert_char(&mut self, idx: usize, c: char) { self.insert(idx, c); @@ -288,8 +306,8 @@ impl ChangeListener for Changeset { fn insert_str(&mut self, idx: usize, string: &str) { self.insert_str(idx, string); } - fn delete(&mut self, idx: usize, string: &str, _: Direction) { - self.delete(idx, string); + fn replace(&mut self, idx: usize, old: &str, new: &str) { + self.replace(idx, old, new); } } @@ -346,7 +364,7 @@ mod tests { let mut cs = Changeset::new(); assert_eq!(buf.as_str(), "Hello"); - cs.delete(5, ", world!".to_owned()); + cs.delete(5, ", world!"); cs.undo(&mut buf); assert_eq!(buf.as_str(), "Hello, world!"); @@ -361,8 +379,8 @@ mod tests { buf.insert_str(0, "Hlo"); let mut cs = Changeset::new(); - cs.delete(1, "e".to_owned()); - cs.delete(1, "l".to_owned()); + cs.delete(1, "e"); + cs.delete(1, "l"); assert_eq!(1, cs.undos.len()); cs.undo(&mut buf); @@ -375,15 +393,15 @@ mod tests { buf.insert_str(0, "Hlo"); let mut cs = Changeset::new(); - cs.delete(2, "l".to_owned()); - cs.delete(1, "e".to_owned()); + cs.delete(2, "l"); + cs.delete(1, "e"); 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, None); buf.insert_str(0, "Hello, world!"); @@ -392,12 +410,12 @@ mod tests { buf.replace(1..5, "i"); assert_eq!(buf.as_str(), "Hi, world!"); - cs.replace(1, "ello".to_owned(), "i"); + cs.replace(1, "ello", "i"); cs.undo(&mut buf); assert_eq!(buf.as_str(), "Hello, world!"); cs.redo(&mut buf); assert_eq!(buf.as_str(), "Hi, world!"); - }*/ + } } -- GitLab