From 8e125b8c7c7b3df8dd73d00a52edcec9fbd2d3af Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Fri, 30 Aug 2019 11:20:52 -0400 Subject: [PATCH] Basic backspacing --- src/editor.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++----- src/main.rs | 2 +- 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index d177f25..060d93b 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -11,6 +11,7 @@ use std::time::{Duration, Instant}; use self::EditorKey::*; const KILO_TAB_STOP: usize = 4; +const KILO_QUIT_TIMES: u8 = 3; /// A representation of a line in the editor #[derive(Debug, Default)] @@ -40,10 +41,14 @@ pub struct Editor { screen_cols: usize, screen_rows: usize, rows: Vec, + dirty: u64, filename: String, status_message: String, status_message_time: Instant, + + // Properties not present in C version output_buffer: String, + quit_times: u8, } /// Keycode mapping enum @@ -87,10 +92,12 @@ impl Default for Editor { screen_cols: 0, screen_rows: 0, rows: vec![], + dirty: 0, filename: String::new(), status_message: String::new(), status_message_time: Instant::now(), output_buffer: String::new(), + quit_times: KILO_QUIT_TIMES, } } } @@ -126,6 +133,7 @@ impl Editor { for char in in_str.chars() { input.push(match char { + '\x08' => Backspace, '\x7f' => Backspace, '\x1b' => Escape, '\r' => Enter, @@ -285,6 +293,7 @@ impl Editor { match char { Backspace => self._del_or_backspace(Backspace), + DeleteKey => self._del_or_backspace(DeleteKey), Enter => { // TODO } @@ -306,6 +315,11 @@ impl Editor { OtherKey(c) => { if c.is_ascii_control() { 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[H"); // Break out of the input loop @@ -313,10 +327,10 @@ impl Editor { } if c == ctrl_key('s') { - // @TODO show save success/error + // Save success/error message handled by save method match self.save() { Ok(_) => (), - Err(e) => (), + Err(_) => (), } } @@ -330,6 +344,8 @@ impl Editor { _ => (), }; + self.quit_times = KILO_QUIT_TIMES; + return key; } @@ -338,7 +354,10 @@ impl Editor { } fn _del_or_backspace(&mut self, key: EditorKey) { - // TODO + if key == DeleteKey { + self.move_cursor(&ArrowRight); + } + self.delete_char(); } fn _page_up_or_down(&mut self, key: EditorKey) { @@ -457,7 +476,9 @@ impl Editor { &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 mut len = left_message.len(); if len > self.screen_cols { @@ -567,6 +588,8 @@ impl Editor { fn append_row(&mut self, row: &str) { self.rows.push(EditorRow::new(row)); self.update_row(self.rows.len() - 1); + + self.dirty += 1; } 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); 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; } + 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 // ------------------------------------------------------------------------ @@ -624,18 +672,30 @@ impl Editor { self.append_row(&line); } + self.dirty = 0; + Ok(()) } fn save(&mut self) -> io::Result<()> { if self.filename.len() == 0 { - return Ok(()) + return Ok(()); } let mut file = File::create(&self.filename)?; 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()?; Ok(()) diff --git a/src/main.rs b/src/main.rs index 393fd4e..9f5999f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ fn main() -> Result<(), Error> { 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. // `None` is returned on a quit action, in other cases, `Some(())` is returned,