From 4c593d764bd53793a364f5779fcdbc37d4c727de Mon Sep 17 00:00:00 2001 From: Tim Oram Date: Fri, 21 Jul 2023 11:10:00 -0230 Subject: [PATCH] Add Operation::Load to History Add to the todo_file History a load operation tracker, that will acts as a sentinel value for the start of the undo history queue. This will enable hooking into the start of the history queue. In order to expose the additional information, the Operation is now returned from the History undo/redo methods. --- src/todo_file/src/history/history_item.rs | 9 ++ src/todo_file/src/history/mod.rs | 29 ++-- src/todo_file/src/history/operation.rs | 1 + src/todo_file/src/history/tests.rs | 160 ++++++++++++---------- src/todo_file/src/lib.rs | 59 +++++++- 5 files changed, 174 insertions(+), 84 deletions(-) diff --git a/src/todo_file/src/history/history_item.rs b/src/todo_file/src/history/history_item.rs index b0014c4b3..9ea89dc04 100644 --- a/src/todo_file/src/history/history_item.rs +++ b/src/todo_file/src/history/history_item.rs @@ -9,6 +9,15 @@ pub(crate) struct HistoryItem { } impl HistoryItem { + pub(crate) const fn new_load() -> Self { + Self { + operation: Operation::Load, + start_index: 0, + end_index: 0, + lines: vec![], + } + } + pub(crate) fn new_modify(start_index: usize, end_index: usize, lines: Vec) -> Self { Self { operation: Operation::Modify, diff --git a/src/todo_file/src/history/mod.rs b/src/todo_file/src/history/mod.rs index 68074b7cf..2020d8593 100644 --- a/src/todo_file/src/history/mod.rs +++ b/src/todo_file/src/history/mod.rs @@ -23,13 +23,14 @@ impl History { pub(crate) fn new(limit: u32) -> Self { Self { redo_history: VecDeque::new(), - undo_history: VecDeque::new(), + undo_history: VecDeque::from([HistoryItem::new_load()]), limit: limit.try_into().expect("History limit is too large"), } } pub(crate) fn apply_operation(lines: &mut Vec, operation: &HistoryItem) -> HistoryItem { match operation.operation { + Operation::Load => HistoryItem::new_load(), Operation::Modify => { let range = if operation.end_index <= operation.start_index { operation.end_index..=operation.start_index @@ -73,32 +74,40 @@ impl History { } } - pub(crate) fn undo(&mut self, current: &mut Vec) -> Option<(usize, usize)> { - self.undo_history.pop_back().map(|operation| { - let history = Self::apply_operation(current, &operation); + pub(crate) fn undo(&mut self, current: &mut Vec) -> Option<(Operation, usize, usize)> { + self.undo_history.pop_back().map(|history_item| { + let history = Self::apply_operation(current, &history_item); + // do not remove load operation from undo history, as it acts as a sentinel value + if history.operation == Operation::Load { + self.undo_history.push_back(history_item); + return (history.operation, history.start_index, history.end_index); + } let update_range = Self::get_last_index_range(&history, current.len()); self.redo_history.push_back(history); - update_range + (history_item.operation, update_range.0, update_range.1) }) } - pub(crate) fn redo(&mut self, current: &mut Vec) -> Option<(usize, usize)> { - self.redo_history.pop_back().map(|operation| { - let history = Self::apply_operation(current, &operation); + pub(crate) fn redo(&mut self, current: &mut Vec) -> Option<(Operation, usize, usize)> { + self.redo_history.pop_back().map(|history_item| { + // the load operation is not handled here, as it is in `undo` because the load operation should never be + // added to the redo history + let history = Self::apply_operation(current, &history_item); let update_range = Self::get_last_index_range(&history, current.len()); self.undo_history.push_back(history); - update_range + (history_item.operation, update_range.0, update_range.1) }) } pub(crate) fn reset(&mut self) { self.undo_history.clear(); + self.undo_history.push_back(HistoryItem::new_load()); self.redo_history.clear(); } fn get_last_index_range(history_item: &HistoryItem, list_length: usize) -> (usize, usize) { match history_item.operation { - Operation::Add | Operation::Modify => (history_item.start_index, history_item.end_index), + Operation::Add | Operation::Modify | Operation::Load => (history_item.start_index, history_item.end_index), Operation::Remove => { let index = min(history_item.start_index, history_item.end_index); if index == 0 || list_length == 0 { diff --git a/src/todo_file/src/history/operation.rs b/src/todo_file/src/history/operation.rs index bf7575f5d..0ec048d57 100644 --- a/src/todo_file/src/history/operation.rs +++ b/src/todo_file/src/history/operation.rs @@ -1,5 +1,6 @@ #[derive(Debug, PartialEq, Eq)] pub(crate) enum Operation { + Load, Modify, SwapUp, SwapDown, diff --git a/src/todo_file/src/history/tests.rs b/src/todo_file/src/history/tests.rs index 2f4bbed59..442bf5f6c 100644 --- a/src/todo_file/src/history/tests.rs +++ b/src/todo_file/src/history/tests.rs @@ -18,7 +18,11 @@ fn history_item_to_string(item: &HistoryItem) -> String { } fn _assert_history_items(actual: &[HistoryItem], expected: &[HistoryItem]) { - let actual_strings: Vec = actual.iter().map(history_item_to_string).collect(); + let actual_strings: Vec = actual + .iter() + .filter(|item| item.operation != Operation::Load) + .map(history_item_to_string) + .collect(); let expected_strings: Vec = expected.iter().map(history_item_to_string).collect(); pretty_assertions::assert_str_eq!(actual_strings.join("\n"), expected_strings.join("\n")); } @@ -52,9 +56,10 @@ macro_rules! assert_todo_lines { #[test] fn new() { - let history = History::new(100); + let mut history = History::new(100); assert_eq!(history.limit, 100); - assert_empty!(history.undo_history); + assert_eq!(history.undo_history.len(), 1); + assert_some_eq!(history.undo_history.pop_back(), HistoryItem::new_load()); assert_empty!(history.redo_history); } @@ -83,14 +88,29 @@ fn record_history_overflow_limit() { assert_empty!(history.redo_history); } +#[test] +fn undo_at_load() { + let mut history = History::new(10); + let mut lines = create_lines(); + assert_some_eq!(history.undo(&mut lines), (Operation::Load, 0, 0)); + assert_todo_lines!( + lines, + "pick aaa c1", + "pick bbb c2", + "pick ccc c3", + "pick ddd c4", + "pick eee c5" + ); + assert_some_eq!(history.undo_history.pop_back(), HistoryItem::new_load()); +} #[test] fn undo_redo_add_start() { let mut history = History::new(10); history.record(HistoryItem::new_add(0, 0)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (0, 0)); + assert_some_eq!(history.undo(&mut lines), (Operation::Add, 0, 0)); assert_todo_lines!(lines, "pick bbb c2", "pick ccc c3", "pick ddd c4", "pick eee c5"); - assert_some_eq!(history.redo(&mut lines), (0, 0)); + assert_some_eq!(history.redo(&mut lines), (Operation::Remove, 0, 0)); assert_todo_lines!( lines, "pick aaa c1", @@ -106,9 +126,9 @@ fn undo_redo_add_end() { let mut history = History::new(10); history.record(HistoryItem::new_add(4, 4)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (3, 3)); + assert_some_eq!(history.undo(&mut lines), (Operation::Add, 3, 3)); assert_todo_lines!(lines, "pick aaa c1", "pick bbb c2", "pick ccc c3", "pick ddd c4"); - assert_some_eq!(history.redo(&mut lines), (4, 4)); + assert_some_eq!(history.redo(&mut lines), (Operation::Remove, 4, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -118,15 +138,14 @@ fn undo_redo_add_end() { "pick eee c5" ); } - #[test] fn undo_redo_add_middle() { let mut history = History::new(10); history.record(HistoryItem::new_add(2, 2)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::Add, 2, 2)); assert_todo_lines!(lines, "pick aaa c1", "pick bbb c2", "pick ddd c4", "pick eee c5"); - assert_some_eq!(history.redo(&mut lines), (2, 2)); + assert_some_eq!(history.redo(&mut lines), (Operation::Remove, 2, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -136,15 +155,14 @@ fn undo_redo_add_middle() { "pick eee c5" ); } - #[test] fn undo_redo_add_range_start_index_at_top() { let mut history = History::new(10); history.record(HistoryItem::new_add(0, 1)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (0, 0)); + assert_some_eq!(history.undo(&mut lines), (Operation::Add, 0, 0)); assert_todo_lines!(lines, "pick ccc c3", "pick ddd c4", "pick eee c5"); - assert_some_eq!(history.redo(&mut lines), (0, 1)); + assert_some_eq!(history.redo(&mut lines), (Operation::Remove, 0, 1)); assert_todo_lines!( lines, "pick aaa c1", @@ -160,9 +178,9 @@ fn undo_redo_add_range_end_index_at_top() { let mut history = History::new(10); history.record(HistoryItem::new_add(1, 0)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (0, 0)); + assert_some_eq!(history.undo(&mut lines), (Operation::Add, 0, 0)); assert_todo_lines!(lines, "pick ccc c3", "pick ddd c4", "pick eee c5"); - assert_some_eq!(history.redo(&mut lines), (1, 0)); + assert_some_eq!(history.redo(&mut lines), (Operation::Remove, 1, 0)); assert_todo_lines!( lines, "pick aaa c1", @@ -178,9 +196,9 @@ fn undo_redo_add_range_start_index_at_bottom() { let mut history = History::new(10); history.record(HistoryItem::new_add(4, 3)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::Add, 2, 2)); assert_todo_lines!(lines, "pick aaa c1", "pick bbb c2", "pick ccc c3"); - assert_some_eq!(history.redo(&mut lines), (4, 3)); + assert_some_eq!(history.redo(&mut lines), (Operation::Remove, 4, 3)); assert_todo_lines!( lines, "pick aaa c1", @@ -196,9 +214,9 @@ fn undo_redo_add_range_end_index_at_bottom() { let mut history = History::new(10); history.record(HistoryItem::new_add(3, 4)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::Add, 2, 2)); assert_todo_lines!(lines, "pick aaa c1", "pick bbb c2", "pick ccc c3"); - assert_some_eq!(history.redo(&mut lines), (3, 4)); + assert_some_eq!(history.redo(&mut lines), (Operation::Remove, 3, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -208,13 +226,12 @@ fn undo_redo_add_range_end_index_at_bottom() { "pick eee c5" ); } - #[test] fn undo_redo_remove_start() { let mut history = History::new(10); history.record(HistoryItem::new_remove(0, 0, vec![Line::new("drop xxx cx").unwrap()])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (0, 0)); + assert_some_eq!(history.undo(&mut lines), (Operation::Remove, 0, 0)); assert_todo_lines!( lines, "drop xxx cx", @@ -224,7 +241,7 @@ fn undo_redo_remove_start() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (0, 0)); + assert_some_eq!(history.redo(&mut lines), (Operation::Add, 0, 0)); assert_todo_lines!( lines, "pick aaa c1", @@ -240,7 +257,7 @@ fn undo_redo_remove_end() { let mut history = History::new(10); history.record(HistoryItem::new_remove(5, 5, vec![Line::new("drop xxx cx").unwrap()])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (5, 5)); + assert_some_eq!(history.undo(&mut lines), (Operation::Remove, 5, 5)); assert_todo_lines!( lines, "pick aaa c1", @@ -250,7 +267,7 @@ fn undo_redo_remove_end() { "pick eee c5", "drop xxx cx" ); - assert_some_eq!(history.redo(&mut lines), (4, 4)); + assert_some_eq!(history.redo(&mut lines), (Operation::Add, 4, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -266,7 +283,7 @@ fn undo_redo_remove_middle() { let mut history = History::new(10); history.record(HistoryItem::new_remove(2, 2, vec![Line::new("drop xxx cx").unwrap()])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::Remove, 2, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -276,7 +293,7 @@ fn undo_redo_remove_middle() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (2, 2)); + assert_some_eq!(history.redo(&mut lines), (Operation::Add, 2, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -295,7 +312,7 @@ fn undo_redo_remove_range_start_index_top() { Line::new("drop yyy cy").unwrap(), ])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (0, 1)); + assert_some_eq!(history.undo(&mut lines), (Operation::Remove, 0, 1)); assert_todo_lines!( lines, "drop xxx cx", @@ -306,7 +323,7 @@ fn undo_redo_remove_range_start_index_top() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (0, 0)); + assert_some_eq!(history.redo(&mut lines), (Operation::Add, 0, 0)); assert_todo_lines!( lines, "pick aaa c1", @@ -325,7 +342,7 @@ fn undo_redo_remove_range_start_index_bottom() { Line::new("drop yyy cy").unwrap(), ])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (6, 5)); + assert_some_eq!(history.undo(&mut lines), (Operation::Remove, 6, 5)); assert_todo_lines!( lines, "pick aaa c1", @@ -336,7 +353,7 @@ fn undo_redo_remove_range_start_index_bottom() { "drop xxx cx", "drop yyy cy" ); - assert_some_eq!(history.redo(&mut lines), (4, 4)); + assert_some_eq!(history.redo(&mut lines), (Operation::Add, 4, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -355,7 +372,7 @@ fn undo_redo_remove_range_end_index_top() { Line::new("drop yyy cy").unwrap(), ])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (1, 0)); + assert_some_eq!(history.undo(&mut lines), (Operation::Remove, 1, 0)); assert_todo_lines!( lines, "drop xxx cx", @@ -366,7 +383,7 @@ fn undo_redo_remove_range_end_index_top() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (0, 0)); + assert_some_eq!(history.redo(&mut lines), (Operation::Add, 0, 0)); assert_todo_lines!( lines, "pick aaa c1", @@ -385,7 +402,7 @@ fn undo_redo_remove_range_end_index_bottom() { Line::new("drop yyy cy").unwrap(), ])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (5, 6)); + assert_some_eq!(history.undo(&mut lines), (Operation::Remove, 5, 6)); assert_todo_lines!( lines, "pick aaa c1", @@ -396,7 +413,7 @@ fn undo_redo_remove_range_end_index_bottom() { "drop xxx cx", "drop yyy cy" ); - assert_some_eq!(history.redo(&mut lines), (4, 4)); + assert_some_eq!(history.redo(&mut lines), (Operation::Add, 4, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -412,7 +429,7 @@ fn undo_redo_swap_up_single_index_start() { let mut history = History::new(10); history.record(HistoryItem::new_swap_up(1, 1)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (1, 1)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapUp, 1, 1)); assert_todo_lines!( lines, "pick bbb c2", @@ -421,7 +438,7 @@ fn undo_redo_swap_up_single_index_start() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (0, 0)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapDown, 0, 0)); assert_todo_lines!( lines, "pick aaa c1", @@ -437,7 +454,7 @@ fn undo_redo_swap_up_single_index_end() { let mut history = History::new(10); history.record(HistoryItem::new_swap_up(4, 4)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (4, 4)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapUp, 4, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -446,7 +463,7 @@ fn undo_redo_swap_up_single_index_end() { "pick eee c5", "pick ddd c4" ); - assert_some_eq!(history.redo(&mut lines), (3, 3)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapDown, 3, 3)); assert_todo_lines!( lines, "pick aaa c1", @@ -462,7 +479,7 @@ fn undo_redo_swap_up_single_index_middle() { let mut history = History::new(10); history.record(HistoryItem::new_swap_up(2, 2)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapUp, 2, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -471,7 +488,7 @@ fn undo_redo_swap_up_single_index_middle() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (1, 1)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapDown, 1, 1)); assert_todo_lines!( lines, "pick aaa c1", @@ -487,7 +504,7 @@ fn undo_redo_swap_up_range_down_index_start() { let mut history = History::new(10); history.record(HistoryItem::new_swap_up(1, 2)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (1, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapUp, 1, 2)); assert_todo_lines!( lines, "pick ccc c3", @@ -496,7 +513,7 @@ fn undo_redo_swap_up_range_down_index_start() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (0, 1)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapDown, 0, 1)); assert_todo_lines!( lines, "pick aaa c1", @@ -512,7 +529,7 @@ fn undo_redo_swap_up_range_down_index_end() { let mut history = History::new(10); history.record(HistoryItem::new_swap_up(3, 4)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (3, 4)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapUp, 3, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -521,7 +538,7 @@ fn undo_redo_swap_up_range_down_index_end() { "pick ccc c3", "pick ddd c4" ); - assert_some_eq!(history.redo(&mut lines), (2, 3)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapDown, 2, 3)); assert_todo_lines!( lines, "pick aaa c1", @@ -537,7 +554,7 @@ fn undo_redo_swap_up_range_up_index_start() { let mut history = History::new(10); history.record(HistoryItem::new_swap_up(2, 1)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 1)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapUp, 2, 1)); assert_todo_lines!( lines, "pick ccc c3", @@ -546,7 +563,7 @@ fn undo_redo_swap_up_range_up_index_start() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (1, 0)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapDown, 1, 0)); assert_todo_lines!( lines, "pick aaa c1", @@ -562,7 +579,7 @@ fn undo_redo_swap_up_range_up_index_end() { let mut history = History::new(10); history.record(HistoryItem::new_swap_up(4, 3)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (4, 3)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapUp, 4, 3)); assert_todo_lines!( lines, "pick aaa c1", @@ -571,7 +588,7 @@ fn undo_redo_swap_up_range_up_index_end() { "pick ccc c3", "pick ddd c4" ); - assert_some_eq!(history.redo(&mut lines), (3, 2)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapDown, 3, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -587,7 +604,7 @@ fn undo_redo_swap_down_range_down_index_start() { let mut history = History::new(10); history.record(HistoryItem::new_swap_down(0, 1)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (0, 1)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapDown, 0, 1)); assert_todo_lines!( lines, "pick bbb c2", @@ -596,7 +613,7 @@ fn undo_redo_swap_down_range_down_index_start() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (1, 2)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapUp, 1, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -612,7 +629,7 @@ fn undo_redo_swap_down_range_down_index_end() { let mut history = History::new(10); history.record(HistoryItem::new_swap_down(2, 3)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 3)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapDown, 2, 3)); assert_todo_lines!( lines, "pick aaa c1", @@ -621,7 +638,7 @@ fn undo_redo_swap_down_range_down_index_end() { "pick eee c5", "pick ccc c3" ); - assert_some_eq!(history.redo(&mut lines), (3, 4)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapUp, 3, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -637,7 +654,7 @@ fn undo_redo_swap_down_range_up_index_start() { let mut history = History::new(10); history.record(HistoryItem::new_swap_down(1, 0)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (1, 0)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapDown, 1, 0)); assert_todo_lines!( lines, "pick bbb c2", @@ -646,7 +663,7 @@ fn undo_redo_swap_down_range_up_index_start() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (2, 1)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapUp, 2, 1)); assert_todo_lines!( lines, "pick aaa c1", @@ -662,7 +679,7 @@ fn undo_redo_swap_down_range_up_index_end() { let mut history = History::new(10); history.record(HistoryItem::new_swap_down(3, 2)); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (3, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::SwapDown, 3, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -671,7 +688,7 @@ fn undo_redo_swap_down_range_up_index_end() { "pick eee c5", "pick ccc c3" ); - assert_some_eq!(history.redo(&mut lines), (4, 3)); + assert_some_eq!(history.redo(&mut lines), (Operation::SwapUp, 4, 3)); assert_todo_lines!( lines, "pick aaa c1", @@ -687,7 +704,7 @@ fn undo_redo_modify_single_index_start() { let mut history = History::new(10); history.record(HistoryItem::new_modify(0, 0, vec![Line::new("drop xxx cx").unwrap()])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (0, 0)); + assert_some_eq!(history.undo(&mut lines), (Operation::Modify, 0, 0)); assert_todo_lines!( lines, "drop xxx cx", @@ -696,7 +713,7 @@ fn undo_redo_modify_single_index_start() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (0, 0)); + assert_some_eq!(history.redo(&mut lines), (Operation::Modify, 0, 0)); assert_todo_lines!( lines, "pick aaa c1", @@ -712,7 +729,7 @@ fn undo_redo_modify_single_index_end() { let mut history = History::new(10); history.record(HistoryItem::new_modify(4, 4, vec![Line::new("drop xxx cx").unwrap()])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (4, 4)); + assert_some_eq!(history.undo(&mut lines), (Operation::Modify, 4, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -721,7 +738,7 @@ fn undo_redo_modify_single_index_end() { "pick ddd c4", "drop xxx cx" ); - assert_some_eq!(history.redo(&mut lines), (4, 4)); + assert_some_eq!(history.redo(&mut lines), (Operation::Modify, 4, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -737,7 +754,7 @@ fn undo_redo_modify_single_index_middle() { let mut history = History::new(10); history.record(HistoryItem::new_modify(2, 2, vec![Line::new("drop xxx cx").unwrap()])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::Modify, 2, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -746,7 +763,7 @@ fn undo_redo_modify_single_index_middle() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (2, 2)); + assert_some_eq!(history.redo(&mut lines), (Operation::Modify, 2, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -766,7 +783,7 @@ fn undo_redo_modify_range_down_index_start() { Line::new("drop xx3 c3").unwrap(), ])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (0, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::Modify, 0, 2)); assert_todo_lines!( lines, "drop xx1 c1", @@ -775,7 +792,7 @@ fn undo_redo_modify_range_down_index_start() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (0, 2)); + assert_some_eq!(history.redo(&mut lines), (Operation::Modify, 0, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -795,7 +812,7 @@ fn undo_redo_modify_range_down_index_end() { Line::new("drop xx3 c3").unwrap(), ])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 4)); + assert_some_eq!(history.undo(&mut lines), (Operation::Modify, 2, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -804,7 +821,7 @@ fn undo_redo_modify_range_down_index_end() { "drop xx2 c2", "drop xx3 c3" ); - assert_some_eq!(history.redo(&mut lines), (2, 4)); + assert_some_eq!(history.redo(&mut lines), (Operation::Modify, 2, 4)); assert_todo_lines!( lines, "pick aaa c1", @@ -824,7 +841,7 @@ fn undo_redo_modify_range_up_index_start() { Line::new("drop xx3 c3").unwrap(), ])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (2, 0)); + assert_some_eq!(history.undo(&mut lines), (Operation::Modify, 2, 0)); assert_todo_lines!( lines, "drop xx1 c1", @@ -833,7 +850,7 @@ fn undo_redo_modify_range_up_index_start() { "pick ddd c4", "pick eee c5" ); - assert_some_eq!(history.redo(&mut lines), (2, 0)); + assert_some_eq!(history.redo(&mut lines), (Operation::Modify, 2, 0)); assert_todo_lines!( lines, "pick aaa c1", @@ -853,7 +870,7 @@ fn undo_redo_modify_range_up_index_end() { Line::new("drop xx3 c3").unwrap(), ])); let mut lines = create_lines(); - assert_some_eq!(history.undo(&mut lines), (4, 2)); + assert_some_eq!(history.undo(&mut lines), (Operation::Modify, 4, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -862,7 +879,7 @@ fn undo_redo_modify_range_up_index_end() { "drop xx2 c2", "drop xx3 c3" ); - assert_some_eq!(history.redo(&mut lines), (4, 2)); + assert_some_eq!(history.redo(&mut lines), (Operation::Modify, 4, 2)); assert_todo_lines!( lines, "pick aaa c1", @@ -879,6 +896,7 @@ fn reset() { history.redo_history.push_front(HistoryItem::new_add(1, 1)); history.undo_history.push_front(HistoryItem::new_add(1, 1)); history.reset(); - assert_empty!(history.undo_history); + assert_eq!(history.undo_history.len(), 1); + assert_some_eq!(history.undo_history.pop_back(), HistoryItem::new_load()); assert_empty!(history.redo_history); } diff --git a/src/todo_file/src/lib.rs b/src/todo_file/src/lib.rs index d249ec86e..77fbd87fc 100644 --- a/src/todo_file/src/lib.rs +++ b/src/todo_file/src/lib.rs @@ -154,7 +154,10 @@ use self::{ history::{History, HistoryItem}, utils::{remove_range, swap_range_down, swap_range_up}, }; -use crate::errors::{FileReadErrorCause, IoError}; +use crate::{ + errors::{FileReadErrorCause, IoError}, + history::Operation, +}; /// Represents a rebase file. #[derive(Debug)] @@ -391,14 +394,22 @@ impl TodoFile { #[inline] pub fn undo(&mut self) -> Option<(usize, usize)> { self.version.increment(); - self.history.undo(&mut self.lines) + if let Some((operation, start, end)) = self.history.undo(&mut self.lines) { + return if operation == Operation::Load { + None + } + else { + Some((start, end)) + }; + } + None } /// Redo the last undone modification. #[inline] pub fn redo(&mut self) -> Option<(usize, usize)> { self.version.increment(); - self.history.redo(&mut self.lines) + self.history.redo(&mut self.lines).map(|(_, start, end)| (start, end)) } /// Get the current version @@ -749,6 +760,32 @@ mod tests { assert_todo_lines!(todo_file, "reword aaa comment", "reword bbb comment"); } + #[test] + fn undo_load_operation() { + let (mut todo_file, _) = + create_and_load_todo_file(&["pick aaa comment", "drop bbb comment", "edit ccc comment"]); + assert_none!(todo_file.undo()); + } + + #[test] + fn undo_empty_history() { + let (mut todo_file, _) = + create_and_load_todo_file(&["pick aaa comment", "drop bbb comment", "edit ccc comment"]); + // set short history, to remove load entry + todo_file.history = History::new(1); + todo_file.update_range(0, 0, &EditContext::new().action(Action::Drop)); + _ = todo_file.undo(); // remove Drop operation + assert_none!(todo_file.undo()); + } + + #[test] + fn undo_operation() { + let (mut todo_file, _) = + create_and_load_todo_file(&["pick aaa comment", "drop bbb comment", "edit ccc comment"]); + todo_file.update_range(0, 1, &EditContext::new().action(Action::Drop)); + assert_some_eq!(todo_file.undo(), (0, 1)); + } + #[test] fn history_undo_redo() { let (mut todo_file, _) = @@ -764,6 +801,22 @@ mod tests { assert_ne!(todo_file.version(), &old_version); } + #[test] + fn redo_empty_history() { + let (mut todo_file, _) = + create_and_load_todo_file(&["pick aaa comment", "drop bbb comment", "edit ccc comment"]); + assert_none!(todo_file.redo()); + } + + #[test] + fn redo_operation() { + let (mut todo_file, _) = + create_and_load_todo_file(&["pick aaa comment", "drop bbb comment", "edit ccc comment"]); + todo_file.update_range(0, 1, &EditContext::new().action(Action::Drop)); + _ = todo_file.undo(); + assert_some_eq!(todo_file.redo(), (0, 1)); + } + #[test] fn swap_up() { let (mut todo_file, _) =