From 11798f21bd28dc3ba5c2c9e008fc25e3ed43b8d2 Mon Sep 17 00:00:00 2001 From: Carol Date: Tue, 10 Jan 2023 15:33:56 +0200 Subject: [PATCH 1/4] Add UNDO (CTRL+Z) functionality for last cut, remove line and paste --- src/editor.rs | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index e9cc605c..4537d094 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -21,10 +21,12 @@ const PASTE: u8 = ctrl_key(b'V'); const DUPLICATE: u8 = ctrl_key(b'D'); const EXECUTE: u8 = ctrl_key(b'E'); const REMOVE_LINE: u8 = ctrl_key(b'R'); +const UNDO: u8 = ctrl_key(b'Z'); +const REDO: u8 = ctrl_key(b'Y'); const BACKSPACE: u8 = 127; const HELP_MESSAGE: &str = - "^S save | ^Q quit | ^F find | ^G go to | ^D duplicate | ^E execute | ^C copy | ^X cut | ^V paste"; + "^S save | ^Q quit | ^F find | ^G go to | ^D duplicate | ^E execute | ^C copy | ^X cut | ^V paste | ^Z undo | ^Y redo"; /// `set_status!` sets a formatted status message for the editor. /// Example usage: `set_status!(editor, "{} written to {}", file_size, file_name)` @@ -122,6 +124,13 @@ pub struct Editor { orig_term_mode: Option, /// The copied buffer of a row copied_row: Vec, + /// The history buffer of latest deleted row + history_row: Vec, + /// The history buffer position of latest deleted tow + history_row_y: usize, + /// The history type if undo/redo have to delete or restore line + /// true if restores line | false if deletes line + history_type: bool } /// Describes a status message, shown at the bottom at the screen. @@ -427,12 +436,29 @@ impl Editor { } else { self.rows.insert(self.cursor.y + 1, Row::new(self.copied_row.clone())); } + self.history_row_y = self.cursor.y + 1; + self.history_type = false; self.update_row(self.cursor.y + usize::from(self.cursor.y + 1 != self.rows.len()), false); (self.cursor.y, self.dirty) = (self.cursor.y + 1, true); // The line number has changed self.update_screen_cols(); } + fn undo(&mut self) { + if self.history_type == true { + self.n_bytes += self.history_row.len() as u64; + self.rows.insert(self.history_row_y, Row::new(self.history_row.clone())); + self.update_row(self.history_row_y, false); + (self.cursor.y, self.dirty) = (self.history_row_y, true); + self.history_type = false; + // The line number has changed + self.update_screen_cols(); + } else { + self.cursor.y = self.history_row_y; + self.delete_current_row(); + } + } + /// Try to load a file. If found, load the rows and update the render and syntax highlighting. /// If not found, do not return an error. fn load(&mut self, path: &Path) -> Result<(), Error> { @@ -613,7 +639,12 @@ impl Editor { Key::End => self.cursor.x = self.current_row().map_or(0, |row| row.chars.len()), Key::Char(b'\r' | b'\n') => self.insert_new_line(), // Enter Key::Char(BACKSPACE | DELETE_BIS) => self.delete_char(), // Backspace or Ctrl + H - Key::Char(REMOVE_LINE) => self.delete_current_row(), + Key::Char(REMOVE_LINE) => { + self.history_row_y = self.cursor.y; + self.history_row = self.rows[self.cursor.y].chars.clone(); + self.history_type = true; + self.delete_current_row(); + } Key::Delete => { self.move_cursor(&AKey::Right); self.delete_char(); @@ -640,12 +671,16 @@ impl Editor { Key::Char(GOTO) => prompt_mode = Some(PromptMode::GoTo(String::new())), Key::Char(DUPLICATE) => self.duplicate_current_row(), Key::Char(CUT) => { + self.history_row_y = self.cursor.y; + self.history_row = self.rows[self.cursor.y].chars.clone(); + self.history_type = true; self.copy_current_row(); self.delete_current_row(); } Key::Char(COPY) => self.copy_current_row(), Key::Char(PASTE) => self.paste_current_row(), Key::Char(EXECUTE) => prompt_mode = Some(PromptMode::Execute(String::new())), + Key::Char(UNDO) => self.undo(), Key::Char(c) => self.insert_byte(*c), } self.quit_times = quit_times; From 5eb8a0ea5ddb98b119f5fe74d081bb279854122b Mon Sep 17 00:00:00 2001 From: Carol Date: Tue, 10 Jan 2023 16:02:40 +0200 Subject: [PATCH 2/4] Add REDO (CTRL+Y) functionality for last cut, remove line and paste --- src/editor.rs | 72 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 4537d094..baecf30d 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -128,9 +128,11 @@ pub struct Editor { history_row: Vec, /// The history buffer position of latest deleted tow history_row_y: usize, - /// The history type if undo/redo have to delete or restore line + /// If undo/redo have to delete or restore line /// true if restores line | false if deletes line - history_type: bool + history_restore: bool, + /// If redo is available (when exists a previous undo) + redo_availability: bool, } /// Describes a status message, shown at the bottom at the screen. @@ -436,8 +438,6 @@ impl Editor { } else { self.rows.insert(self.cursor.y + 1, Row::new(self.copied_row.clone())); } - self.history_row_y = self.cursor.y + 1; - self.history_type = false; self.update_row(self.cursor.y + usize::from(self.cursor.y + 1 != self.rows.len()), false); (self.cursor.y, self.dirty) = (self.cursor.y + 1, true); // The line number has changed @@ -445,17 +445,41 @@ impl Editor { } fn undo(&mut self) { - if self.history_type == true { - self.n_bytes += self.history_row.len() as u64; - self.rows.insert(self.history_row_y, Row::new(self.history_row.clone())); - self.update_row(self.history_row_y, false); - (self.cursor.y, self.dirty) = (self.history_row_y, true); - self.history_type = false; - // The line number has changed - self.update_screen_cols(); - } else { - self.cursor.y = self.history_row_y; - self.delete_current_row(); + if self.history_row_y != usize::MAX { + if !self.redo_availability { + if self.history_restore { + self.n_bytes += self.history_row.len() as u64; + self.rows.insert(self.history_row_y, Row::new(self.history_row.clone())); + self.update_row(self.history_row_y, false); + (self.cursor.y, self.dirty) = (self.history_row_y, true); + // The line number has changed + self.update_screen_cols(); + } else { + self.history_row = self.rows[self.history_row_y].chars.clone(); + self.cursor.y = self.history_row_y; + self.delete_current_row(); + } + } + self.redo_availability = true; + } + } + + fn redo(&mut self) { + if self.history_row_y != usize::MAX { + if self.redo_availability { + if self.history_restore { + self.cursor.y = self.history_row_y; + self.delete_current_row(); + } else { + self.n_bytes += self.history_row.len() as u64; + self.rows.insert(self.history_row_y, Row::new(self.history_row.clone())); + self.update_row(self.history_row_y, false); + (self.cursor.y, self.dirty) = (self.history_row_y, true); + // The line number has changed + self.update_screen_cols(); + } + } + self.redo_availability = false; } } @@ -642,7 +666,7 @@ impl Editor { Key::Char(REMOVE_LINE) => { self.history_row_y = self.cursor.y; self.history_row = self.rows[self.cursor.y].chars.clone(); - self.history_type = true; + self.history_restore = true; self.delete_current_row(); } Key::Delete => { @@ -673,15 +697,25 @@ impl Editor { Key::Char(CUT) => { self.history_row_y = self.cursor.y; self.history_row = self.rows[self.cursor.y].chars.clone(); - self.history_type = true; + self.history_restore = true; + self.redo_availability = false; self.copy_current_row(); self.delete_current_row(); } Key::Char(COPY) => self.copy_current_row(), - Key::Char(PASTE) => self.paste_current_row(), + Key::Char(PASTE) => { + self.history_row_y = self.cursor.y + 1; + self.history_restore = false; + self.redo_availability = false; + self.paste_current_row(); + } Key::Char(EXECUTE) => prompt_mode = Some(PromptMode::Execute(String::new())), Key::Char(UNDO) => self.undo(), - Key::Char(c) => self.insert_byte(*c), + Key::Char(REDO) => self.redo(), + Key::Char(c) => { + self.history_row_y = usize::MAX; // resets undo and redo option + self.insert_byte(*c); + } } self.quit_times = quit_times; (false, prompt_mode) From 5f1f83c5de8b179576b6c3a2be82151f4a04c453 Mon Sep 17 00:00:00 2001 From: Carol Date: Tue, 10 Jan 2023 16:04:06 +0200 Subject: [PATCH 3/4] Modify README.md to reflect made changes --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0e506408..5ef553af 100644 --- a/README.md +++ b/README.md @@ -201,6 +201,8 @@ kibi --version # Print version information and exit | Ctrl-C | Copies the entire line | | Ctrl-X | Cuts the entire line | | Ctrl-V | Will paste the copied line | +| Ctrl-Z | Will undo the last remove line, cut or paste | +| Ctrl-Y | Will redo the last undo | ### Configuration From 886dc3b07b2d6df4e58561300803a12a44a0221c Mon Sep 17 00:00:00 2001 From: Carol Date: Tue, 10 Jan 2023 16:22:19 +0200 Subject: [PATCH 4/4] Fixed bug causing undo / redo not working when pasting text --- src/editor.rs | 58 +++++++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index baecf30d..92640a1e 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -445,42 +445,38 @@ impl Editor { } fn undo(&mut self) { - if self.history_row_y != usize::MAX { - if !self.redo_availability { - if self.history_restore { - self.n_bytes += self.history_row.len() as u64; - self.rows.insert(self.history_row_y, Row::new(self.history_row.clone())); - self.update_row(self.history_row_y, false); - (self.cursor.y, self.dirty) = (self.history_row_y, true); - // The line number has changed - self.update_screen_cols(); - } else { - self.history_row = self.rows[self.history_row_y].chars.clone(); - self.cursor.y = self.history_row_y; - self.delete_current_row(); - } + if !self.redo_availability { + if self.history_restore == true { + self.n_bytes += self.history_row.len() as u64; + self.rows.insert(self.history_row_y, Row::new(self.history_row.clone())); + self.update_row(self.history_row_y, false); + (self.cursor.y, self.dirty) = (self.history_row_y, true); + // The line number has changed + self.update_screen_cols(); + } else { + self.history_row = self.rows[self.history_row_y].chars.clone(); + self.cursor.y = self.history_row_y; + self.delete_current_row(); } self.redo_availability = true; } } fn redo(&mut self) { - if self.history_row_y != usize::MAX { - if self.redo_availability { - if self.history_restore { - self.cursor.y = self.history_row_y; - self.delete_current_row(); - } else { - self.n_bytes += self.history_row.len() as u64; - self.rows.insert(self.history_row_y, Row::new(self.history_row.clone())); - self.update_row(self.history_row_y, false); - (self.cursor.y, self.dirty) = (self.history_row_y, true); - // The line number has changed - self.update_screen_cols(); - } + if self.redo_availability { + if self.history_restore { + self.cursor.y = self.history_row_y; + self.delete_current_row(); + } else { + self.n_bytes += self.history_row.len() as u64; + self.rows.insert(self.history_row_y, Row::new(self.history_row.clone())); + self.update_row(self.history_row_y, false); + (self.cursor.y, self.dirty) = (self.history_row_y, true); + // The line number has changed + self.update_screen_cols(); } - self.redo_availability = false; } + self.redo_availability = false; } /// Try to load a file. If found, load the rows and update the render and syntax highlighting. @@ -667,6 +663,7 @@ impl Editor { self.history_row_y = self.cursor.y; self.history_row = self.rows[self.cursor.y].chars.clone(); self.history_restore = true; + self.redo_availability = false; self.delete_current_row(); } Key::Delete => { @@ -712,10 +709,7 @@ impl Editor { Key::Char(EXECUTE) => prompt_mode = Some(PromptMode::Execute(String::new())), Key::Char(UNDO) => self.undo(), Key::Char(REDO) => self.redo(), - Key::Char(c) => { - self.history_row_y = usize::MAX; // resets undo and redo option - self.insert_byte(*c); - } + Key::Char(c) => self.insert_byte(*c), } self.quit_times = quit_times; (false, prompt_mode)