From cf92421be4eb4302a7c9a209bec9b096cd6b57c9 Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Sun, 2 Apr 2017 10:03:37 +0200
Subject: [PATCH] Do not remove change listeners but disable them

---
 src/completion.rs  |  4 +++-
 src/kill_ring.rs   | 12 +++++++++++
 src/lib.rs         | 40 +++++++++++++++++-----------------
 src/line_buffer.rs | 32 +++++++++++++--------------
 src/undo.rs        | 54 +++++++++++++++++++++++++++++++---------------
 5 files changed, 88 insertions(+), 54 deletions(-)

diff --git a/src/completion.rs b/src/completion.rs
index 28bb8e99..6b049109 100644
--- a/src/completion.rs
+++ b/src/completion.rs
@@ -130,7 +130,9 @@ pub fn escape(input: String, esc_char: Option<char>, break_chars: &BTreeSet<char
         return input;
     }
     let esc_char = esc_char.unwrap();
-    let n = input.chars().filter(|c| break_chars.contains(c)).count();
+    let n = input.chars()
+        .filter(|c| break_chars.contains(c))
+        .count();
     if n == 0 {
         return input;
     }
diff --git a/src/kill_ring.rs b/src/kill_ring.rs
index d28f9203..d76d94b1 100644
--- a/src/kill_ring.rs
+++ b/src/kill_ring.rs
@@ -20,6 +20,7 @@ pub struct KillRing {
     index: usize,
     // whether or not the last command was a kill or a yank
     last_action: Action,
+    killing: bool,
 }
 
 impl KillRing {
@@ -29,9 +30,17 @@ impl KillRing {
             slots: Vec::with_capacity(size),
             index: 0,
             last_action: Action::Other,
+            killing: false,
         }
     }
 
