Make possible to customize key bindings (#115)
diff --git a/src/consts.rs b/src/consts.rs
index 93c2e73..ed38db6 100644
--- a/src/consts.rs
+++ b/src/consts.rs
@@ -1,5 +1,6 @@
+//! Key constants
 
-#[derive(Debug, Clone, PartialEq, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum KeyPress {
     UnknownEscSeq,
     Backspace,
diff --git a/src/keymap.rs b/src/keymap.rs
index 17a502a..696532a 100644
--- a/src/keymap.rs
+++ b/src/keymap.rs
@@ -1,4 +1,8 @@
 //! Bindings from keys to command for Emacs and Vi modes
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::rc::Rc;
+
 use config::Config;
 use config::EditMode;
 use consts::KeyPress;
@@ -54,6 +58,12 @@
             _ => false,
         }
     }
+    fn is_repeatable(&self) -> bool {
+        match *self {
+            Cmd::Move(_) => true,
+            _ => self.is_repeatable_change(),
+        }
+    }
 
     fn redo(&self, new: Option<RepeatCount>) -> Cmd {
         match *self {
@@ -61,6 +71,7 @@
                 Cmd::Insert(repeat_count(previous, new), text.clone())
             }
             Cmd::Kill(ref mvt) => Cmd::Kill(mvt.redo(new)),
+            Cmd::Move(ref mvt) => Cmd::Move(mvt.redo(new)),
             Cmd::Replace(previous, c) => Cmd::Replace(repeat_count(previous, new), c),
             Cmd::SelfInsert(previous, c) => Cmd::SelfInsert(repeat_count(previous, new), c),
             //Cmd::TransposeChars => Cmd::TransposeChars,
@@ -160,6 +171,7 @@
 
 pub struct EditState {
     mode: EditMode,
+    custom_bindings: Rc<RefCell<HashMap<KeyPress, Cmd>>>,
     // Vi Command/Alternate, Insert/Input mode
     insert: bool, // vi only ?
     // numeric arguments: http://web.mit.edu/gnu/doc/html/rlman_1.html#SEC7
@@ -170,9 +182,10 @@
 }
 
 impl EditState {
-    pub fn new(config: &Config) -> EditState {
+    pub fn new(config: &Config, custom_bindings: Rc<RefCell<HashMap<KeyPress, Cmd>>>) -> EditState {
         EditState {
             mode: config.edit_mode(),
+            custom_bindings: custom_bindings,
             insert: true,
             num_args: 0,
             last_cmd: Cmd::Noop,
@@ -230,6 +243,13 @@
             key = try!(self.emacs_digit_argument(rdr, digit));
         }
         let (n, positive) = self.emacs_num_args(); // consume them in all cases
+        if let Some(cmd) = self.custom_bindings.borrow().get(&key) {
+            return Ok(if cmd.is_repeatable() {
+                cmd.redo(Some(n))
+            } else {
+                cmd.clone()
+            });
+        }
         let cmd = match key {
             KeyPress::Char(c) => {
                 if positive {
@@ -347,6 +367,17 @@
         }
         let no_num_args = self.num_args == 0;
         let n = self.vi_num_args(); // consume them in all cases
+        if let Some(cmd) = self.custom_bindings.borrow().get(&key) {
+            return Ok(if cmd.is_repeatable() {
+                if no_num_args {
+                    cmd.redo(None)
+                } else {
+                    cmd.redo(Some(n))
+                }
+            } else {
+                cmd.clone()
+            });
+        }
         let cmd = match key {
             KeyPress::Char('$') |
             KeyPress::End => Cmd::Move(Movement::EndOfLine),
@@ -492,6 +523,13 @@
 
     fn vi_insert<R: RawReader>(&mut self, rdr: &mut R) -> Result<Cmd> {
         let key = try!(rdr.next_key());
+        if let Some(cmd) = self.custom_bindings.borrow().get(&key) {
+            return Ok(if cmd.is_repeatable() {
+                cmd.redo(None)
+            } else {
+                cmd.clone()
+            });
+        }
         let cmd = match key {
             KeyPress::Char(c) => Cmd::SelfInsert(1, c),
             KeyPress::Ctrl('H') |
diff --git a/src/lib.rs b/src/lib.rs
index 2350214..264ea10 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -44,6 +44,7 @@
 mod tty;
 
 use std::cell::RefCell;
+use std::collections::HashMap;
 use std::fmt;
 use std::io::{self, Write};
 use std::mem;
@@ -58,10 +59,12 @@
 use completion::{Completer, longest_common_prefix};
 use history::{Direction, History};
 use line_buffer::{LineBuffer, MAX_LINE, WordAction};
-use keymap::{Anchor, At, CharSearch, Cmd, EditState, Movement, RepeatCount, Word};
+pub use keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
+use keymap::EditState;
 use kill_ring::{Mode, KillRing};
 pub use config::{CompletionType, Config, EditMode, HistoryDuplicates};
 use undo::Changeset;
+pub use consts::KeyPress;
 
 /// The error type for I/O and Linux Syscalls (Errno)
 pub type Result<T> = result::Result<T, error::ReadlineError>;
@@ -94,7 +97,8 @@
            term: Terminal,
            config: &Config,
            prompt: &'prompt str,
-           history_index: usize)
+           history_index: usize,
+           custom_bindings: Rc<RefCell<HashMap<KeyPress, Cmd>>>)
            -> State<'out, 'prompt> {
         let capacity = MAX_LINE;
         let cols = term.get_columns();
@@ -111,7 +115,7 @@
             snapshot: LineBuffer::with_capacity(capacity),
             term: term,
             byte_buffer: [0; 4],
-            edit_state: EditState::new(config),
+            edit_state: EditState::new(config, custom_bindings),
             changes: Rc::new(RefCell::new(Changeset::new())),
         }
     }
@@ -851,7 +855,8 @@
                            editor.term.clone(),
                            &editor.config,
                            prompt,
-                           editor.history.len());
+                           editor.history.len(),
+                           editor.custom_bindings.clone());
     s.line.bind(UNDOS_NAME, s.changes.clone());
     try!(s.refresh_line());
 
@@ -1140,6 +1145,7 @@
     completer: Option<C>,
     kill_ring: Rc<RefCell<KillRing>>,
     config: Config,
+    custom_bindings: Rc<RefCell<HashMap<KeyPress, Cmd>>>,
 }
 
 impl<C: Completer> Editor<C> {
@@ -1155,6 +1161,7 @@
             completer: None,
             kill_ring: Rc::new(RefCell::new(KillRing::new(60))),
             config: config,
+            custom_bindings: Rc::new(RefCell::new(HashMap::new())),
         }
     }
 
@@ -1202,6 +1209,15 @@
         self.completer = completer;
     }
 
+    /// Bind a sequence to a command.
+    pub fn bind_sequence(&mut self, key_seq: KeyPress, cmd: Cmd) -> Option<Cmd> {
+        self.custom_bindings.borrow_mut().insert(key_seq, cmd)
+    }
+    /// Remove a binding for the given sequence.
+    pub fn unbind_sequence(&mut self, key_seq: KeyPress) -> Option<Cmd> {
+        self.custom_bindings.borrow_mut().remove(&key_seq)
+    }
+
     /// ```
     /// let mut rl = rustyline::Editor::<()>::new();
     /// for readline in rl.iter("> ") {
@@ -1263,6 +1279,7 @@
 #[cfg(test)]
 mod test {
     use std::cell::RefCell;
+    use std::collections::HashMap;
     use std::io::Write;
     use std::rc::Rc;
     use line_buffer::LineBuffer;
@@ -1294,7 +1311,7 @@
             snapshot: LineBuffer::with_capacity(100),
             term: term,
             byte_buffer: [0; 4],
-            edit_state: EditState::new(&config),
+            edit_state: EditState::new(&config, Rc::new(RefCell::new(HashMap::new()))),
             changes: Rc::new(RefCell::new(Changeset::new())),
         }
     }