Repository: kkawakam/rustyline (rust)

Issue:
Reverse search with a long prompt followed by ctrl-c causes an assertion failure panic
You can cause a panic with the following:
1. set a prompt of at least 35 characters
2. initiate a reverse search (ctrl-r)
3. type something
4. hit ctrl-c

Here's the backtrace:
```
(failed reverse-i-search)`okef': eokf  thread 'main' panicked at 'assertion failed: self.layout.cursor <= self.layout.end', /home/user/rustyline/src/edit.rs:128:13
stack backtrace:
   0: rust_begin_unwind
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/std/src/panicking.rs:517:5
   1: core::panicking::panic_fmt
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14
   2: core::panicking::panic
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:50:5
   3: rustyline::edit::State<H>::move_cursor
             at ./src/edit.rs:128:13
   4: rustyline::edit::State<H>::edit_move_buffer_end
             at ./src/edit.rs:465:13
   5: rustyline::command::execute
             at ./src/command.rs:225:13
   6: rustyline::readline_edit
             at ./src/lib.rs:537:15
   7: rustyline::readline_raw
             at ./src/lib.rs:572:22
   8: rustyline::Editor<H>::readline_with
             at ./src/lib.rs:784:13
   9: rustyline::Editor<H>::readline
             at ./src/lib.rs:760:9
  10: example::main
             at ./examples/example.rs:108:24
  11: core::ops::function::FnOnce::call_once
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```

This is just a slightly modified version of the example.rs example:
```rust
use std::borrow::Cow::{self, Borrowed, Owned};

use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::config::OutputStreamType;
use rustyline::error::ReadlineError;
use rustyline::highlight::{Highlighter, MatchingBracketHighlighter};
use rustyline::hint::{Hinter, HistoryHinter};
use rustyline::validate::{self, MatchingBracketValidator, Validator};
use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, KeyEvent};
use rustyline_derive::Helper;

#[derive(Helper)]
struct MyHelper {
    completer: FilenameCompleter,
    highlighter: MatchingBracketHighlighter,
    validator: MatchingBracketValidator,
    hinter: HistoryHinter,
    colored_prompt: String,
}

impl Completer for MyHelper {
    type Candidate = Pair;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        ctx: &Context<'_>,
    ) -> Result<(usize, Vec<Pair>), ReadlineError> {
        self.completer.complete(line, pos, ctx)
    }
}

impl Hinter for MyHelper {
    type Hint = String;

    fn hint(&self, line: &str, pos: usize, ctx: &Context<'_>) -> Option<String> {
        self.hinter.hint(line, pos, ctx)
    }
}

impl Highlighter for MyHelper {
    fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
        &'s self,
        prompt: &'p str,
        default: bool,
    ) -> Cow<'b, str> {
        if default {
            Borrowed(&self.colored_prompt)
        } else {
            Borrowed(prompt)
        }
    }

    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
        Owned("\x1b[1m".to_owned() + hint + "\x1b[m")
    }

    fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
        self.highlighter.highlight(line, pos)
    }

    fn highlight_char(&self, line: &str, pos: usize) -> bool {
        self.highlighter.highlight_char(line, pos)
    }
}

impl Validator for MyHelper {
    fn validate(
        &self,
        ctx: &mut validate::ValidationContext,
    ) -> rustyline::Result<validate::ValidationResult> {
        self.validator.validate(ctx)
    }

    fn validate_while_typing(&self) -> bool {
        self.validator.validate_while_typing()
    }
}