+    pub fn start_killing(&mut self) {
+        self.killing = true;
+    }
+    pub fn stop_killing(&mut self) {
+        self.killing = false;
+    }
+
     /// Reset `last_action` state.
     pub fn reset(&mut self) {
         self.last_action = Action::Other;
@@ -107,6 +116,9 @@ impl ChangeListener for KillRing {
     fn insert_char(&mut self, _: usize, _: char) {}
     fn insert_str(&mut self, _: usize, _: &str) {}
     fn delete(&mut self, _: usize, string: &str, dir: Direction) {
+        if !self.killing {
+            return;
+        }
         let mode = match dir {
             Direction::Forward => Mode::Append,
             Direction::Backward => Mode::Prepend,
diff --git a/src/lib.rs b/src/lib.rs
index b4cdbda0..d5326c3a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -214,7 +214,8 @@ impl<'out, 'prompt> State<'out, 'prompt> {
         let mut info = try!(self.term.get_console_screen_buffer_info());
         info.dwCursorPosition.X = 0;
         info.dwCursorPosition.Y -= self.cursor.row as i16;
-        try!(self.term.set_console_cursor_position(info.dwCursorPosition));
+        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,
@@ -230,7 +231,8 @@ impl<'out, 'prompt> State<'out, 'prompt> {
         let mut info = try!(self.term.get_console_screen_buffer_info());
         info.dwCursorPosition.X = cursor.col as i16;
         info.dwCursorPosition.Y -= (end_pos.row - cursor.row) as i16;
-        try!(self.term.set_console_cursor_position(info.dwCursorPosition));
+        try!(self.term
+            .set_console_cursor_position(info.dwCursorPosition));
 
         self.cursor = cursor;
         self.old_rows = end_pos.row;
@@ -838,9 +840,6 @@ fn reverse_incremental_search<R: RawReader>(rdr: &mut R,
     Ok(Some(cmd))
 }
 
-static KILL_RING_NAME: &'static str = "kill_ring";
-static UNDOS_NAME: &'static str = "undos";
-
 /// Handles reading and editting the readline buffer.
 /// It will also handle special inputs in an appropriate fashion
 /// (e.g., C-c will exit readline)
@@ -860,7 +859,10 @@ fn readline_edit<C: Completer>(prompt: &str,
                            prompt,
                            editor.history.len(),
                            editor.custom_bindings.clone());
-    s.line.bind(UNDOS_NAME, s.changes.clone());
+
+    s.line.bind(s.changes.clone());
+    s.line.bind(editor.kill_ring.clone());
+
     try!(s.refresh_line());
 
     let mut rdr = try!(s.term.create_reader(&editor.config));
@@ -945,15 +947,15 @@ fn readline_edit<C: Completer>(prompt: &str,
             }
             Cmd::Kill(Movement::EndOfLine) => {
                 // Kill the text from point to the end of the line.
-                s.line.bind(KILL_RING_NAME, editor.kill_ring.clone());
+                editor.kill_ring.borrow_mut().start_killing();
                 try!(edit_kill_line(&mut s));
-                s.line.unbind(KILL_RING_NAME);
+                editor.kill_ring.borrow_mut().stop_killing();
             }
             Cmd::Kill(Movement::WholeLine) => {
                 try!(edit_move_home(&mut s));
-                s.line.bind(KILL_RING_NAME, editor.kill_ring.clone());
+                editor.kill_ring.borrow_mut().start_killing();
                 try!(edit_kill_line(&mut s));
-                s.line.unbind(KILL_RING_NAME);
+                editor.kill_ring.borrow_mut().stop_killing();
             }
             Cmd::ClearScreen => {
                 // Clear the screen leaving the current line at the top of the screen.
@@ -974,9 +976,9 @@ fn readline_edit<C: Completer>(prompt: &str,
             }
             Cmd::Kill(Movement::BeginningOfLine) => {
                 // Kill backward from point to the beginning of the line.
-                s.line.bind(KILL_RING_NAME, editor.kill_ring.clone());
+                editor.kill_ring.borrow_mut().start_killing();
                 try!(edit_discard_line(&mut s));
-                s.line.unbind(KILL_RING_NAME);
+                editor.kill_ring.borrow_mut().stop_killing();
             }
             #[cfg(unix)]
             Cmd::QuotedInsert => {
@@ -1003,9 +1005,9 @@ fn readline_edit<C: Completer>(prompt: &str,
             }
             Cmd::Kill(Movement::BackwardWord(n, word_def)) => {
                 // kill one word backward (until start of word)
-                s.line.bind(KILL_RING_NAME, editor.kill_ring.clone());
+                editor.kill_ring.borrow_mut().start_killing();
                 try!(edit_delete_prev_word(&mut s, word_def, n));
-                s.line.unbind(KILL_RING_NAME);
+                editor.kill_ring.borrow_mut().stop_killing();
             }
             Cmd::BeginningOfHistory => {
                 // move to first entry in history
@@ -1025,9 +1027,9 @@ fn readline_edit<C: Completer>(prompt: &str,
             }
             Cmd::Kill(Movement::ForwardWord(n, at, word_def)) => {
                 // kill one word forward (until start/end of word)
-                s.line.bind(KILL_RING_NAME, editor.kill_ring.clone());
+                editor.kill_ring.borrow_mut().start_killing();
                 try!(edit_delete_word(&mut s, at, word_def, n));
-                s.line.unbind(KILL_RING_NAME);
+                editor.kill_ring.borrow_mut().stop_killing();
             }
             Cmd::Move(Movement::ForwardWord(n, at, word_def)) => {
                 // move forwards one word
@@ -1053,16 +1055,14 @@ fn readline_edit<C: Completer>(prompt: &str,
             }
             Cmd::Move(Movement::ViCharSearch(n, cs)) => try!(edit_move_to(&mut s, cs, n)),
             Cmd::Kill(Movement::ViCharSearch(n, cs)) => {
-                s.line.bind(KILL_RING_NAME, editor.kill_ring.clone());
+                editor.kill_ring.borrow_mut().start_killing();
                 try!(edit_delete_to(&mut s, cs, n));
-                s.line.unbind(KILL_RING_NAME);
+                editor.kill_ring.borrow_mut().stop_killing();
             }
             Cmd::Undo => {
-                s.line.unbind(UNDOS_NAME);
                 if s.changes.borrow_mut().undo(&mut s.line) {
                     try!(s.refresh_line());
                 }
-                s.line.bind(UNDOS_NAME, s.changes.clone());
             }
             Cmd::Interrupt => {
                 return Err(error::ReadlineError::Interrupted);
diff --git a/src/line_buffer.rs b/src/line_buffer.rs
index ce05691f..21fe051a 100644
--- a/src/line_buffer.rs
+++ b/src/line_buffer.rs
@@ -1,6 +1,5 @@
 //! Line buffer with current cursor position
 use std::cell::RefCell;
-use std::collections::HashMap;
 use std::fmt;
 use std::iter;
 use std::ops::{Deref, Range};
@@ -40,7 +39,7 @@ pub trait ChangeListener {
 pub struct LineBuffer {
     buf: String, // Edited line buffer
     pos: usize, // Current cursor position (byte position)
-    cl: HashMap<&'static str, Rc<RefCell<ChangeListener>>>,
+    cl: Vec<Rc<RefCell<ChangeListener>>>,
 }
 
 impl fmt::Debug for LineBuffer {
@@ -58,7 +57,7 @@ impl LineBuffer {
         LineBuffer {
             buf: String::with_capacity(capacity),
             pos: 0,
-            cl: HashMap::new(),
+            cl: Vec::new(),
         }
     }
 
@@ -68,16 +67,13 @@ impl LineBuffer {
         assert!(lb.insert_str(0, line));
         lb.set_pos(pos);
         if cl.is_some() {
-            lb.bind("test", cl.unwrap());
+            lb.bind(cl.unwrap());
         }
         lb
     }
 
-    pub fn bind(&mut self, key: &'static str, cl: Rc<RefCell<ChangeListener>>) {
-        self.cl.insert(key, cl);
-    }
-    pub fn unbind(&mut self, key: &'static str) {
-        self.cl.remove(key);
+    pub fn bind(&mut self, cl: Rc<RefCell<ChangeListener>>) {
+        self.cl.push(cl);
     }
 
     /// Extracts a string slice containing the entire buffer.
@@ -179,7 +175,7 @@ impl LineBuffer {
         let push = self.pos == self.buf.len();
         if n == 1 {
             self.buf.insert(self.pos, ch);
-            for cl in self.cl.values() {
+            for cl in &self.cl {
                 cl.borrow_mut().insert_char(self.pos, ch);
             }
         } else {
@@ -269,7 +265,8 @@ impl LineBuffer {
         match self.next_pos(n) {
             Some(pos) => {
                 let start = self.pos;
-                let chars = self.drain(start..pos, Direction::Forward).collect::<String>();
+                let chars = self.drain(start..pos, Direction::Forward)
+                    .collect::<String>();
                 Some(chars)
             }
             None => None,
@@ -573,7 +570,8 @@ impl LineBuffer {
                 if start == end {
                     return false;
                 }
-                let word = self.drain(start..end, Direction::default()).collect::<String>();
+                let word = self.drain(start..end, Direction::default())
+                    .collect::<String>();
                 let result = match a {
                     WordAction::CAPITALIZE => {
                         let ch = (&word).graphemes(true).next().unwrap();
@@ -608,7 +606,8 @@ impl LineBuffer {
 
         let w1 = self.buf[w1_beg..w1_end].to_owned();
 
-        let w2 = self.drain(w2_beg..w2_end, Direction::default()).collect::<String>();
+        let w2 = self.drain(w2_beg..w2_end, Direction::default())
+            .collect::<String>();
         self.insert_str(w2_beg, &w1);
 
         self.drain(w1_beg..w1_end, Direction::default());
@@ -628,7 +627,7 @@ impl LineBuffer {
     }
 
     pub fn insert_str(&mut self, idx: usize, s: &str) -> bool {
-        for cl in self.cl.values() {
+        for cl in &self.cl {
             cl.borrow_mut().insert_str(idx, s);
         }
         if idx == self.buf.len() {
@@ -646,8 +645,9 @@ impl LineBuffer {
     }
 
     fn drain(&mut self, range: Range<usize>, dir: Direction) -> Drain {
-        for cl in self.cl.values() {
-            cl.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);
         }
         self.buf.drain(range)
     }
diff --git a/src/undo.rs b/src/undo.rs
index fce9e75a..e77f33c4 100644
--- a/src/undo.rs
+++ b/src/undo.rs
@@ -69,6 +69,7 @@ impl Change {
 pub struct Changeset {
     undos: Vec<Change>, // undoable changes
     redos: Vec<Change>, // undone changes, redoable
+    undoing: bool,
 }
 
 impl Changeset {
@@ -76,6 +77,7 @@ impl Changeset {
         Changeset {
             undos: Vec::new(),
             redos: Vec::new(),
+            undoing: false,
         }
     }
 
@@ -134,20 +136,22 @@ impl Changeset {
 
     pub fn insert_str<S: Into<String>>(&mut self, idx: usize, string: S) {
         self.redos.clear();
-        self.undos.push(Change::Insert {
-            idx: idx,
-            text: string.into(),
-        });
+        self.undos
+            .push(Change::Insert {
+                idx: idx,
+                text: string.into(),
+            });
     }
 
     pub fn delete<S: AsRef<str> + Into<String>>(&mut self, indx: usize, string: S) {
         self.redos.clear();
 
         if !Self::single_char(string.as_ref()) {
-            self.undos.push(Change::Delete {
-                idx: indx,
-                text: string.into(),
-            });
+            self.undos
+                .push(Change::Delete {
+                    idx: indx,
+                    text: string.into(),
+                });
             return;
         }
         let last_change = self.undos.pop();
@@ -169,24 +173,27 @@ impl Changeset {
                     self.undos.push(last_change);
                 } else {
                     self.undos.push(last_change);
-                    self.undos.push(Change::Delete {
-                        idx: indx,
-                        text: string.into(),
-                    });
+                    self.undos
+                        .push(Change::Delete {
+                            idx: indx,
+                            text: string.into(),
+                        });
                 }
             }
             None => {
-                self.undos.push(Change::Delete {
-                    idx: indx,
-                    text: string.into(),
-                });
+                self.undos
+                    .push(Change::Delete {
+                        idx: indx,
+                        text: string.into(),
+                    });
             }
         };
     }
 
     fn single_char(s: &str) -> bool {
         let mut graphemes = s.graphemes(true);
-        graphemes.next().map_or(false, |grapheme| grapheme.is_alphanumeric()) &&
+        graphemes.next()
+            .map_or(false, |grapheme| grapheme.is_alphanumeric()) &&
         graphemes.next().is_none()
     }
 
@@ -200,6 +207,7 @@ impl Changeset {
     }*/
 
     pub fn undo(&mut self, line: &mut LineBuffer) -> bool {
+        self.undoing = true;
         let mut waiting_for_begin = 0;
         let mut undone = false;
         loop {
@@ -224,11 +232,13 @@ impl Changeset {
                 break;
             }
         }
+        self.undoing = false;
         undone
     }
 
     #[cfg(test)]
     pub fn redo(&mut self, line: &mut LineBuffer) -> bool {
+        self.undoing = true;
         let mut waiting_for_end = 0;
         let mut redone = false;
         loop {
@@ -253,18 +263,28 @@ impl Changeset {
                 break;
             }
         }
+        self.undoing = false;
         redone
     }
 }
 
 impl ChangeListener for Changeset {
     fn insert_char(&mut self, idx: usize, c: char) {
+        if self.undoing {
+            return;
+        }
         self.insert(idx, c);
     }
     fn insert_str(&mut self, idx: usize, string: &str) {
+        if self.undoing {
+            return;
+        }
         self.insert_str(idx, string);
     }
     fn delete(&mut self, idx: usize, string: &str, _: Direction) {
+        if self.undoing {
+            return;
+        }
         self.delete(idx, string);
     }
 }
-- 
GitLab