Basic backspacing

This commit is contained in:
Timothy Warren 2019-08-30 11:20:52 -04:00
parent 19c724d18f
commit 8e125b8c7c
2 changed files with 67 additions and 7 deletions

View File

@ -11,6 +11,7 @@ use std::time::{Duration, Instant};
use self::EditorKey::*; use self::EditorKey::*;
const KILO_TAB_STOP: usize = 4; const KILO_TAB_STOP: usize = 4;
const KILO_QUIT_TIMES: u8 = 3;
/// A representation of a line in the editor /// A representation of a line in the editor
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -40,10 +41,14 @@ pub struct Editor {
screen_cols: usize, screen_cols: usize,
screen_rows: usize, screen_rows: usize,
rows: Vec<EditorRow>, rows: Vec<EditorRow>,
dirty: u64,
filename: String, filename: String,
status_message: String, status_message: String,
status_message_time: Instant, status_message_time: Instant,
// Properties not present in C version
output_buffer: String, output_buffer: String,
quit_times: u8,
} }
/// Keycode mapping enum /// Keycode mapping enum
@ -87,10 +92,12 @@ impl Default for Editor {
screen_cols: 0, screen_cols: 0,
screen_rows: 0, screen_rows: 0,
rows: vec![], rows: vec![],
dirty: 0,
filename: String::new(), filename: String::new(),
status_message: String::new(), status_message: String::new(),
status_message_time: Instant::now(), status_message_time: Instant::now(),
output_buffer: String::new(), output_buffer: String::new(),
quit_times: KILO_QUIT_TIMES,
} }
} }
} }
@ -126,6 +133,7 @@ impl Editor {
for char in in_str.chars() { for char in in_str.chars() {
input.push(match char { input.push(match char {
'\x08' => Backspace,
'\x7f' => Backspace, '\x7f' => Backspace,
'\x1b' => Escape, '\x1b' => Escape,
'\r' => Enter, '\r' => Enter,
@ -285,6 +293,7 @@ impl Editor {
match char { match char {
Backspace => self._del_or_backspace(Backspace), Backspace => self._del_or_backspace(Backspace),
DeleteKey => self._del_or_backspace(DeleteKey),
Enter => { Enter => {
// TODO // TODO
} }
@ -306,6 +315,11 @@ impl Editor {
OtherKey(c) => { OtherKey(c) => {
if c.is_ascii_control() { if c.is_ascii_control() {
if c == ctrl_key('q') { if c == ctrl_key('q') {
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.quit_times -= 1;
return Some(OtherKey('\0'));
}
print!("\x1b[2J"); print!("\x1b[2J");
print!("\x1b[H"); print!("\x1b[H");
// Break out of the input loop // Break out of the input loop
@ -313,10 +327,10 @@ impl Editor {
} }
if c == ctrl_key('s') { if c == ctrl_key('s') {
// @TODO show save success/error // Save success/error message handled by save method
match self.save() { match self.save() {
Ok(_) => (), Ok(_) => (),
Err(e) => (), Err(_) => (),
} }
} }
@ -330,6 +344,8 @@ impl Editor {
_ => (), _ => (),
}; };
self.quit_times = KILO_QUIT_TIMES;
return key; return key;
} }
@ -338,7 +354,10 @@ impl Editor {
} }
fn _del_or_backspace(&mut self, key: EditorKey<char>) { fn _del_or_backspace(&mut self, key: EditorKey<char>) {
// TODO if key == DeleteKey {
self.move_cursor(&ArrowRight);
}
self.delete_char();
} }
fn _page_up_or_down(&mut self, key: EditorKey<char>) { fn _page_up_or_down(&mut self, key: EditorKey<char>) {
@ -457,7 +476,9 @@ impl Editor {
&self.filename &self.filename
}; };
let mut left_message = format!("{:.80} - {} lines", filename, self.rows.len()); let modified = if self.dirty > 0 { "(modified}" } else { "" };
let mut left_message = format!("{:.80} - {} lines {}", filename, self.rows.len(), modified);
let right_message = format!("{}/{}", self.cursor_y + 1, self.rows.len()); let right_message = format!("{}/{}", self.cursor_y + 1, self.rows.len());
let mut len = left_message.len(); let mut len = left_message.len();
if len > self.screen_cols { if len > self.screen_cols {
@ -567,6 +588,8 @@ impl Editor {
fn append_row(&mut self, row: &str) { fn append_row(&mut self, row: &str) {
self.rows.push(EditorRow::new(row)); self.rows.push(EditorRow::new(row));
self.update_row(self.rows.len() - 1); self.update_row(self.rows.len() - 1);
self.dirty += 1;
} }
fn row_insert_char(&mut self, row_index: usize, char_index: usize, ch: char) { fn row_insert_char(&mut self, row_index: usize, char_index: usize, ch: char) {
@ -580,6 +603,20 @@ impl Editor {
row.chars.insert(at, ch); row.chars.insert(at, ch);
self.update_row(row_index); self.update_row(row_index);
self.dirty += 1;
}
fn row_delete_char(&mut self, row_index: usize, char_index: usize) {
let row = &mut self.rows[row_index];
if char_index >= row.chars.len() {
return;
}
row.chars.remove(char_index);
self.update_row(row_index);
self.dirty += 1;
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -595,6 +632,17 @@ impl Editor {
self.cursor_x += 1; self.cursor_x += 1;
} }
fn delete_char(&mut self) {
if self.cursor_y == self.rows.len() {
return;
}
if self.cursor_x > 0 {
self.row_delete_char(self.cursor_y, self.cursor_x - 1);
self.cursor_x -= 1;
}
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// File I/O // File I/O
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -624,18 +672,30 @@ impl Editor {
self.append_row(&line); self.append_row(&line);
} }
self.dirty = 0;
Ok(()) Ok(())
} }
fn save(&mut self) -> io::Result<()> { fn save(&mut self) -> io::Result<()> {
if self.filename.len() == 0 { if self.filename.len() == 0 {
return Ok(()) return Ok(());
} }
let mut file = File::create(&self.filename)?; let mut file = File::create(&self.filename)?;
let data = &mut self.rows_to_string(); let data = &mut self.rows_to_string();
file.write_all(data.as_bytes())?; let res = file.write_all(data.as_bytes());
match res {
Ok(()) => {
self.dirty = 0;
self.set_status_message(&format!("{} bytes written to disk", data.len()));
}
Err(e) => self.set_status_message(&format!("Failed to save: {:?}", e)),
};
file.sync_all()?; file.sync_all()?;
Ok(()) Ok(())

View File

@ -25,7 +25,7 @@ fn main() -> Result<(), Error> {
editor.open(&args[1])?; editor.open(&args[1])?;
} }
editor.set_status_message("HELP: Ctrl-Q = quit"); editor.set_status_message("HELP: Ctrl-S = save | Ctrl-Q = quit");
// 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,