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, _) =