Newer
Older
//! This implementation is based on [Antirez's
//! Linenoise](https://github.com/antirez/linenoise)
//! Ok(line) => println!("Line: {:?}",line),
//! Err(_) => println!("No input"),
//! }
#[cfg(windows)]
extern crate winapi;
pub mod completion;
use std::path::Path;
use unicode_segmentation::UnicodeSegmentation;
use history::{Direction, History};
pub use keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
use keymap::EditState;
pub use config::{CompletionType, Config, EditMode, HistoryDuplicates};
/// The error type for I/O and Linux Syscalls (Errno)
pub type Result<T> = result::Result<T, error::ReadlineError>;
/// Represent the state during line editing.
/// Implement rendering.
prompt: &'prompt str, // Prompt to display
prompt_size: Position, // Prompt Unicode/visible width and height
line: LineBuffer, // Edited line buffer
cursor: Position, /* Cursor position (relative to the start of the prompt
* for `row`) */
old_rows: usize, // Number of rows used so far (from start of prompt to end of input)
history_index: usize, // The history index we are currently editing
saved_line_for_history: LineBuffer, // Current edited line before history browsing
fn new(
out: &'out mut Renderer,
config: &Config,
prompt: &'prompt str,
history_index: usize,
custom_bindings: Rc<RefCell<HashMap<KeyPress, Cmd>>>,
let prompt_size = out.calculate_position(prompt, Position::default());
out: out,
prompt: prompt,
prompt_size: prompt_size,
line: LineBuffer::with_capacity(capacity),
cursor: prompt_size,
old_rows: prompt_size.row,
edit_state: EditState::new(config, custom_bindings),
fn next_cmd<R: RawReader>(&mut self, rdr: &mut R) -> Result<Cmd> {
if rc.is_err() && self.out.sigwinch() {
self.out.update_size();
try!(self.refresh_line());
continue;
}
return rc;
self.saved_line_for_history
.update(self.line.as_str(), self.line.pos());
self.line.update(
self.saved_line_for_history.as_str(),
self.saved_line_for_history.pos(),
);
fn move_cursor(&mut self) -> Result<()> {
// calculate the desired position of the cursor
let cursor = self.out
.calculate_position(&self.line[..self.line.pos()], self.prompt_size);
if self.cursor == cursor {
return Ok(());
}
try!(self.out.move_cursor(self.cursor, cursor));
self.cursor = cursor;
Ok(())
}
/// 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<()> {
let hint = self.hint();
self.refresh(self.prompt, prompt_size, hint)
}
fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()> {
let prompt_size = self.out.calculate_position(prompt, Position::default());
let hint = self.hint();
self.refresh(prompt, prompt_size, hint)
fn refresh(&mut self, prompt: &str, prompt_size: Position, hint: Option<String>) -> Result<()> {
let (cursor, end_pos) = try!(self.out.refresh_line(
prompt,
prompt_size,
&self.line,
fn hint(&self) -> Option<String> {
if let Some(ref hinter) = self.hinter {
hinter
.borrow_mut()
.hint(self.line.as_str(), self.line.pos())
impl<'out, 'prompt> fmt::Debug for State<'out, 'prompt> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("State")
.field("old_rows", &self.old_rows)
/// Insert the character `ch` at cursor current position.
fn edit_insert(s: &mut State, ch: char, n: RepeatCount) -> Result<()> {
let prompt_size = s.prompt_size;
let hint = s.hint();
if n == 1 && s.cursor.col + ch.width().unwrap_or(0) < s.out.get_columns() &&
hint.is_none()
{
// Avoid a full update of the line in the trivial case.
let cursor = s.out
.calculate_position(&s.line[..s.line.pos()], s.prompt_size);
let bits = ch.encode_utf8(&mut s.byte_buffer);
let bits = bits.as_bytes();
}
} else {
Ok(())
}
}
/// Replace a single (or n) character(s) under the cursor (Vi mode)
fn edit_replace_char(s: &mut State, ch: char, n: RepeatCount) -> Result<()> {
s.changes.borrow_mut().begin();
let succeed = if let Some(chars) = s.line.delete(n) {
let count = chars.graphemes(true).count();
/// Overwrite the character under the cursor (Vi mode)
fn edit_overwrite_char(s: &mut State, ch: char) -> Result<()> {
if let Some(end) = s.line.next_pos(1) {
{
let text = ch.encode_utf8(&mut s.byte_buffer);
let start = s.line.pos();
s.line.replace(start..end, text);
}
s.refresh_line()
} else {
Ok(())
}
}
fn edit_yank(s: &mut State, text: &str, anchor: Anchor, n: RepeatCount) -> Result<()> {
if let Anchor::After = anchor {
s.line.move_forward(1);
}
if s.line.yank(text, n).is_some() {
// Delete previously yanked text and yank/paste `text` at current position.
fn edit_yank_pop(s: &mut State, yank_size: usize, text: &str) -> Result<()> {
let result = edit_yank(s, text, Anchor::Before, 1);
s.changes.borrow_mut().end();
result
fn edit_move_backward(s: &mut State, n: RepeatCount) -> Result<()> {
if s.line.move_backward(n) {
} else {
Ok(())
}
}
/// Move cursor on the right.
fn edit_move_forward(s: &mut State, n: RepeatCount) -> Result<()> {
if s.line.move_forward(n) {
}
}
/// Move cursor to the start of the line.
fn edit_move_home(s: &mut State) -> Result<()> {
} else {
Ok(())
}
}
/// Move cursor to the end of the line.
fn edit_move_end(s: &mut State) -> Result<()> {
}
}
/// Delete the character at the right of the cursor without altering the cursor
/// position. Basically this is what happens with the "Delete" keyboard key.
fn edit_delete(s: &mut State, n: RepeatCount) -> Result<()> {
} else {
Ok(())
}
}
/// Backspace implementation.
fn edit_backspace(s: &mut State, n: RepeatCount) -> Result<()> {
if s.line.backspace(n) {
} else {
Ok(())
}
}
/// Kill the text from point to the end of the line.
fn edit_kill_line(s: &mut State) -> Result<()> {
if s.line.kill_line() {
s.refresh_line()
}
}
/// Kill backward from point to the beginning of the line.
fn edit_discard_line(s: &mut State) -> Result<()> {
if s.line.discard_line() {
s.refresh_line()
}
}
/// Exchange the char before cursor with the character at cursor.
fn edit_transpose_chars(s: &mut State) -> Result<()> {
s.changes.borrow_mut().begin();
let succeed = s.line.transpose_chars();
s.changes.borrow_mut().end();
fn edit_move_to_prev_word(s: &mut State, word_def: Word, n: RepeatCount) -> Result<()> {
} else {
Ok(())
}
}
/// Delete the previous word, maintaining the cursor at the start of the
/// current word.
fn edit_delete_prev_word(s: &mut State, word_def: Word, n: RepeatCount) -> Result<()> {
if s.line.delete_prev_word(word_def, n) {
s.refresh_line()
fn edit_move_to_next_word(s: &mut State, at: At, word_def: Word, n: RepeatCount) -> Result<()> {
fn edit_move_to(s: &mut State, cs: CharSearch, n: RepeatCount) -> Result<()> {
/// Kill from the cursor to the end of the current word, or, if between words,
/// to the end of the next word.
fn edit_delete_word(s: &mut State, at: At, word_def: Word, n: RepeatCount) -> Result<()> {
if s.line.delete_word(at, word_def, n) {
s.refresh_line()
fn edit_delete_to(s: &mut State, cs: CharSearch, n: RepeatCount) -> Result<()> {
if s.line.delete_to(cs, n) {
s.refresh_line()
s.changes.borrow_mut().begin();
let succeed = s.line.edit_word(a);
s.changes.borrow_mut().end();
fn edit_transpose_words(s: &mut State, n: RepeatCount) -> Result<()> {
s.changes.borrow_mut().begin();
let succeed = s.line.transpose_words(n);
s.changes.borrow_mut().end();
/// Substitute the currently edited line with the next or previous history
/// entry.
fn edit_history_next(s: &mut State, history: &History, prev: bool) -> Result<()> {
if history.is_empty() {
return Ok(());
}
if s.history_index == history.len() {
} else if s.history_index == 0 && prev {
return Ok(());
}
if prev {
s.history_index -= 1;
if s.history_index < history.len() {
let buf = history.get(s.history_index).unwrap();
fn edit_history_search(s: &mut State, history: &History, dir: Direction) -> Result<()> {
if history.is_empty() {
if s.history_index == history.len() && dir == Direction::Forward {
} else if s.history_index == 0 && dir == Direction::Reverse {
}
if dir == Direction::Reverse {
s.history_index -= 1;
} else {
s.history_index += 1;
}
if let Some(history_index) =
history.starts_with(&s.line.as_str()[..s.line.pos()], s.history_index, dir)
{
s.history_index = history_index;
let buf = history.get(history_index).unwrap();
/// Substitute the currently edited line with the first/last history entry.
fn edit_history(s: &mut State, history: &History, first: bool) -> Result<()> {
if history.is_empty() {
return Ok(());
}
if s.history_index == history.len() {
if first {
} else {
return Ok(());
}
} else if s.history_index == 0 && first {
return Ok(());
}
if first {
s.history_index = 0;
let buf = history.get(s.history_index).unwrap();
} else {
s.history_index = history.len();
// Restore current edited line
let (start, candidates) = try!(completer.complete(&s.line, s.line.pos()));
} else if CompletionType::Circular == config.completion_type() {
// Save the current edited line before overwriting it
let backup = s.line.as_str().to_owned();
let backup_pos = s.line.pos();
let mut i = 0;
loop {
// Show completion or original buffer
if i < candidates.len() {
completer.update(&mut s.line, start, &candidates[i]);
} else if CompletionType::List == config.completion_type() {
// beep if ambiguous
if candidates.len() > 1 {
}
if let Some(lcp) = longest_common_prefix(&candidates) {
// if we can extend the item, extend it and return to main loop
if lcp.len() > s.line.pos() - start {
try!(s.refresh_line());
return Ok(None);
}
}
// if any character other than tab, pass it to the main loop
// move cursor to EOL to avoid overwriting the command line
let save_pos = s.line.pos();
try!(edit_move_end(s));
s.line.set_pos(save_pos);
// we got a second tab, maybe show list of possible completions
let show_completions = if candidates.len() > config.completion_prompt_limit() {
let msg = format!("\nDisplay all {} possibilities? (y or n)", candidates.len());
while cmd != Cmd::SelfInsert(1, 'y') && cmd != Cmd::SelfInsert(1, 'Y') &&
cmd != Cmd::SelfInsert(1, 'n') &&
cmd != Cmd::SelfInsert(1, 'N') &&
cmd != Cmd::Kill(Movement::BackwardChar(1))
{
fn page_completions<R: RawReader>(
rdr: &mut R,
s: &mut State,
candidates: &[String],
) -> Result<Option<Cmd>> {
let max_width = cmp::min(
cols,
candidates
.into_iter()
.map(|s| s.as_str().width())
.max()
.unwrap() + min_col_pad,
);
let num_rows = (candidates.len() + num_cols - 1) / num_cols;
let mut ab = String::new();
for row in 0..num_rows {
if row == pause_row {
while cmd != Cmd::SelfInsert(1, 'y') && cmd != Cmd::SelfInsert(1, 'Y') &&
cmd != Cmd::SelfInsert(1, 'q') &&
cmd != Cmd::SelfInsert(1, 'Q') &&
cmd != Cmd::SelfInsert(1, ' ') &&
cmd != Cmd::Kill(Movement::BackwardChar(1)) &&
cmd != Cmd::AcceptLine
{
Cmd::SelfInsert(1, 'y') | Cmd::SelfInsert(1, 'Y') | Cmd::SelfInsert(1, ' ') => {
}
ab.clear();
for col in 0..num_cols {
let i = (col * num_rows) + row;
if i < candidates.len() {
let candidate = &candidates[i];
ab.push_str(candidate);
if ((col + 1) * num_rows) + row < candidates.len() {
for _ in width..max_width {
ab.push(' ');
}
}
}
}
fn reverse_incremental_search<R: RawReader>(
rdr: &mut R,
s: &mut State,
history: &History,
) -> Result<Option<Cmd>> {
if history.is_empty() {
return Ok(None);
}
let mark = s.changes.borrow_mut().begin();
// Save the current edited line (and cursor position) before overwriting it
let backup = s.line.as_str().to_owned();
let backup_pos = s.line.pos();
let mut search_buf = String::new();
let mut history_idx = history.len() - 1;
let mut direction = Direction::Reverse;
// Display the reverse-i-search prompt and process chars
loop {
let prompt = if success {
format!("(reverse-i-search)`{}': ", search_buf)
} else {
format!("(failed reverse-i-search)`{}': ", search_buf)
};
try!(s.refresh_prompt_and_line(&prompt));
direction = Direction::Reverse;
if history_idx > 0 {
history_idx -= 1;
} else {
success = false;
continue;
}
direction = Direction::Forward;
if history_idx < history.len() - 1 {
history_idx += 1;
} else {
success = false;
continue;
}
}
success = match history.search(&search_buf, history_idx, direction) {
Some(idx) => {
history_idx = idx;
let entry = history.get(idx).unwrap();
/// Handles reading and editting the readline buffer.
/// It will also handle special inputs in an appropriate fashion
/// (e.g., C-c will exit readline)
let completer = editor.completer.as_ref();
let mut stdout = editor.term.create_writer();
editor.reset_kill_ring();
let mut s = State::new(
&mut stdout,
&editor.config,
prompt,
editor.history.len(),
editor.custom_bindings.clone(),
s.line.set_delete_listener(editor.kill_ring.clone());
s.line.set_change_listener(s.changes.clone());
s.line
.update((left.to_owned() + right).as_ref(), left.len());
let mut rdr = try!(editor.term.create_reader(&editor.config));
Main
committed
loop {
if cmd.should_reset_kill_ring() {
editor.reset_kill_ring();
}
completer.unwrap().borrow_mut().deref_mut(),
if let Cmd::SelfInsert(n, c) = cmd {
try!(edit_insert(&mut s, c, n));
} else if let Cmd::Insert(n, text) = cmd {
try!(edit_yank(&mut s, &text, Anchor::Before, n));
continue;
let next = try!(reverse_incremental_search(
&mut rdr,
&mut s,
&editor.history,
));
// Move to the beginning of line.
try!(edit_move_home(&mut s))
}
try!(edit_move_home(&mut s));
try!(edit_move_to_next_word(&mut s, At::Start, Word::Big, 1))
}
try!(edit_move_backward(&mut s, n))
Cmd::Replace(n, c) => {
try!(edit_replace_char(&mut s, c, n));
Cmd::Overwrite(c) => {
try!(edit_overwrite_char(&mut s, c));
}
Cmd::EndOfFile => if !s.edit_state.is_emacs_mode() && !s.line.is_empty() {
try!(edit_move_end(&mut s));
break;
} else if s.line.is_empty() {
return Err(error::ReadlineError::Eof);
} else {
try!(edit_delete(&mut s, 1))
},
try!(edit_move_forward(&mut s, n))
editor.kill_ring.borrow_mut().start_killing();
editor.kill_ring.borrow_mut().stop_killing();
editor.kill_ring.borrow_mut().start_killing();
editor.kill_ring.borrow_mut().stop_killing();
try!(edit_history_next(&mut s, &editor.history, false))
try!(edit_history_next(&mut s, &editor.history, true))
Cmd::HistorySearchBackward => try!(edit_history_search(
&mut s,
&editor.history,
Direction::Reverse,
)),
Cmd::HistorySearchForward => try!(edit_history_search(
&mut s,
&editor.history,
Direction::Forward,
)),
// Exchange the char before cursor with the character at cursor.
try!(edit_transpose_chars(&mut s))
}
editor.kill_ring.borrow_mut().start_killing();
editor.kill_ring.borrow_mut().stop_killing();
if let Some(text) = editor.kill_ring.borrow_mut().yank() {
Cmd::ViYankTo(mvt) => if let Some(text) = s.line.copy(mvt) {
editor.kill_ring.borrow_mut().kill(&text, Mode::Append)
},
// Accept the line regardless of where the cursor is.
try!(edit_move_end(&mut s));
if s.hinter.is_some() {
// Force a refresh without hints to leave the previous
// line as the user typed it after a newline.
s.hinter = None;
try!(s.refresh_line());
}
Cmd::Kill(Movement::BackwardWord(n, word_def)) => {
// kill one word backward (until start of word)
editor.kill_ring.borrow_mut().start_killing();
editor.kill_ring.borrow_mut().stop_killing();
// move to first entry in history
try!(edit_history(&mut s, &editor.history, true))
// move to last entry in history
try!(edit_history(&mut s, &editor.history, false))
// capitalize word after point
try!(edit_word(&mut s, WordAction::CAPITALIZE))
}
Cmd::Kill(Movement::ForwardWord(n, at, word_def)) => {
// kill one word forward (until start/end of word)
editor.kill_ring.borrow_mut().start_killing();
editor.kill_ring.borrow_mut().stop_killing();
// lowercase word after point
try!(edit_word(&mut s, WordAction::LOWERCASE))
}
// uppercase word after point
try!(edit_word(&mut s, WordAction::UPPERCASE))
if let Some((yank_size, text)) = editor.kill_ring.borrow_mut().yank_pop() {
Cmd::Move(Movement::ViCharSearch(n, cs)) => try!(edit_move_to(&mut s, cs, n)),
Cmd::Kill(Movement::ViCharSearch(n, cs)) => {
editor.kill_ring.borrow_mut().start_killing();
editor.kill_ring.borrow_mut().stop_killing();
s.line.remove_change_listener();
s.line.set_change_listener(s.changes.clone());
Cmd::Interrupt => {
return Err(error::ReadlineError::Interrupted);
}
Cmd::Suspend => {
try!(original_mode.disable_raw_mode());
try!(tty::suspend());
try!(editor.term.enable_raw_mode()); // TODO original_mode may have changed
// Ignore the character typed.
Main
committed
}
}
struct Guard(tty::Mode);
#[allow(unused_must_use)]
impl Drop for Guard {
fn drop(&mut self) {
}
}
/// Readline method that will enable RAW mode, call the `readline_edit()`
fn readline_raw(
prompt: &str,
initial: Option<(&str, &str)>,
editor: &mut Editor,
) -> Result<String> {
let original_mode = try!(editor.term.enable_raw_mode());
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_ref());
}
}
drop(guard); // try!(disable_raw_mode(original_mode));
}
fn readline_direct() -> Result<String> {
if try!(io::stdin().read_line(&mut line)) > 0 {
Ok(line)
} else {
Err(error::ReadlineError::Eof)
}
}
history: History,
completer: Option<Rc<RefCell<Completer>>>,
custom_bindings: Rc<RefCell<HashMap<KeyPress, Cmd>>>,
Self::with_config(Config::default())
}
pub fn with_config(config: Config) -> Editor {
history: History::with_config(config),
custom_bindings: Rc::new(RefCell::new(HashMap::new())),
/// This method will read a line from STDIN and will display a `prompt`.
///
/// It uses terminal-style interaction if `stdin` is connected to a
/// terminal.
/// Otherwise (e.g., if `stdin` is a pipe or the terminal is not supported),
/// it uses file-style interaction.
pub fn readline(&mut self, prompt: &str) -> Result<String> {
/// This function behaves in the exact same manner as `readline`, except
/// that it pre-populates the input area.
///
/// The text that resides in the input area is given as a 2-tuple.
/// The string on the left of the tuple what will appear to the left of the
/// cursor
/// and the string on the right is what will appear to the right of the
/// cursor.
pub fn readline_with_initial(&mut self, prompt: &str, initial: (&str, &str)) -> Result<String> {
self.readline_with(prompt, Some(initial))
}
fn readline_with(&mut self, prompt: &str, initial: Option<(&str, &str)>) -> Result<String> {
// Write prompt and flush it to stdout
let mut stdout = io::stdout();
try!(stdout.write_all(prompt.as_bytes()));
try!(stdout.flush());
readline_direct()
} else {
}
}
/// Load the history from the specified file.
pub fn load_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<()> {
self.history.load(path)
}
/// Save the history in the specified file.
pub fn save_history<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<()> {
self.history.save(path)
}
/// Add a new entry in the history.
pub fn add_history_entry<S: AsRef<str> + Into<String>>(&mut self, line: S) -> bool {
self.history.add(line)
}
/// Clear history.
pub fn clear_history(&mut self) {
self.history.clear()
}
/// Return a mutable reference to the history object.
pub fn get_history(&mut self) -> &mut History {
&mut self.history
}
/// Return an immutable reference to the history object.
pub fn get_history_const(&self) -> &History {
&self.history
}
/// Register a callback function to be called for tab-completion.
pub fn set_completer(&mut self, completer: Option<Rc<RefCell<Completer>>>) {
self.completer = completer;
/// Register a hints function to be called to show hints to the uer at the
/// right of the prompt.
pub fn set_hinter(&mut self, hinter: Option<Rc<RefCell<Hinter>>>) {
self.hinter = hinter;
}
/// 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("> ") {
/// match readline {
/// Ok(line) => {
/// println!("Line: {}", line);
/// },
/// Err(err) => {
/// println!("Error: {:?}", err);
/// break
/// }
/// }
/// }
/// ```
pub fn iter<'a>(&'a mut self, prompt: &'a str) -> Iter {
fn reset_kill_ring(&self) {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Editor")
.field("term", &self.term)
.field("config", &self.config)
type Item = Result<String>;
fn next(&mut self) -> Option<Result<String>> {
let readline = self.editor.readline(self.prompt);
match readline {
Err(error::ReadlineError::Eof) => None,
e @ Err(_) => Some(e),
}
}
}
use completion::Completer;
use keymap::{Cmd, EditState};
use super::{Editor, Position, Result, State};
fn init_state<'out>(out: &'out mut Renderer, line: &str, pos: usize) -> State<'out, 'static> {
line: LineBuffer::init(line, pos, None),
edit_state: EditState::new(&config, Rc::new(RefCell::new(HashMap::new()))),
fn init_editor(keys: &[KeyPress]) -> Editor {
let mut editor = Editor::new();
editor.term.keys.extend(keys.iter().cloned());
editor
}
#[test]
fn edit_history_next() {
let mut out = ::std::io::sink();
let line = "current edited line";
let mut history = History::new();
history.add("line0");
history.add("line1");
s.history_index = history.len();
for _ in 0..2 {
super::edit_history_next(&mut s, &history, false).unwrap();
super::edit_history_next(&mut s, &history, false).unwrap();
super::edit_history_next(&mut s, &history, false).unwrap();
struct SimpleCompleter;
impl Completer for SimpleCompleter {
fn complete(&mut self, line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
Ok((0, vec![line.to_owned() + "t"]))
}
}
#[test]
fn complete_line() {
let mut out = ::std::io::sink();
let keys = &[KeyPress::Enter];
let mut rdr = keys.iter();
let cmd =
super::complete_line(&mut rdr, &mut s, &mut completer, &Config::default()).unwrap();
fn assert_line(keys: &[KeyPress], expected_line: &str) {
let mut editor = init_editor(keys);
let actual_line = editor.readline(&">>").unwrap();
assert_eq!(expected_line, actual_line);
}
#[test]
fn delete_key() {
assert_line(
&[KeyPress::Char('a'), KeyPress::Delete, KeyPress::Enter],
"a",
);
assert_line(
&[
KeyPress::Char('a'),
KeyPress::Left,
KeyPress::Delete,
KeyPress::Enter,
],
"",
);
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
}
#[test]
fn down_key() {
assert_line(&[KeyPress::Down, KeyPress::Enter], "");
}
#[test]
fn end_key() {
assert_line(&[KeyPress::End, KeyPress::Enter], "");
}
#[test]
fn home_key() {
assert_line(&[KeyPress::Home, KeyPress::Enter], "");
}
#[test]
fn left_key() {
assert_line(&[KeyPress::Left, KeyPress::Enter], "");
}
#[test]
fn meta_backspace_key() {
assert_line(&[KeyPress::Meta('\x08'), KeyPress::Enter], "");
}
#[test]
fn page_down_key() {
assert_line(&[KeyPress::PageDown, KeyPress::Enter], "");
}
#[test]
fn page_up_key() {
assert_line(&[KeyPress::PageUp, KeyPress::Enter], "");
}
#[test]
fn right_key() {
assert_line(&[KeyPress::Right, KeyPress::Enter], "");
}
#[test]
fn up_key() {
assert_line(&[KeyPress::Up, KeyPress::Enter], "");
}
#[test]
fn unknown_esc_key() {
assert_line(&[KeyPress::UnknownEscSeq, KeyPress::Enter], "");
}