history: Add unique mode to keep unique item in History. (#529)

This commit is contained in:
Jason Lee 2025-01-06 19:19:36 +08:00 committed by GitHub
parent 99e51288e0
commit e012eb6d07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 65 additions and 3 deletions

View file

@ -28,7 +28,7 @@ const MINIMUM_SIZE: Size<Pixels> = size(px(100.), px(100.));
const DRAG_BAR_HEIGHT: Pixels = px(30.);
const HANDLE_SIZE: Pixels = px(20.0);
#[derive(Clone, Debug)]
#[derive(Clone, PartialEq, Debug)]
struct TileChange {
tile_id: EntityId,
old_bounds: Option<Bounds<Pixels>>,

View file

@ -3,7 +3,7 @@ use std::{
time::{Duration, Instant},
};
pub trait HistoryItem: Clone {
pub trait HistoryItem: Clone + PartialEq {
fn version(&self) -> usize;
fn set_version(&mut self, version: usize);
}
@ -22,6 +22,7 @@ pub struct History<I: HistoryItem> {
pub(crate) ignore: bool,
max_undo: usize,
group_interval: Option<Duration>,
unique: bool,
}
impl<I> History<I>
@ -37,6 +38,7 @@ where
version: 0,
max_undo: 1000,
group_interval: None,
unique: false,
}
}
@ -46,6 +48,13 @@ where
self
}
/// Set the history to be unique, defaults to false.
/// If set to true, the history will only keep unique changes.
pub fn unique(mut self) -> Self {
self.unique = true;
self
}
/// Set the interval in milliseconds to group changes, defaults to None.
pub fn group_interval(mut self, group_interval: Duration) -> Self {
self.group_interval = Some(group_interval);
@ -75,6 +84,10 @@ where
self.undos.remove(0);
}
if self.unique {
self.undos.retain(|c| *c != item);
}
let mut item = item;
item.set_version(version);
self.undos.push(item);
@ -150,6 +163,12 @@ mod tests {
version: usize,
}
impl PartialEq for TabIndex {
fn eq(&self, other: &Self) -> bool {
self.tab_index == other.tab_index
}
}
impl From<usize> for TabIndex {
fn from(value: usize) -> Self {
TabIndex {
@ -210,4 +229,47 @@ mod tests {
assert_eq!(history.undo().is_none(), true);
}
#[test]
fn test_unique_history() {
let mut history: History<TabIndex> = History::new().max_undo(100).unique();
// Push some items
history.push(0.into());
history.push(1.into());
history.push(1.into()); // Duplicate, should be ignored
history.push(2.into());
history.push(1.into()); // Duplicate, should be remove old, and add new
// Check the version and undo stack
assert_eq!(history.version(), 5);
assert_eq!(history.undos().len(), 3);
assert_eq!(history.undos().last().unwrap().tab_index, 1);
// Undo the last change
let changes = history.undo().unwrap();
assert_eq!(changes.len(), 1);
assert_eq!(changes[0].tab_index, 1);
// Redo the last undone change
let changes = history.redo().unwrap();
assert_eq!(changes.len(), 1);
assert_eq!(changes[0].tab_index, 1);
// Push another item
history.push(3.into());
// Check the version and undo stack
assert_eq!(history.version(), 6);
assert_eq!(history.undos().len(), 4);
// Undo all changes
for _ in 0..4 {
history.undo();
}
// Check the undo stack is empty and redo stack has all changes
assert_eq!(history.undos().len(), 0);
assert_eq!(history.redos().len(), 4);
}
}

View file

@ -2,7 +2,7 @@ use std::{fmt::Debug, ops::Range};
use crate::history::HistoryItem;
#[derive(Debug, Clone)]
#[derive(Debug, PartialEq, Clone)]
pub struct Change {
pub(crate) old_range: Range<usize>,
pub(crate) old_text: String,