Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement M-d and M-DEL #29

Merged
merged 1 commit into from
Apr 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ rustyline = "0.2.2"
- Unicode (UTF-8) (linenoise supports only ASCII)
- Word completion (linenoise supports only line completion)
- Filename completion
- History search ([Searching for Commands in the History](http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC8)
- History search ([Searching for Commands in the History](http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC8))
- Kill ring ([Killing Commands](http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#IDX3))

## ToDo
Expand Down
2 changes: 2 additions & 0 deletions src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub enum KeyPress {
BACKSPACE,
UNKNOWN_ESC_SEQ,
ESC_SEQ_DELETE,
ESC_BACKSPACE,
ESC_D,
ESC_Y,
}

Expand Down
84 changes: 63 additions & 21 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,17 +452,19 @@ fn edit_transpose_chars(s: &mut State) -> Result<()> {

/// Delete the previous word, maintaining the cursor at the start of the
/// current word.
fn edit_delete_prev_word(s: &mut State) -> Result<Option<String>> {
fn edit_delete_prev_word<F>(s: &mut State, test: F) -> Result<Option<String>>
where F: Fn(char) -> bool
{
if s.pos > 0 {
let old_pos = s.pos;
let mut ch = s.buf.char_at_reverse(s.pos);
// eat any spaces on the left
while s.pos > 0 && ch.is_whitespace() {
while s.pos > 0 && test(ch) {
s.pos -= ch.len_utf8();
ch = s.buf.char_at_reverse(s.pos);
}
// eat any non-spaces on the left
while s.pos > 0 && !ch.is_whitespace() {
while s.pos > 0 && !test(ch) {
s.pos -= ch.len_utf8();
ch = s.buf.char_at_reverse(s.pos);
}
Expand All @@ -474,6 +476,27 @@ fn edit_delete_prev_word(s: &mut State) -> Result<Option<String>> {
}
}

/// 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) -> Result<Option<String>> {
if s.pos < s.buf.len() {
let mut pos = s.pos;
let mut ch = s.buf.char_at(pos);
while pos < s.buf.len() && !ch.is_alphanumeric() {
pos += ch.len_utf8();
ch = s.buf.char_at(pos);
}
while pos < s.buf.len() && ch.is_alphanumeric() {
pos += ch.len_utf8();
ch = s.buf.char_at(pos);
}
let text = s.buf.drain(s.pos..pos).collect();
try!(s.refresh_line());
Ok(Some(text))
} else {
Ok(None)
}
}

/// Substitute the currently edited line with the next or previous history
/// entry.
fn edit_history_next(s: &mut State, history: &History, prev: bool) -> Result<()> {
Expand Down Expand Up @@ -673,19 +696,19 @@ fn escape_sequence<R: io::Read>(chars: &mut io::Chars<R>) -> Result<KeyPress> {
} else {
// TODO ESC-B (b): move backward a word (https://github.com/antirez/linenoise/pull/64, https://github.com/antirez/linenoise/pull/6)
// TODO ESC-C (c): capitalize word after point
// TODO ESC-D (d): kill one word forward
// TODO ESC-F (f): move forward a word
// TODO ESC-L (l): lowercase word after point
// TODO ESC-N (n): search history forward not interactively
// TODO ESC-P (p): search history backward not interactively
// TODO ESC-R (r): Undo all changes made to this line.
// TODO EST-T (t): transpose words
// TODO ESC-U (u): uppercase word after point
// TODO ESC-CTRl-H | ESC-BACKSPACE kill one word backward
// TODO ESC-<: move to first entry in history
// TODO ESC->: move to last entry in history
match seq1 {
'd' | 'D' => Ok(KeyPress::ESC_D),
'y' | 'Y' => Ok(KeyPress::ESC_Y),
'\x08' | '\x7f' => Ok(KeyPress::ESC_BACKSPACE),
_ => {
writeln!(io::stderr(), "key: {:?}, seq1, {:?}", KeyPress::ESC, seq1).unwrap();
Ok(KeyPress::UNKNOWN_ESC_SEQ)
Expand Down Expand Up @@ -794,9 +817,8 @@ fn readline_edit(prompt: &str,
}
KeyPress::CTRL_K => {
// Kill the text from point to the end of the line.
match try!(edit_kill_line(&mut s)) {
Some(text) => kill_ring.kill(&text, true),
None => (),
if let Some(text) = try!(edit_kill_line(&mut s)) {
kill_ring.kill(&text, true)
}
}
KeyPress::CTRL_L => {
Expand All @@ -821,31 +843,41 @@ fn readline_edit(prompt: &str,
}
KeyPress::CTRL_U => {
// Kill backward from point to the beginning of the line.
match try!(edit_discard_line(&mut s)) {
Some(text) => kill_ring.kill(&text, false),
None => (),
if let Some(text) = try!(edit_discard_line(&mut s)) {
kill_ring.kill(&text, false)
}
}
// TODO CTRL_V // Quoted insert
KeyPress::CTRL_W => {
// Kill the word behind point, using white space as a word boundary
match try!(edit_delete_prev_word(&mut s)) {
Some(text) => kill_ring.kill(&text, false),
None => (),
if let Some(text) = try!(edit_delete_prev_word(&mut s, char::is_whitespace)) {
kill_ring.kill(&text, false)
}
}
KeyPress::CTRL_Y => {
// retrieve (yank) last item killed
match kill_ring.yank() {
Some(text) => try!(edit_yank(&mut s, text)),
None => (),
if let Some(text) = kill_ring.yank() {
try!(edit_yank(&mut s, text))
}
}
KeyPress::ESC_BACKSPACE => {
// kill one word backward
// Kill from the cursor the start of the current word, or, if between words, to the start of the previous word.
if let Some(text) = try!(edit_delete_prev_word(&mut s,
|ch| !ch.is_alphanumeric())) {
kill_ring.kill(&text, false)
}
}
KeyPress::ESC_D => {
// kill one word forward
if let Some(text) = try!(edit_delete_word(&mut s)) {
kill_ring.kill(&text, true)
}
}
KeyPress::ESC_Y => {
// yank-pop
match kill_ring.yank_pop() {
Some((yank_size, text)) => try!(edit_yank_pop(&mut s, yank_size, text)),
None => (),
if let Some((yank_size, text)) = kill_ring.yank_pop() {
try!(edit_yank_pop(&mut s, yank_size, text))
}
}
// TODO CTRL-_ // undo
Expand Down Expand Up @@ -1087,12 +1119,22 @@ mod test {
fn delete_prev_word() {
let mut out = ::std::io::sink();
let mut s = init_state(&mut out, "a ß c", 6, 80);
let text = super::edit_delete_prev_word(&mut s).unwrap();
let text = super::edit_delete_prev_word(&mut s, char::is_whitespace).unwrap();
assert_eq!("a c", s.buf);
assert_eq!(2, s.pos);
assert_eq!(Some("ß ".to_string()), text);
}

#[test]
fn delete_word() {
let mut out = ::std::io::sink();
let mut s = init_state(&mut out, "a ß c", 1, 80);
let text = super::edit_delete_word(&mut s).unwrap();
assert_eq!("a c", s.buf);
assert_eq!(1, s.pos);
assert_eq!(Some(" ß".to_string()), text);
}

#[test]
fn edit_history_next() {
let mut out = ::std::io::sink();
Expand Down