Treat ctrl+letter chords as their own character class
This commit is contained in:
parent
6497111427
commit
f8700c8e93
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
11
src/main.rs
11
src/main.rs
@ -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)
|
||||||
|
|
||||||
|
()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user