// To debug rustyline:
// RUST_LOG=rustyline=debug cargo run --example example 2> debug.log
fn main() -> rustyline::Result<()> {
    env_logger::init();
    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .edit_mode(EditMode::Emacs)
        .output_stream(OutputStreamType::Stdout)
        .build();
    let h = MyHelper {
        completer: FilenameCompleter::new(),
        highlighter: MatchingBracketHighlighter::new(),
        hinter: HistoryHinter {},
        colored_prompt: "".to_owned(),
        validator: MatchingBracketValidator::new(),
    };
    let mut rl = Editor::with_config(config);
    rl.set_helper(Some(h));
    rl.bind_sequence(KeyEvent::alt('n'), Cmd::HistorySearchForward);
    rl.bind_sequence(KeyEvent::alt('p'), Cmd::HistorySearchBackward);
    if rl.load_history("history.txt").is_err() {
        println!("No previous history.");
    }
    loop {
        let p = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>";
        rl.helper_mut().expect("No helper").colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p);
        let readline = rl.readline(&p);
        match readline {
            Ok(line) => {
                rl.add_history_entry(line.as_str());
                println!("Line: {}", line);
            }
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
    rl.append_history("history.txt")
}
```

Pull request description:
Fix interrupted interactive search
We just want to move cursor to the end of actual line: either edited
line or interactive search line.
Fix #575

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index c2e240a..cfa0432 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,7 +2,7 @@
 name = "rustyline"
 version = "9.0.0"
 authors = ["Katsu Kawakami <kkawa1570@gmail.com>"]
-edition = "2021"
+edition = "2018"
 description = "Rustyline, a readline implementation based on Antirez's Linenoise"
 documentation = "http://docs.rs/rustyline"
 repository = "https://github.com/kkawakam/rustyline"
diff --git a/examples/example.rs b/examples/example.rs
index f937b8f..7b3ccc8 100644
--- a/examples/example.rs
+++ b/examples/example.rs
@@ -113,11 +113,11 @@ fn main() -> rustyline::Result<()> {
                 println!("Line: {}", line);
             }
             Err(ReadlineError::Interrupted) => {
-                println!("Interrupted");
+                println!("CTRL-C");
                 break;
             }
             Err(ReadlineError::Eof) => {
-                println!("Encountered Eof");
+                println!("CTRL-D");
                 break;
             }
             Err(err) => {
diff --git a/src/command.rs b/src/command.rs
index f434b79..89a0b9b 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -222,7 +222,7 @@ pub fn execute<H: Helper>(
             // Move to end, in case cursor was in the middle of the
             // line, so that next thing application prints goes after
             // the input
-            s.edit_move_buffer_end()?;
+            s.move_cursor_to_end()?;
             return Err(error::ReadlineError::Interrupted);
         }
         _ => {
diff --git a/src/edit.rs b/src/edit.rs
index 5e47188..a77018d 100644
--- a/src/edit.rs
+++ b/src/edit.rs
@@ -130,6 +130,15 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
         Ok(())
     }
 
+    pub fn move_cursor_to_end(&mut self) -> Result<()> {
+        if self.layout.cursor == self.layout.end {
+            return Ok(());
+        }
+        self.out.move_cursor(self.layout.cursor, self.layout.end)?;
+        self.layout.cursor = self.layout.end;
+        Ok(())
+    }
+
     pub fn move_cursor_at_leftmost(&mut self, rdr: &mut <Terminal as Term>::Reader) -> Result<()> {
         self.out.move_cursor_at_leftmost(rdr)
     }
diff --git a/src/error.rs b/src/error.rs
index 3ef771a..b4d365f 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -14,9 +14,9 @@ use std::io;
 pub enum ReadlineError {
     /// I/O Error
     Io(io::Error),
-    /// EOF (VEOF / Ctrl-D)
+    /// EOF (Ctrl-D)
     Eof,
-    /// Interrupt signal (VINTR / VQUIT / Ctrl-C)
+    /// Ctrl-C
     Interrupted,
     /// Chars Error
     #[cfg(unix)]
diff --git a/src/keymap.rs b/src/keymap.rs
index 2abeaf0..05ac671 100644
--- a/src/keymap.rs
+++ b/src/keymap.rs
@@ -434,7 +434,6 @@ impl InputState {
         }
     }
 
-    /// Application customized binding
     fn custom_binding(
         &self,
         wrt: &mut dyn Refresher,
@@ -457,20 +456,6 @@ impl InputState {
         }
     }
 
-    /// Terminal peculiar binding
-    fn term_binding<R: RawReader>(
-        rdr: &mut R,
-        wrt: &mut dyn Refresher,
-        key: &KeyEvent,
-    ) -> Option<Cmd> {
-        let cmd = rdr.find_binding(key);
-        if cmd == Some(Cmd::EndOfFile) && !wrt.line().is_empty() {
-            None // ReadlineError::Eof only if line is empty
-        } else {
-            cmd
-        }
-    }
-
     fn custom_seq_binding<R: RawReader>(
         &self,
         rdr: &mut R,
@@ -565,8 +550,6 @@ impl InputState {
             } else {
                 cmd
             });
-        } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
-            return Ok(cmd);
         }
         let cmd = match key {
             E(K::Char(c), M::NONE) => {
@@ -742,8 +725,6 @@ impl InputState {
             } else {
                 cmd
             });
-        } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
-            return Ok(cmd);
         }
         let cmd = match key {
             E(K::Char('$'), M::NONE) | E(K::End, M::NONE) => Cmd::Move(Movement::EndOfLine),
@@ -915,8 +896,6 @@ impl InputState {
             } else {
                 cmd
             });
-        } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
-            return Ok(cmd);
         }
         let cmd = match key {
             E(K::Char(c), M::NONE) => {
@@ -1059,7 +1038,6 @@ impl InputState {
             } else {
                 Movement::ForwardChar(n)
             }),
-            #[cfg(any(windows, test))]
             E(K::Char('C'), M::CTRL) => Cmd::Interrupt,
             E(K::Char('D'), M::CTRL) => {
                 if self.is_emacs_mode() && !wrt.line().is_empty() {
@@ -1068,10 +1046,8 @@ impl InputState {
                     } else {
                         Movement::BackwardChar(n)
                     })
-                } else if cfg!(window) || cfg!(test) || !wrt.line().is_empty() {
-                    Cmd::EndOfFile
                 } else {
-                    Cmd::Unknown
+                    Cmd::EndOfFile
                 }
             }
             E(K::Delete, M::NONE) => Cmd::Kill(if positive {
@@ -1119,6 +1095,7 @@ impl InputState {
                     Cmd::Unknown // TODO Validate
                 }
             }
+            E(K::Char('Z'), M::CTRL) => Cmd::Suspend,
             E(K::Char('_'), M::CTRL) => Cmd::Undo(n),
             E(K::UnknownEscSeq, M::NONE) => Cmd::Noop,
             E(K::BracketedPasteStart, M::NONE) => {
diff --git a/src/lib.rs b/src/lib.rs
index 3f13d38..e189e77 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -440,7 +440,6 @@ fn readline_edit<H: Helper>(
     initial: Option<(&str, &str)>,
     editor: &mut Editor<H>,
     original_mode: &tty::Mode,
-    term_key_map: tty::KeyMap,
 ) -> Result<String> {
     let mut stdout = editor.term.create_writer();
 
@@ -458,7 +457,7 @@ fn readline_edit<H: Helper>(
             .update((left.to_owned() + right).as_ref(), left.len());
     }
 
-    let mut rdr = editor.term.create_reader(&editor.config, term_key_map)?;
+    let mut rdr = editor.term.create_reader(&editor.config)?;
     if editor.term.is_output_tty() && editor.config.check_cursor_position() {
         if let Err(e) = s.move_cursor_at_leftmost(&mut rdr) {
             if s.out.sigwinch() {
@@ -567,9 +566,9 @@ fn readline_raw<H: Helper>(
     initial: Option<(&str, &str)>,
     editor: &mut Editor<H>,
 ) -> Result<String> {
-    let (original_mode, term_key_map) = editor.term.enable_raw_mode()?;
+    let original_mode = editor.term.enable_raw_mode()?;
     let guard = Guard(&original_mode);
-    let user_input = readline_edit(prompt, initial, editor, &original_mode, term_key_map);
+    let user_input = readline_edit(prompt, initial, editor, &original_mode);
     if editor.config.auto_add_history() {
         if let Ok(ref line) = user_input {
             editor.add_history_entry(line.as_str());
diff --git a/src/tty/mod.rs b/src/tty/mod.rs
index 96bcf8f..3b511bb 100644
--- a/src/tty/mod.rs
+++ b/src/tty/mod.rs
@@ -7,7 +7,7 @@ use crate::highlight::Highlighter;
 use crate::keys::KeyEvent;
 use crate::layout::{Layout, Position};
 use crate::line_buffer::LineBuffer;
-use crate::{Cmd, Result};
+use crate::Result;
 
 /// Terminal state
 pub trait RawMode: Sized {
@@ -24,8 +24,6 @@ pub trait RawReader {
     fn next_char(&mut self) -> Result<char>;
     /// Bracketed paste
     fn read_pasted_text(&mut self) -> Result<String>;
-    /// Check if `key` is bound to a peculiar command
-    fn find_binding(&self, key: &KeyEvent) -> Option<Cmd>;
 }
 
 /// Display prompt, line and cursor in terminal output
@@ -201,7 +199,6 @@ fn width(s: &str, esc_seq: &mut u8) -> usize {
 
 /// Terminal contract
 pub trait Term {
-    type KeyMap;
     type Reader: RawReader; // rl_instream
     type Writer: Renderer<Reader = Self::Reader>; // rl_outstream
     type Mode: RawMode;
@@ -221,9 +218,9 @@ pub trait Term {
     /// check if output stream is connected to a terminal.
     fn is_output_tty(&self) -> bool;
     /// Enable RAW mode for the terminal.
-    fn enable_raw_mode(&mut self) -> Result<(Self::Mode, Self::KeyMap)>;
+    fn enable_raw_mode(&mut self) -> Result<Self::Mode>;
     /// Create a RAW reader
-    fn create_reader(&self, config: &Config, key_map: Self::KeyMap) -> Result<Self::Reader>;
+    fn create_reader(&self, config: &Config) -> Result<Self::Reader>;
     /// Create a writer
     fn create_writer(&self) -> Self::Writer;
 }
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
index 8e97bc4..6faa2c5 100644
--- a/src/tty/unix.rs
+++ b/src/tty/unix.rs
@@ -9,18 +9,18 @@ use log::{debug, warn};
 use nix::poll::{self, PollFlags};
 use nix::sys::signal;
 use nix::sys::termios;
-use nix::sys::termios::{SetArg, SpecialCharacterIndices as SCI, Termios};
+use nix::sys::termios::SetArg;
 use unicode_segmentation::UnicodeSegmentation;
 use utf8parse::{Parser, Receiver};
 
 use super::{width, RawMode, RawReader, Renderer, Term};
 use crate::config::{BellStyle, ColorMode, Config, OutputStreamType};
+use crate::error;
 use crate::highlight::Highlighter;
 use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M};
 use crate::layout::{Layout, Position};
 use crate::line_buffer::LineBuffer;
-use crate::{error, Cmd, Result};
-use std::collections::HashMap;
+use crate::Result;
 
 const STDIN_FILENO: RawFd = libc::STDIN_FILENO;
 
@@ -95,10 +95,6 @@ fn is_a_tty(fd: RawFd) -> bool {
     unsafe { libc::isatty(fd) != 0 }
 }
 
-pub type PosixKeyMap = HashMap<KeyEvent, Cmd>;
-#[cfg(not(test))]
-pub type KeyMap = PosixKeyMap;
-
 #[must_use = "You must restore default mode (disable_raw_mode)"]
 pub struct PosixMode {
     termios: termios::Termios,
@@ -154,7 +150,6 @@ pub struct PosixRawReader {
     buf: [u8; 1],
     parser: Parser,
     receiver: Utf8,
-    key_map: PosixKeyMap,
 }
 
 struct Utf8 {
@@ -189,7 +184,7 @@ const RXVT_CTRL: char = '\x1e';
 const RXVT_CTRL_SHIFT: char = '@';
 
 impl PosixRawReader {
-    fn new(config: &Config, key_map: PosixKeyMap) -> Self {
+    fn new(config: &Config) -> Self {
         Self {
             stdin: StdinRaw {},
             timeout_ms: config.keyseq_timeout(),
@@ -199,7 +194,6 @@ impl PosixRawReader {
                 c: None,
                 valid: true,
             },
-            key_map,
         }
     }
 
@@ -722,14 +716,6 @@ impl RawReader for PosixRawReader {
         let buffer = buffer.replace("\r", "\n");
         Ok(buffer)
     }
-
-    fn find_binding(&self, key: &KeyEvent) -> Option<Cmd> {
-        let cmd = self.key_map.get(key).cloned();
-        if let Some(ref cmd) = cmd {
-            debug!(target: "rustyline", "terminal key binding: {:?} => {:?}", key, cmd);
-        }
-        cmd
-    }
 }
 
 impl Receiver for Utf8 {
@@ -1043,13 +1029,6 @@ extern "C" fn sigwinch_handler(_: libc::c_int) {
     debug!(target: "rustyline", "SIGWINCH");
 }
 
-fn map_key(key_map: &mut HashMap<KeyEvent, Cmd>, raw: &Termios, index: SCI, name: &str, cmd: Cmd) {
-    let cc = char::from(raw.control_chars[index as usize]);
-    let key = KeyEvent::new(cc, M::NONE);
-    debug!(target: "rustyline", "{}: {:?}", name, key);
-    key_map.insert(key, cmd);
-}
-
 #[cfg(not(test))]
 pub type Terminal = PosixTerminal;
 
@@ -1076,7 +1055,6 @@ impl PosixTerminal {
 }
 
 impl Term for PosixTerminal {
-    type KeyMap = PosixKeyMap;
     type Mode = PosixMode;
     type Reader = PosixRawReader;
     type Writer = PosixRenderer;
@@ -1123,9 +1101,9 @@ impl Term for PosixTerminal {
 
     // Interactive loop:
 
-    fn enable_raw_mode(&mut self) -> Result<(Self::Mode, PosixKeyMap)> {
+    fn enable_raw_mode(&mut self) -> Result<Self::Mode> {
         use nix::errno::Errno::ENOTTY;
-        use nix::sys::termios::{ControlFlags, InputFlags, LocalFlags};
+        use nix::sys::termios::{ControlFlags, InputFlags, LocalFlags, SpecialCharacterIndices};
         if !self.stdin_isatty {
             return Err(ENOTTY.into());
         }
@@ -1147,15 +1125,8 @@ impl Term for PosixTerminal {
         // disable echoing, canonical mode, extended input processing and signals
         raw.local_flags &=
             !(LocalFlags::ECHO | LocalFlags::ICANON | LocalFlags::IEXTEN | LocalFlags::ISIG);
-        raw.control_chars[SCI::VMIN as usize] = 1; // One character-at-a-time input
-        raw.control_chars[SCI::VTIME as usize] = 0; // with blocking read
-
-        let mut key_map: HashMap<KeyEvent, Cmd> = HashMap::with_capacity(3);
-        map_key(&mut key_map, &raw, SCI::VEOF, "VEOF", Cmd::EndOfFile);
-        map_key(&mut key_map, &raw, SCI::VINTR, "VINTR", Cmd::Interrupt);
-        map_key(&mut key_map, &raw, SCI::VQUIT, "VQUIT", Cmd::Interrupt);
-        map_key(&mut key_map, &raw, SCI::VSUSP, "VSUSP", Cmd::Suspend);
-
+        raw.control_chars[SpecialCharacterIndices::VMIN as usize] = 1; // One character-at-a-time input
+        raw.control_chars[SpecialCharacterIndices::VTIME as usize] = 0; // with blocking read
         termios::tcsetattr(STDIN_FILENO, SetArg::TCSADRAIN, &raw)?;
 
         // enable bracketed paste
@@ -1167,18 +1138,15 @@ impl Term for PosixTerminal {
         } else {
             Some(self.stream_type)
         };
-        Ok((
-            PosixMode {
-                termios: original_mode,
-                out,
-            },
-            key_map,
-        ))
+        Ok(PosixMode {
+            termios: original_mode,
+            out,
+        })
     }
 
     /// Create a RAW reader
-    fn create_reader(&self, config: &Config, key_map: PosixKeyMap) -> Result<PosixRawReader> {
-        Ok(PosixRawReader::new(config, key_map))
+    fn create_reader(&self, config: &Config) -> Result<PosixRawReader> {
+        Ok(PosixRawReader::new(config))
     }
 
     fn create_writer(&self) -> PosixRenderer {
diff --git a/src/tty/windows.rs b/src/tty/windows.rs
index 62732d0..76645cd 100644
--- a/src/tty/windows.rs
+++ b/src/tty/windows.rs
@@ -17,11 +17,12 @@ use winapi::um::{consoleapi, processenv, winbase, winuser};
 
 use super::{width, RawMode, RawReader, Renderer, Term};
 use crate::config::{BellStyle, ColorMode, Config, OutputStreamType};
+use crate::error;
 use crate::highlight::Highlighter;
 use crate::keys::{KeyCode as K, KeyEvent, Modifiers as M};
 use crate::layout::{Layout, Position};
 use crate::line_buffer::LineBuffer;
-use crate::{error, Cmd, Result};
+use crate::Result;
 
 const STDIN_FILENO: DWORD = winbase::STD_INPUT_HANDLE;
 const STDOUT_FILENO: DWORD = winbase::STD_OUTPUT_HANDLE;
@@ -65,10 +66,6 @@ fn get_console_mode(handle: HANDLE) -> Result<DWORD> {
     Ok(original_mode)
 }
 
-type ConsoleKeyMap = ();
-#[cfg(not(test))]
-pub type KeyMap = ConsoleKeyMap;
-
 #[must_use = "You must restore default mode (disable_raw_mode)"]
 #[cfg(not(test))]
 pub type Mode = ConsoleMode;
@@ -227,10 +224,6 @@ impl RawReader for ConsoleRawReader {
     fn read_pasted_text(&mut self) -> Result<String> {
         Ok(clipboard_win::get_clipboard_string()?)
     }
-
-    fn find_binding(&self, _: &KeyEvent) -> Option<Cmd> {
-        None
-    }
 }
 
 pub struct ConsoleRenderer {
@@ -543,7 +536,6 @@ impl Console {
 }
 
 impl Term for Console {
-    type KeyMap = ConsoleKeyMap;
     type Mode = ConsoleMode;
     type Reader = ConsoleRawReader;
     type Writer = ConsoleRenderer;
@@ -608,7 +600,7 @@ impl Term for Console {
     // }
 
     /// Enable RAW mode for the terminal.
-    fn enable_raw_mode(&mut self) -> Result<(ConsoleMode, ConsoleKeyMap)> {
+    fn enable_raw_mode(&mut self) -> Result<Self::Mode> {
         if !self.stdin_isatty {
             Err(io::Error::new(
                 io::ErrorKind::Other,
@@ -663,18 +655,15 @@ impl Term for Console {
             None
         };
 
-        Ok((
-            ConsoleMode {
-                original_stdin_mode,
-                stdin_handle: self.stdin_handle,
-                original_stdstream_mode,
-                stdstream_handle: self.stdstream_handle,
-            },
-            (),
-        ))
+        Ok(ConsoleMode {
+            original_stdin_mode,
+            stdin_handle: self.stdin_handle,
+            original_stdstream_mode,
+            stdstream_handle: self.stdstream_handle,
+        })
     }
 
-    fn create_reader(&self, _: &Config, _: ConsoleKeyMap) -> Result<ConsoleRawReader> {
+    fn create_reader(&self, _: &Config) -> Result<ConsoleRawReader> {
         ConsoleRawReader::create()
     }
 


Test patch:
diff --git a/src/tty/test.rs b/src/tty/test.rs
index 0b362f4..d8bed91 100644
--- a/src/tty/test.rs
+++ b/src/tty/test.rs
@@ -10,9 +10,8 @@ use crate::highlight::Highlighter;
 use crate::keys::KeyEvent;
 use crate::layout::{Layout, Position};
 use crate::line_buffer::LineBuffer;
-use crate::{Cmd, Result};
+use crate::Result;
 
-pub type KeyMap = ();
 pub type Mode = ();
 
 impl RawMode for Mode {
@@ -37,10 +36,6 @@ impl<'a> RawReader for Iter<'a, KeyEvent> {
     fn read_pasted_text(&mut self) -> Result<String> {
         unimplemented!()
     }
-
-    fn find_binding(&self, _: &KeyEvent) -> Option<Cmd> {
-        None
-    }
 }
 
 impl RawReader for IntoIter<KeyEvent> {
@@ -64,10 +59,6 @@ impl RawReader for IntoIter<KeyEvent> {
     fn read_pasted_text(&mut self) -> Result<String> {
         unimplemented!()
     }
-
-    fn find_binding(&self, _: &KeyEvent) -> Option<Cmd> {
-        None
-    }
 }
 
 pub struct Sink {}
@@ -149,7 +140,6 @@ pub struct DummyTerminal {
 }
 
 impl Term for DummyTerminal {
-    type KeyMap = KeyMap;
     type Mode = Mode;
     type Reader = IntoIter<KeyEvent>;
     type Writer = Sink;
@@ -191,11 +181,11 @@ impl Term for DummyTerminal {
 
     // Interactive loop:
 
-    fn enable_raw_mode(&mut self) -> Result<(Mode, KeyMap)> {
-        Ok(((), ()))
+    fn enable_raw_mode(&mut self) -> Result<Mode> {
+        Ok(())
     }
 
-    fn create_reader(&self, _: &Config, _: KeyMap) -> Result<Self::Reader> {
+    fn create_reader(&self, _: &Config) -> Result<IntoIter<KeyEvent>> {
         Ok(self.keys.clone().into_iter())
     }