...
 
Commits (10)
[package]
name = "liner"
version = "0.5.0" # remember to update README
version = "0.6.0" # remember to update README
authors = ["MovingtoMars <definitelynotliam@gmail.com>"]
description = "A library offering readline-like functionality."
repository = "https://gitlab.redox-os.org/redox-os/liner"
......
......@@ -20,7 +20,7 @@ A Rust library offering readline-like functionality.
In `Cargo.toml`:
```toml
[dependencies]
liner = "0.5.0"
liner = "0.6.0"
...
```
......
......@@ -130,6 +130,9 @@ impl Context {
if keymap.handle_key(c.unwrap(), &mut ed, handler)? {
break;
}
ed.display()?;
ed.flush()?;
}
Ok(ed.into())
......
This diff is collapsed.
use std::io::{self, Write};
use std::io::Write;
use termion::event::Key;
use crate::CursorPosition;
......@@ -22,7 +22,7 @@ impl Emacs {
Self::default()
}
fn handle_ctrl_key<'a, W: Write>(&mut self, c: char, ed: &mut Editor<'a, W>) -> io::Result<()> {
fn handle_ctrl_key<'a, W: Write>(&mut self, c: char, ed: &mut Editor<'a, W>) {
match c {
'l' => ed.clear(),
'a' => ed.move_cursor_to_start_of_line(),
......@@ -36,14 +36,13 @@ impl Emacs {
'k' => ed.delete_all_after_cursor(),
'w' => ed.delete_word_before_cursor(true),
'x' => {
ed.undo()?;
Ok(())
ed.undo();
}
_ => Ok(()),
_ => (),
}
}
fn handle_alt_key<'a, W: Write>(&mut self, c: char, ed: &mut Editor<'a, W>) -> io::Result<()> {
fn handle_alt_key<'a, W: Write>(&mut self, c: char, ed: &mut Editor<'a, W>) {
match c {
'<' => ed.move_to_start_of_history(),
'>' => ed.move_to_end_of_history(),
......@@ -51,22 +50,21 @@ impl Emacs {
'f' => emacs_move_word(ed, EmacsMoveDir::Right),
'b' => emacs_move_word(ed, EmacsMoveDir::Left),
'r' => {
ed.revert()?;
Ok(())
ed.revert();
}
'.' => self.handle_last_arg_fetch(ed),
_ => Ok(()),
_ => (),
}
}
fn handle_last_arg_fetch<'a, W: Write>(&mut self, ed: &mut Editor<'a, W>) -> io::Result<()> {
fn handle_last_arg_fetch<'a, W: Write>(&mut self, ed: &mut Editor<'a, W>) {
// Empty history means no last arg to fetch.
if ed.context().history.is_empty() {
return Ok(());
return;
}
let history_index = match self.last_arg_fetch_index {
Some(0) => return Ok(()),
Some(0) => return,
Some(x) => x - 1,
None => ed
.current_history_location()
......@@ -78,29 +76,23 @@ impl Emacs {
if self.last_arg_fetch_index.is_some() {
let buffer_len = ed.current_buffer().num_chars();
if let Some(last_arg_len) = ed.current_buffer().last_arg().map(|x| x.len()) {
ed.delete_until(buffer_len - last_arg_len)?;
ed.delete_until(buffer_len - last_arg_len);
}
}
// Actually insert it
let buf = ed.context().history[history_index].clone();
if let Some(last_arg) = buf.last_arg() {
ed.insert_chars_after_cursor(last_arg)?;
ed.insert_chars_after_cursor(last_arg);
}
// Edit the index in case the user does a last arg fetch again.
self.last_arg_fetch_index = Some(history_index);
Ok(())
}
}
impl KeyMap for Emacs {
fn handle_key_core<'a, W: Write>(
&mut self,
key: Key,
ed: &mut Editor<'a, W>,
) -> io::Result<()> {
fn handle_key_core<'a, W: Write>(&mut self, key: Key, ed: &mut Editor<'a, W>) {
match key {
Key::Alt('.') => {}
_ => self.last_arg_fetch_index = None,
......@@ -118,8 +110,7 @@ impl KeyMap for Emacs {
Key::End => ed.move_cursor_to_end_of_line(),
Key::Backspace => ed.delete_before_cursor(),
Key::Delete => ed.delete_after_cursor(),
Key::Null => Ok(()),
_ => Ok(()),
_ => (),
}
}
}
......@@ -130,7 +121,7 @@ enum EmacsMoveDir {
Right,
}
fn emacs_move_word<W: Write>(ed: &mut Editor<W>, direction: EmacsMoveDir) -> io::Result<()> {
fn emacs_move_word<W: Write>(ed: &mut Editor<W>, direction: EmacsMoveDir) {
let (words, pos) = ed.get_words_and_cursor_position();
let word_index = match pos {
......@@ -154,7 +145,7 @@ fn emacs_move_word<W: Write>(ed: &mut Editor<W>, direction: EmacsMoveDir) -> io:
};
match word_index {
None => Ok(()),
None => {}
Some(i) => {
let (start, end) = words[i];
......@@ -206,7 +197,7 @@ mod tests {
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new();
ed.insert_str_after_cursor("done").unwrap();
ed.insert_str_after_cursor("done");
assert_eq!(ed.cursor(), 4);
assert!(simulate_keys(&mut map, &mut ed, [Key::Char('\n')].iter()));
......@@ -221,7 +212,7 @@ mod tests {
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new();
ed.insert_str_after_cursor("let").unwrap();
ed.insert_str_after_cursor("let");
assert_eq!(ed.cursor(), 3);
simulate_keys(&mut map, &mut ed, [Key::Left, Key::Char('f')].iter());
......@@ -237,7 +228,7 @@ mod tests {
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new();
ed.insert_str_after_cursor("abc def ghi").unwrap();
ed.insert_str_after_cursor("abc def ghi");
assert_eq!(ed.cursor(), 11);
simulate_keys(&mut map, &mut ed, [Key::Alt('b')].iter());
......@@ -257,7 +248,7 @@ mod tests {
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new();
ed.insert_str_after_cursor("right").unwrap();
ed.insert_str_after_cursor("right");
assert_eq!(ed.cursor(), 5);
simulate_keys(&mut map, &mut ed, [Key::Left, Key::Left, Key::Right].iter());
......@@ -272,7 +263,7 @@ mod tests {
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = Emacs::new();
ed.insert_str_after_cursor("not empty").unwrap();
ed.insert_str_after_cursor("not empty");
let res = map.handle_key(Key::Ctrl('h'), &mut ed, &mut EmptyCompleter);
assert_eq!(res.is_ok(), true);
......
......@@ -5,11 +5,7 @@ use std::io::{self, ErrorKind, Write};
use termion::event::Key;
pub trait KeyMap: Default {
fn handle_key_core<'a, W: Write>(
&mut self,
key: Key,
editor: &mut Editor<'a, W>,
) -> io::Result<()>;
fn handle_key_core<'a, W: Write>(&mut self, key: Key, editor: &mut Editor<'a, W>);
fn init<'a, W: Write>(&mut self, _editor: &mut Editor<'a, W>) {}
......@@ -23,8 +19,6 @@ pub trait KeyMap: Default {
handler.on_event(Event::new(editor, EventKind::BeforeKey(key)));
let is_empty = editor.current_buffer().is_empty();
if key == Key::Ctrl('h') {
// XXX: Might need to change this when remappable keybindings are added.
key = Key::Backspace;
......@@ -32,43 +26,35 @@ pub trait KeyMap: Default {
match key {
Key::Ctrl('c') => {
editor.handle_newline()?;
editor.handle_newline();
return Err(io::Error::new(ErrorKind::Interrupted, "ctrl-c"));
}
// if the current buffer is empty, treat ctrl-d as eof
Key::Ctrl('d') if is_empty => {
editor.handle_newline()?;
Key::Ctrl('d') if editor.current_buffer().is_empty() => {
editor.handle_newline();
return Err(io::Error::new(ErrorKind::UnexpectedEof, "ctrl-d"));
}
Key::Char('\t') => editor.complete(handler)?,
Key::Char('\n') => {
done = editor.handle_newline()?;
}
Key::Char('\t') => editor.complete(handler),
Key::Char('\n') => done = editor.handle_newline(),
Key::Ctrl('f') if editor.is_currently_showing_autosuggestion() => {
editor.accept_autosuggestion()?;
}
Key::Ctrl('r') => {
editor.search(false)?;
}
Key::Ctrl('s') => {
editor.search(true)?;
editor.accept_autosuggestion()
}
Key::Ctrl('r') => editor.search(false),
Key::Ctrl('s') => editor.search(true),
Key::Right
if editor.is_currently_showing_autosuggestion()
&& editor.cursor_is_at_end_of_line() =>
{
editor.accept_autosuggestion()?;
editor.accept_autosuggestion()
}
_ => {
self.handle_key_core(key, editor)?;
self.handle_key_core(key, editor);
editor.skip_completions_hint();
}
};
handler.on_event(Event::new(editor, EventKind::AfterKey(key)));
editor.flush()?;
Ok(done)
}
}
......@@ -90,13 +76,7 @@ mod tests {
struct TestKeyMap;
impl KeyMap for TestKeyMap {
fn handle_key_core<'a, W: Write>(
&mut self,
_: Key,
_: &mut Editor<'a, W>,
) -> io::Result<()> {
Ok(())
}
fn handle_key_core<'a, W: Write>(&mut self, _: Key, _: &mut Editor<'a, W>) {}
}
struct EmptyCompleter;
......@@ -127,7 +107,7 @@ mod tests {
let out = Vec::new();
let mut ed = Editor::new(out, "prompt".to_owned(), None, &mut context).unwrap();
let mut map = TestKeyMap;
ed.insert_str_after_cursor("not empty").unwrap();
ed.insert_str_after_cursor("not empty");
let res = map.handle_key(Ctrl('d'), &mut ed, &mut EmptyCompleter);
assert_eq!(res.is_ok(), true);
......
This diff is collapsed.