Treat ctrl+letter chords as their own character class

This commit is contained in:
Timothy Warren 2019-09-17 11:19:46 -04:00
parent 6497111427
commit f8700c8e93
2 changed files with 47 additions and 44 deletions

View File

@ -146,6 +146,8 @@ pub enum KeyCode<T = char> {
EndKey, EndKey,
PageUp, PageUp,
PageDown, PageDown,
/// Control key chords
Ctrl(T),
/// Function keys (F1, etc.) T holds the index /// Function keys (F1, etc.) T holds the index
Function(T), Function(T),
/// Any other type of character /// Any other type of character
@ -188,6 +190,8 @@ impl Row {
impl KeyCode<char> { impl KeyCode<char> {
pub fn unwrap(self) -> char { pub fn unwrap(self) -> char {
match self { match self {
self::Ctrl(val) => val,
self::Function(val) => val,
self::OtherKey(val) => val, self::OtherKey(val) => val,
_ => panic!("called `KeyCode::unwrap()` on a `None` value"), _ => panic!("called `KeyCode::unwrap()` on a `None` value"),
} }
@ -281,7 +285,13 @@ impl Editor {
'\x08' => return Some(Backspace), '\x08' => return Some(Backspace),
'\x7f' => return Some(Backspace), '\x7f' => return Some(Backspace),
'\r' => return Some(Enter), '\r' => return Some(Enter),
_ => return Some(OtherKey(ch)), ch => {
if ch.is_ascii_control() {
return Some(Ctrl(ctrl_to_letter(ch)))
}
return Some(OtherKey(ch))
},
}, },
None => return None, None => return None,
} }
@ -737,7 +747,7 @@ impl Editor {
loop { loop {
self.set_status_message(&format!("{} {}", prompt, buffer)); self.set_status_message(&format!("{} {}", prompt, buffer));
self.refresh_screen(false); self.refresh_screen();
let ch = self.read_key(); let ch = self.read_key();
if ch.is_some() { if ch.is_some() {
@ -851,14 +861,18 @@ impl Editor {
self.cursor_x = self.rows[self.cursor_y].chars.len(); self.cursor_x = self.rows[self.cursor_y].chars.len();
} }
} }
Function(_) => (), Ctrl(c) => {
OtherKey(c) => { match c {
if c.is_ascii_control() { 'f' => self.find(),
if c == ctrl_key('f') { // 'h' => self._del_or_backspace(Backspace),
self.find(); 's' => {
// Save success/error message handled by save method
match self.save() {
Ok(_) => (),
Err(_) => (),
}
} }
'q' => {
if c == ctrl_key('q') {
if self.dirty > 0 && self.quit_times > 0 { if self.dirty > 0 && self.quit_times > 0 {
self.set_status_message(&format!("WARNING!!! File has unsaved changes. Press Ctrl-Q {} more times to quit.", self.quit_times)); self.set_status_message(&format!("WARNING!!! File has unsaved changes. Press Ctrl-Q {} more times to quit.", self.quit_times));
self.quit_times -= 1; self.quit_times -= 1;
@ -868,23 +882,14 @@ impl Editor {
print!("\x1b[H"); print!("\x1b[H");
// Break out of the input loop // Break out of the input loop
return None; return None;
} },
_ => (),
if c == ctrl_key('s') {
// Save success/error message handled by save method
match self.save() {
Ok(_) => (),
Err(_) => (),
}
}
if c == ctrl_key('h') {
self._del_or_backspace(Backspace);
}
} else {
self.insert_char(c);
} }
} }
Function(_) => (),
OtherKey(c) => {
self.insert_char(c);
}
}; };
self.quit_times = KILO_QUIT_TIMES; self.quit_times = KILO_QUIT_TIMES;
@ -1094,11 +1099,7 @@ impl Editor {
} }
} }
pub fn refresh_screen(&mut self, skip_refresh: bool) { pub fn refresh_screen(&mut self) {
if skip_refresh {
return;
}
self.scroll(); self.scroll();
self.output_buffer.clear(); self.output_buffer.clear();
@ -1618,18 +1619,17 @@ fn get_syntax_db() -> Vec<Syntax> {
] ]
} }
/// Convert Ctrl+letter chords to their /// Convert Ctrl+letter chord to their
/// ASCII table equivalents /// letter equivalent
pub fn ctrl_key(c: char) -> char { pub fn ctrl_to_letter(c: char) -> char {
let key = c as u8; let key = c as u8;
if !c.is_ascii() { if (!c.is_ascii_control()) || c == '\x7f' {
panic!("CTRL_KEY only accepts ASCII characters"); panic!("Only ascii control characters have associated letters")
} }
// Intentionally "overflow" u8 to wrap around to the // Shift forward to the letter equivalent
// beginning of the ASCII table. Ctrl+a is 1, Ctrl+b is 2, etc. (key + 0x60) as char
(key & 0x1f) as char
} }
/// Determine whether a character is one which separates tokens /// Determine whether a character is one which separates tokens
@ -1725,4 +1725,10 @@ mod tests {
assert_eq!(is_separator(ch), false); assert_eq!(is_separator(ch), false);
} }
} }
#[test]
fn ctrl_key_functions() {
let a = ctrl_to_letter(ctrl_a);
assert_eq!(a, 'a', "ctrl_to_letter gives letter from ctrl chord");
}
} }

View File

@ -38,23 +38,20 @@ fn main() -> Result<(), Error> {
editor.set_status_message("HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find"); editor.set_status_message("HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find");
let mut skip_refresh: bool = false;
// Main input loop. Editor::process_keypress uses an Option Enum as a sentinel. // Main input loop. Editor::process_keypress uses an Option Enum as a sentinel.
// `None` is returned on a quit action, in other cases, `Some(())` is returned, // `None` is returned on a quit action, in other cases, `Some(())` is returned,
// continuing the loop // continuing the loop
loop { loop {
editor.refresh_screen(skip_refresh); editor.refresh_screen();
match editor.process_keypress() { match editor.process_keypress() {
Some(key) => { Some(key) => {
match key { match key {
editor::KeyCode::OtherKey('\0') => { editor::KeyCode::OtherKey('\0') => (),
skip_refresh = true;
}
_ => { _ => {
skip_refresh = false;
//println!("{:?}\r\n", key) //println!("{:?}\r\n", key)
()
} }
} }
} }