undo/redo
This commit is contained in:
+46
-1
@@ -1,4 +1,4 @@
|
||||
use std::{cmp::min, mem::swap};
|
||||
use std::{cmp::min, mem::{replace, swap}};
|
||||
|
||||
use crate::{BYTES_PER_LINE, app::{App, Mode, PartialAction}, edit_action::EditAction};
|
||||
|
||||
@@ -51,6 +51,9 @@ pub enum Action {
|
||||
ExtendLineAbove,
|
||||
|
||||
Delete,
|
||||
|
||||
Undo,
|
||||
Redo,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -103,6 +106,9 @@ impl App {
|
||||
Action::ExtendLineAbove => self.extend_line_above(),
|
||||
|
||||
Action::Delete => self.delete(),
|
||||
|
||||
Action::Undo => self.undo(),
|
||||
Action::Redo => self.redo(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,6 +357,45 @@ impl App {
|
||||
self.mode = Mode::Normal;
|
||||
}
|
||||
}
|
||||
|
||||
fn undo(&mut self) {
|
||||
if self.time_traveling == Some(0) { return }
|
||||
|
||||
let current_date = self.time_traveling
|
||||
.map_or(self.edit_history.len() - 1, |date| date - 1);
|
||||
|
||||
self.time_traveling = Some(current_date);
|
||||
|
||||
let edit_action = replace(
|
||||
&mut self.edit_history[current_date],
|
||||
EditAction::Placeholder
|
||||
);
|
||||
|
||||
self.undo_edit(&edit_action);
|
||||
|
||||
self.edit_history[current_date] = edit_action;
|
||||
}
|
||||
|
||||
fn redo(&mut self) {
|
||||
let Some(previous_date) = self.time_traveling else { return };
|
||||
|
||||
let current_date = previous_date + 1;
|
||||
|
||||
self.time_traveling = if current_date == self.edit_history.len() {
|
||||
None
|
||||
} else {
|
||||
Some(current_date)
|
||||
};
|
||||
|
||||
let edit_action = replace(
|
||||
&mut self.edit_history[previous_date],
|
||||
EditAction::Placeholder
|
||||
);
|
||||
|
||||
self.execute_edit(&edit_action);
|
||||
|
||||
self.edit_history[previous_date] = edit_action;
|
||||
}
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
+3
-1
@@ -23,7 +23,8 @@ pub struct App {
|
||||
pub partial_replace: Option<u8>,
|
||||
|
||||
pub edit_history: Vec<EditAction>,
|
||||
// some index to keep track of where we are? edit_prophecy?
|
||||
// the index *after* the latest edit action
|
||||
pub time_traveling: Option<usize>,
|
||||
|
||||
pub logs: Vec<String>,
|
||||
}
|
||||
@@ -102,6 +103,7 @@ impl App {
|
||||
partial_replace: None,
|
||||
|
||||
edit_history: Vec::new(),
|
||||
time_traveling: None,
|
||||
|
||||
logs: Vec::new(),
|
||||
}
|
||||
|
||||
@@ -133,6 +133,9 @@ impl Default for Config {
|
||||
("X".try_into().unwrap(), Action::ExtendLineAbove),
|
||||
|
||||
("d".try_into().unwrap(), Action::Delete),
|
||||
|
||||
("u".try_into().unwrap(), Action::Undo),
|
||||
("U".try_into().unwrap(), Action::Redo),
|
||||
].into()),
|
||||
(Some(PartialAction::Goto), [
|
||||
("j".try_into().unwrap(), Action::GotoLineStart),
|
||||
@@ -175,6 +178,9 @@ impl Default for Config {
|
||||
("X".try_into().unwrap(), Action::ExtendLineAbove),
|
||||
|
||||
("d".try_into().unwrap(), Action::Delete),
|
||||
|
||||
("u".try_into().unwrap(), Action::Undo),
|
||||
("U".try_into().unwrap(), Action::Redo),
|
||||
].into())
|
||||
].into())
|
||||
].into()
|
||||
|
||||
+53
-3
@@ -3,6 +3,12 @@ use crate::{app::App, cursor::Cursor};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EditAction {
|
||||
// this is a hacky workaround to allow disjoint borrows for
|
||||
// undoing/redoing edits. because the undo/redo operations need
|
||||
// to borrow an EditAction from edit_history, but should never touch
|
||||
// edit_history themselves, we can swap out the action in question,
|
||||
// then swap it back in once the undo/redo is done
|
||||
Placeholder,
|
||||
Delete {
|
||||
cursor: Cursor,
|
||||
old_data: Vec<u8>
|
||||
@@ -11,17 +17,31 @@ pub enum EditAction {
|
||||
cursor: Cursor,
|
||||
old_data: Vec<u8>,
|
||||
new_byte: u8
|
||||
}
|
||||
},
|
||||
// Insert {
|
||||
// cursor: Cursor,
|
||||
// which side of cursor? append/insert
|
||||
// new_data: Vec<u8>
|
||||
// }
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn execute_and_add(&mut self, edit_action: EditAction) {
|
||||
assert!(!matches!(edit_action, EditAction::Placeholder));
|
||||
|
||||
self.execute_edit(&edit_action);
|
||||
|
||||
if let Some(date) = self.time_traveling {
|
||||
self.edit_history.truncate(date);
|
||||
self.time_traveling = None;
|
||||
}
|
||||
|
||||
self.edit_history.push(edit_action);
|
||||
}
|
||||
|
||||
fn execute_edit(&mut self, edit_action: &EditAction) {
|
||||
pub fn execute_edit(&mut self, edit_action: &EditAction) {
|
||||
match edit_action {
|
||||
EditAction::Placeholder => unreachable!(),
|
||||
EditAction::Delete { cursor, .. } => self.delete_at(*cursor),
|
||||
EditAction::Replace {
|
||||
cursor, old_data: _, new_byte
|
||||
@@ -29,6 +49,16 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn undo_edit(&mut self, edit_action: &EditAction) {
|
||||
match edit_action {
|
||||
EditAction::Placeholder => unreachable!(),
|
||||
EditAction::Delete { cursor, old_data } => self.undo_delete_at(*cursor, old_data),
|
||||
EditAction::Replace {
|
||||
cursor, old_data, ..
|
||||
} => self.undo_replace_at_with(*cursor, old_data),
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_at(&mut self, cursor: Cursor) {
|
||||
self.contents.drain(cursor.range());
|
||||
|
||||
@@ -36,8 +66,28 @@ impl App {
|
||||
self.cursor.collapse();
|
||||
}
|
||||
|
||||
fn undo_delete_at(&mut self, cursor: Cursor, old_data: &[u8]) {
|
||||
let cursor_start = min(cursor.head, cursor.tail);
|
||||
|
||||
self.contents.splice(
|
||||
cursor_start..cursor_start,
|
||||
old_data.iter().copied()
|
||||
);
|
||||
|
||||
self.cursor = cursor;
|
||||
}
|
||||
|
||||
fn replace_at_with(&mut self, cursor: Cursor, new_byte: u8) {
|
||||
self.contents[self.cursor.range()].fill(new_byte);
|
||||
self.contents[cursor.range()].fill(new_byte);
|
||||
|
||||
self.cursor = cursor;
|
||||
}
|
||||
|
||||
fn undo_replace_at_with(&mut self, cursor: Cursor, old_data: &[u8]) {
|
||||
self.contents.splice(
|
||||
cursor.range(),
|
||||
old_data.iter().copied()
|
||||
);
|
||||
|
||||
self.cursor = cursor;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ const CHUNKS_PER_LINE: usize = BYTES_PER_LINE / BYTES_PER_CHUNK;
|
||||
// TODO:
|
||||
// - undo/redo
|
||||
// - modifications
|
||||
// - replace
|
||||
// - partial action(s)
|
||||
// - insert/append
|
||||
// - mode
|
||||
// - how this works with edit history is strange :/
|
||||
|
||||
Reference in New Issue
Block a user