From 95d2c3c246d2fdcd4c7eda238ee7487931b08fab Mon Sep 17 00:00:00 2001
From: gwenn <gtreguier@gmail.com>
Date: Mon, 11 Apr 2016 20:55:38 +0200
Subject: [PATCH] Avoid cloning by swapping.

---
 src/lib.rs | 66 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 38 insertions(+), 28 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs
index 3f7aef44..1773a483 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -33,6 +33,7 @@ mod kill_ring;
 
 use std::fmt;
 use std::io::{self, Read, Write};
+use std::mem;
 use std::path::Path;
 use std::result;
 use std::sync;
@@ -59,7 +60,8 @@ struct State<'out, 'prompt> {
     cursor: Position, // Cursor position (relative to the start of the prompt for `row`)
     cols: usize, // Number of columns in terminal
     history_index: usize, // The history index we are currently editing.
-    history_end: String, // Current edited line before history browsing
+    snapshot: String, // Current edited line before history browsing/completion
+    snapshot_pos: usize, // Current cursor position before history browsing/completion
 }
 
 #[derive(Copy, Clone, Debug, Default)]
@@ -85,18 +87,26 @@ impl<'out, 'prompt> State<'out, 'prompt> {
             cursor: prompt_size,
             cols: cols,
             history_index: history_index,
-            history_end: String::new(),
+            snapshot: String::with_capacity(capacity),
+            snapshot_pos: 0,
         }
     }
 
-    fn update_buf<S: Into<String>>(&mut self, buf: S) {
-        self.buf = buf.into();
-        if self.buf.capacity() < MAX_LINE {
-            let cap = self.buf.capacity();
-            self.buf.reserve_exact(MAX_LINE - cap);
+    fn update_buf(&mut self, buf: &str) {
+        self.buf.clear();
+        if buf.len() > MAX_LINE {
+            self.buf.push_str(&buf[..MAX_LINE]);
+        } else {
+            self.buf.push_str(buf);
+
         }
     }
 
+    fn snapshot(&mut self) {
+        mem::swap(&mut self.buf, &mut self.snapshot);
+        mem::swap(&mut self.pos, &mut self.snapshot_pos);
+    }
+
     /// Rewrite the currently edited line accordingly to the buffer content,
     /// cursor position, and number of columns of the terminal.
     fn refresh_line(&mut self) -> Result<()> {
@@ -162,7 +172,7 @@ impl<'out, 'prompt> fmt::Debug for State<'out, 'prompt> {
          .field("cursor", &self.cursor)
          .field("cols", &self.cols)
          .field("history_index", &self.history_index)
-         .field("history_end", &self.history_end)
+         .field("snapshot", &self.snapshot)
          .finish()
     }
 }
@@ -554,7 +564,7 @@ fn edit_history_next(s: &mut State, history: &History, prev: bool) -> Result<()>
     if s.history_index == history.len() {
         if prev {
             // Save the current edited line before to overwrite it
-            s.history_end = s.buf.clone();
+            s.snapshot();
         } else {
             return Ok(());
         }
@@ -568,12 +578,12 @@ fn edit_history_next(s: &mut State, history: &History, prev: bool) -> Result<()>
     }
     if s.history_index < history.len() {
         let buf = history.get(s.history_index).unwrap();
-        s.update_buf(buf.clone());
-    } else {
-        let buf = s.history_end.clone(); // TODO how to avoid cloning?
         s.update_buf(buf);
+        s.pos = s.buf.len();
+    } else {
+        // Restore current edited line
+        s.snapshot();
     };
-    s.pos = s.buf.len();
     s.refresh_line()
 }
 
@@ -592,14 +602,14 @@ fn complete_line<R: io::Read>(chars: &mut io::Chars<R>,
         loop {
             // Show completion or original buffer
             if i < candidates.len() {
-                let buf = s.buf.clone(); // TODO how to avoid cloning?
-                let pos = s.pos;
+                // Save the current edited line before to overwrite it
+                s.snapshot();
                 let (tmp_buf, tmp_pos) = completer.update(&s.buf, s.pos, start, &candidates[i]);
                 s.buf = tmp_buf;
                 s.pos = tmp_pos;
                 try!(s.refresh_line());
-                s.update_buf(buf);
-                s.pos = pos;
+                // Restore current edited line (but no refresh)
+                s.snapshot();
             } else {
                 try!(s.refresh_line());
             }
@@ -624,7 +634,7 @@ fn complete_line<R: io::Read>(chars: &mut io::Chars<R>,
                     // Update buffer and return
                     if i < candidates.len() {
                         let (buf, pos) = completer.update(&s.buf, s.pos, start, &candidates[i]);
-                        s.update_buf(buf);
+                        s.update_buf(&buf);
                         s.pos = pos;
                     }
                     break;
@@ -642,8 +652,7 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut io::Chars<R>,
                                            history: &History)
                                            -> Result<Option<KeyPress>> {
     // Save the current edited line (and cursor position) before to overwrite it
-    let original_buf = s.buf.clone();
-    let original_pos = s.pos;
+    s.snapshot();
 
     let mut search_buf = String::new();
     let mut history_idx = history.len() - 1;
@@ -682,8 +691,8 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut io::Chars<R>,
                     }
                 }
                 KeyPress::CTRL_G => {
-                    s.update_buf(original_buf);
-                    s.pos = original_pos;
+                    // Restore current edited line (before search)
+                    s.snapshot();
                     try!(s.refresh_line());
                     return Ok(None);
                 }
@@ -694,7 +703,7 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut io::Chars<R>,
             Some(idx) => {
                 history_idx = idx;
                 let entry = history.get(idx).unwrap();
-                s.update_buf(entry.clone());
+                s.update_buf(entry);
                 s.pos = entry.find(&search_buf).unwrap();
                 true
             }
@@ -1124,7 +1133,8 @@ mod test {
             cursor: Default::default(),
             cols: cols,
             history_index: 0,
-            history_end: String::new(),
+            snapshot: String::new(),
+            snapshot_pos: 0,
         }
     }
 
@@ -1248,24 +1258,24 @@ mod test {
         }
 
         super::edit_history_next(&mut s, &history, true).unwrap();
-        assert_eq!(line, s.history_end);
+        assert_eq!(line, s.snapshot);
         assert_eq!(1, s.history_index);
         assert_eq!("line1", s.buf);
 
         for _ in 0..2 {
             super::edit_history_next(&mut s, &history, true).unwrap();
-            assert_eq!(line, s.history_end);
+            assert_eq!(line, s.snapshot);
             assert_eq!(0, s.history_index);
             assert_eq!("line0", s.buf);
         }
 
         super::edit_history_next(&mut s, &history, false).unwrap();
-        assert_eq!(line, s.history_end);
+        assert_eq!(line, s.snapshot);
         assert_eq!(1, s.history_index);
         assert_eq!("line1", s.buf);
 
         super::edit_history_next(&mut s, &history, false).unwrap();
-        assert_eq!(line, s.history_end);
+        // assert_eq!(line, s.snapshot);
         assert_eq!(2, s.history_index);
         assert_eq!(line, s.buf);
     }
-- 
GitLab