From 16146e79e5a88dc84bb5d1317c26de120b023cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Sun, 9 Apr 2023 01:46:33 +0200 Subject: [PATCH 01/33] Use edit naming instead of action --- README.md | 14 ++-- examples/history.rs | 20 ++--- examples/record.rs | 14 ++-- src/any.rs | 32 ++++---- src/doctest.rs | 6 +- src/entry.rs | 32 ++++---- src/from_fn.rs | 18 ++--- src/history.rs | 138 ++++++++++++++++----------------- src/history/builder.rs | 2 +- src/history/checkpoint.rs | 30 ++++---- src/history/display.rs | 4 +- src/history/queue.rs | 34 ++++----- src/join.rs | 36 ++++----- src/lib.rs | 38 +++++----- src/record.rs | 156 +++++++++++++++++++------------------- src/record/builder.rs | 2 +- src/record/checkpoint.rs | 62 +++++++-------- src/record/display.rs | 4 +- src/record/queue.rs | 34 ++++----- src/socket.rs | 2 +- 20 files changed, 339 insertions(+), 339 deletions(-) diff --git a/README.md b/README.md index ed31cf3d..e1763979 100644 --- a/README.md +++ b/README.md @@ -9,20 +9,20 @@ It is an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), where all modifications are done by creating objects that applies the modifications. All objects knows how to undo the changes it applies, and by using the provided data -structures it is easy to apply, undo, and redo changes made to a target. +structures it is easy to undo and redo edits made to a target. ## Examples ```rust -use undo::{Action, Record}; +use undo::{Edit, Record}; struct Push(char); -impl Action for Push { +impl Edit for Push { type Target = String; type Output = (); - fn apply(&mut self, target: &mut String) { + fn edit(&mut self, target: &mut String) { target.push(self.0); } @@ -35,9 +35,9 @@ fn main() { let mut target = String::new(); let mut record = Record::new(); - record.apply(&mut target, Push('a')); - record.apply(&mut target, Push('b')); - record.apply(&mut target, Push('c')); + record.edit(&mut target, Push('a')); + record.edit(&mut target, Push('b')); + record.edit(&mut target, Push('c')); assert_eq!(target, "abc"); record.undo(&mut target); diff --git a/examples/history.rs b/examples/history.rs index e7944989..fb7beb00 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -1,13 +1,13 @@ use core::fmt::{self, Display, Formatter}; -use undo::{Action, History}; +use undo::{Edit, History}; struct Push(char); -impl Action for Push { +impl Edit for Push { type Target = String; type Output = (); - fn apply(&mut self, string: &mut String) { + fn edit(&mut self, string: &mut String) { string.push(self.0); } @@ -23,12 +23,12 @@ impl Display for Push { } fn main() { - let mut history = History::new(); let mut target = String::new(); + let mut history = History::new(); - history.apply(&mut target, Push('a')); - history.apply(&mut target, Push('b')); - history.apply(&mut target, Push('c')); + history.edit(&mut target, Push('a')); + history.edit(&mut target, Push('b')); + history.edit(&mut target, Push('c')); assert_eq!(target, "abc"); let abc_branch = history.branch(); @@ -37,9 +37,9 @@ fn main() { history.undo(&mut target); assert_eq!(target, "ab"); - history.apply(&mut target, Push('d')); - history.apply(&mut target, Push('e')); - history.apply(&mut target, Push('f')); + history.edit(&mut target, Push('d')); + history.edit(&mut target, Push('e')); + history.edit(&mut target, Push('f')); assert_eq!(target, "abdef"); let abdef_branch = history.branch(); diff --git a/examples/record.rs b/examples/record.rs index 6402bf50..20a6337e 100644 --- a/examples/record.rs +++ b/examples/record.rs @@ -1,13 +1,13 @@ use core::fmt::{self, Display, Formatter}; -use undo::{Action, Record}; +use undo::{Edit, Record}; struct Push(char); -impl Action for Push { +impl Edit for Push { type Target = String; type Output = (); - fn apply(&mut self, target: &mut String) { + fn edit(&mut self, target: &mut String) { target.push(self.0); } @@ -23,12 +23,12 @@ impl Display for Push { } fn main() { - let mut record = Record::new(); let mut target = String::new(); + let mut record = Record::new(); - record.apply(&mut target, Push('a')); - record.apply(&mut target, Push('b')); - record.apply(&mut target, Push('c')); + record.edit(&mut target, Push('a')); + record.edit(&mut target, Push('b')); + record.edit(&mut target, Push('c')); assert_eq!(target, "abc"); record.undo(&mut target); diff --git a/src/any.rs b/src/any.rs index 030f5840..3468429d 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,11 +1,11 @@ -use crate::Action; +use crate::Edit; use alloc::boxed::Box; use alloc::string::String; use core::fmt::{self, Debug, Display, Formatter}; -/// Any action type. +/// Any edit type. /// -/// This allows you to use multiple types of actions at the same time +/// This allows you to use multiple types of edits at the same time /// as long as they all share the same target and output type. /// /// # Examples @@ -16,49 +16,49 @@ use core::fmt::{self, Debug, Display, Formatter}; /// let mut target = String::new(); /// let mut record = Record::new(); /// -/// record.apply(&mut target, Any::new(Push('a'))); -/// record.apply(&mut target, Any::new(FromFn::new(|s: &mut String| s.push('b')))); +/// record.edit(&mut target, Any::new(Push('a'))); +/// record.edit(&mut target, Any::new(FromFn::new(|s: &mut String| s.push('b')))); /// assert_eq!(target, "ab"); /// # } /// ``` pub struct Any { - action: Box>, + edit: Box>, string: String, } impl Any { - /// Creates an `Any` from the provided action. - pub fn new(action: A) -> Any + /// Creates an `Any` from the provided edit. + pub fn new(edit: A) -> Any where - A: Action, + A: Edit, A: 'static, { Any { - action: Box::new(action), + edit: Box::new(edit), string: String::new(), } } - /// Sets the display message of this action. + /// Sets the display message of this edit. pub fn set_string(&mut self, str: impl Into) { self.string = str.into(); } } -impl Action for Any { +impl Edit for Any { type Target = T; type Output = O; - fn apply(&mut self, target: &mut Self::Target) -> Self::Output { - self.action.apply(target) + fn edit(&mut self, target: &mut Self::Target) -> Self::Output { + self.edit.edit(target) } fn undo(&mut self, target: &mut Self::Target) -> Self::Output { - self.action.undo(target) + self.edit.undo(target) } fn redo(&mut self, target: &mut Self::Target) -> Self::Output { - self.action.redo(target) + self.edit.redo(target) } } diff --git a/src/doctest.rs b/src/doctest.rs index e3c0de4f..d9923bfe 100644 --- a/src/doctest.rs +++ b/src/doctest.rs @@ -1,13 +1,13 @@ // This file is included in the documentation examples to avoid some boilerplate. -/// This is the action used in all the examples. +/// This is the edit used in all the examples. pub struct Push(char); -impl undo::Action for Push { +impl undo::Edit for Push { type Target = String; type Output = (); - fn apply(&mut self, string: &mut String) { + fn edit(&mut self, string: &mut String) { string.push(self.0); } diff --git a/src/entry.rs b/src/entry.rs index 309d2392..94016f80 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,58 +1,58 @@ -use crate::Action; +use crate::Edit; use core::fmt::{self, Debug, Display, Formatter}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::time::SystemTime; -/// Wrapper around an action that contains additional metadata. +/// Wrapper around an edit that contains additional metadata. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct Entry { - pub action: A, +pub struct Entry { + pub edit: E, #[cfg(feature = "std")] pub created_at: SystemTime, #[cfg(feature = "std")] pub updated_at: SystemTime, } -impl Entry { - pub fn undo(&mut self, target: &mut A::Target) -> A::Output { +impl Entry { + pub fn undo(&mut self, target: &mut E::Target) -> E::Output { #[cfg(feature = "std")] { self.updated_at = SystemTime::now(); } - self.action.undo(target) + self.edit.undo(target) } - pub fn redo(&mut self, target: &mut A::Target) -> A::Output { + pub fn redo(&mut self, target: &mut E::Target) -> E::Output { #[cfg(feature = "std")] { self.updated_at = SystemTime::now(); } - self.action.redo(target) + self.edit.redo(target) } } -impl From for Entry { +impl From for Entry { #[cfg(feature = "std")] - fn from(action: A) -> Self { + fn from(edit: E) -> Self { let at = SystemTime::now(); Entry { - action, + edit, created_at: at, updated_at: at, } } #[cfg(not(feature = "std"))] - fn from(action: A) -> Self { - Entry { action } + fn from(edit: E) -> Self { + Entry { edit } } } -impl Display for Entry { +impl Display for Entry { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(&self.action, f) + Display::fmt(&self.edit, f) } } diff --git a/src/from_fn.rs b/src/from_fn.rs index c3b08139..c041b3bc 100644 --- a/src/from_fn.rs +++ b/src/from_fn.rs @@ -1,8 +1,8 @@ -use crate::Action; +use crate::Edit; use core::fmt::{self, Debug, Formatter}; use core::mem; -/// Action made from a function. +/// An edit made from a function. /// /// # Examples /// ``` @@ -14,8 +14,8 @@ use core::mem; /// /// let a: fn(&mut String) = |s| s.push('a'); /// let b: fn(&mut String) = |s| s.push('b'); -/// record.apply(&mut target, FromFn::new(a)); -/// record.apply(&mut target, FromFn::new(b)); +/// record.edit(&mut target, FromFn::new(a)); +/// record.edit(&mut target, FromFn::new(b)); /// assert_eq!(target, "ab"); /// /// record.undo(&mut target); @@ -40,7 +40,7 @@ impl FromFn { } } -impl Action for FromFn +impl Edit for FromFn where F: FnMut(&mut T), T: Clone, @@ -48,7 +48,7 @@ where type Target = T; type Output = (); - fn apply(&mut self, target: &mut Self::Target) -> Self::Output { + fn edit(&mut self, target: &mut Self::Target) -> Self::Output { self.target = Some(target.clone()); (self.f)(target) } @@ -72,7 +72,7 @@ impl Debug for FromFn { } } -/// Action made from a fallible function. +/// An edit made from a fallible function. /// /// Same as [`FromFn`] but for functions that outputs [`Result`]. #[derive(Clone, Debug)] @@ -88,7 +88,7 @@ impl TryFromFn { } } -impl Action for TryFromFn +impl Edit for TryFromFn where F: FnMut(&mut T) -> Result<(), E>, T: Clone, @@ -96,7 +96,7 @@ where type Target = T; type Output = Result<(), E>; - fn apply(&mut self, target: &mut Self::Target) -> Self::Output { + fn edit(&mut self, target: &mut Self::Target) -> Self::Output { self.target = Some(target.clone()); (self.f)(target) } diff --git a/src/history.rs b/src/history.rs index 7a953df7..525aaddd 100644 --- a/src/history.rs +++ b/src/history.rs @@ -1,4 +1,4 @@ -//! A history tree of actions. +//! A history tree of edits. mod builder; mod checkpoint; @@ -11,14 +11,14 @@ pub use display::Display; pub use queue::Queue; use crate::socket::{Nop, Signal, Slot}; -use crate::{Action, At, Entry, Record}; +use crate::{At, Edit, Entry, Record}; use alloc::collections::{BTreeMap, VecDeque}; use alloc::string::{String, ToString}; use alloc::vec::Vec; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// A history tree of actions. +/// A history tree of edits. /// /// Unlike [`Record`] which maintains a linear undo history, /// [`History`] maintains an undo tree containing every edit made to the target. @@ -31,14 +31,14 @@ use serde::{Deserialize, Serialize}; /// let mut target = String::new(); /// let mut history = History::new(); /// -/// history.apply(&mut target, Push('a')); -/// history.apply(&mut target, Push('b')); -/// history.apply(&mut target, Push('c')); +/// history.edit(&mut target, Push('a')); +/// history.edit(&mut target, Push('b')); +/// history.edit(&mut target, Push('c')); /// let abc = history.branch(); /// /// history.go_to(&mut target, abc, 1); -/// history.apply(&mut target, Push('f')); -/// history.apply(&mut target, Push('g')); +/// history.edit(&mut target, Push('f')); +/// history.edit(&mut target, Push('g')); /// assert_eq!(target, "afg"); /// /// history.go_to(&mut target, abc, 3); @@ -47,28 +47,28 @@ use serde::{Deserialize, Serialize}; /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug)] -pub struct History { +pub struct History { root: usize, next: usize, saved: Option, - pub(crate) record: Record, - branches: BTreeMap>, + pub(crate) record: Record, + branches: BTreeMap>, } -impl History { +impl History { /// Returns a new history. - pub fn new() -> History { + pub fn new() -> History { History::builder().build() } } -impl History { +impl History { /// Returns a new history builder. - pub fn builder() -> Builder { + pub fn builder() -> Builder { Builder::new() } - /// Reserves capacity for at least `additional` more actions. + /// Reserves capacity for at least `additional` more edits. /// /// # Panics /// Panics if the new capacity overflows usize. @@ -86,7 +86,7 @@ impl History { self.record.shrink_to_fit(); } - /// Returns the number of actions in the current branch of the history. + /// Returns the number of edits in the current branch of the history. pub fn len(&self) -> usize { self.record.len() } @@ -131,28 +131,28 @@ impl History { self.root } - /// Returns the position of the current action. + /// Returns the position of the current edit. pub fn current(&self) -> usize { self.record.current() } /// Returns a structure for configurable formatting of the history. - pub fn display(&self) -> Display { + pub fn display(&self) -> Display { Display::from(self) } - /// Returns an iterator over the actions in the current branch. - pub fn actions(&self) -> impl Iterator { - self.record.actions() + /// Returns an iterator over the edits in the current branch. + pub fn edits(&self) -> impl Iterator { + self.record.edits() } /// Returns a queue. - pub fn queue(&mut self) -> Queue { + pub fn queue(&mut self) -> Queue { Queue::from(self) } /// Returns a checkpoint. - pub fn checkpoint(&mut self) -> Checkpoint { + pub fn checkpoint(&mut self) -> Checkpoint { Checkpoint::from(self) } @@ -161,12 +161,12 @@ impl History { } } -impl History { - /// Pushes the [`Action`] to the top of the history and executes its [`apply`](Action::apply) method. - pub fn apply(&mut self, target: &mut A::Target, action: A) -> A::Output { +impl History { + /// Pushes the [`Edit`] to the top of the history and executes its [`Edit::edit`] method. + pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { let at = self.at(); let saved = self.record.saved.filter(|&saved| saved > at.current); - let (output, merged, tail) = self.record.__apply(target, action); + let (output, merged, tail) = self.record.edit_inner(target, edit); // Check if the limit has been reached. if !merged && at.current == self.current() { let root = self.branch(); @@ -187,15 +187,15 @@ impl History { output } - /// Calls the [`Action::undo`] method for the active action + /// Calls the [`Edit::undo`] method for the active edit /// and sets the previous one as the new active one. - pub fn undo(&mut self, target: &mut A::Target) -> Option { + pub fn undo(&mut self, target: &mut E::Target) -> Option { self.record.undo(target) } - /// Calls the [`Action::redo`] method for the active action + /// Calls the [`Edit::redo`] method for the active edit /// and sets the next one as the new active one. - pub fn redo(&mut self, target: &mut A::Target) -> Option { + pub fn redo(&mut self, target: &mut E::Target) -> Option { self.record.redo(target) } @@ -205,7 +205,7 @@ impl History { self.record.set_saved(saved); } - /// Removes all actions from the history without undoing them. + /// Removes all edits from the history without undoing them. pub fn clear(&mut self) { self.root = 0; self.next = 1; @@ -284,7 +284,7 @@ impl History { } } - fn mk_path(&mut self, mut to: usize) -> Option)>> { + fn mk_path(&mut self, mut to: usize) -> Option)>> { debug_assert_ne!(self.branch(), to); let mut dest = self.branches.remove(&to)?; let mut i = dest.parent.branch; @@ -299,13 +299,13 @@ impl History { Some(path.into_iter().rev()) } - /// Repeatedly calls [`Action::undo`] or [`Action::redo`] until the action in `branch` at `current` is reached. + /// Repeatedly calls [`Edit::undo`] or [`Edit::redo`] until the edit in `branch` at `current` is reached. pub fn go_to( &mut self, - target: &mut A::Target, + target: &mut E::Target, branch: usize, current: usize, - ) -> Vec { + ) -> Vec { let root = self.root; if root == branch { return self.record.go_to(target, current); @@ -320,11 +320,11 @@ impl History { // Walk to `branch.current` either by undoing or redoing. let o = self.record.go_to(target, branch.parent.current); outputs.extend(o); - // Apply the actions in the branch and move older actions into their own branch. + // Apply the edits in the branch and move older edits into their own branch. for entry in branch.entries { let current = self.current(); let saved = self.record.saved.filter(|&saved| saved > current); - let (_, _, entries) = self.record.__apply(target, entry.action); + let (_, _, entries) = self.record.edit_inner(target, entry.edit); if !entries.is_empty() { self.branches .insert(self.root, Branch::new(new, current, entries)); @@ -338,28 +338,28 @@ impl History { } } -impl History { - /// Returns the string of the action which will be undone +impl History { + /// Returns the string of the edit which will be undone /// in the next call to [`History::undo`]. pub fn undo_string(&self) -> Option { self.record.undo_string() } - /// Returns the string of the action which will be redone + /// Returns the string of the edit which will be redone /// in the next call to [`History::redo`]. pub fn redo_string(&self) -> Option { self.record.redo_string() } } -impl Default for History { - fn default() -> History { +impl Default for History { + fn default() -> History { History::new() } } -impl From> for History { - fn from(record: Record) -> Self { +impl From> for History { + fn from(record: Record) -> Self { History { root: 0, next: 1, @@ -373,13 +373,13 @@ impl From> for History { /// A branch in the history. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub(crate) struct Branch { +pub(crate) struct Branch { pub(crate) parent: At, - pub(crate) entries: VecDeque>, + pub(crate) entries: VecDeque>, } -impl Branch { - fn new(branch: usize, current: usize, entries: VecDeque>) -> Branch { +impl Branch { + fn new(branch: usize, current: usize, entries: VecDeque>) -> Branch { Branch { parent: At::new(branch, current), entries, @@ -393,11 +393,11 @@ mod tests { struct Push(char); - impl Action for Push { + impl Edit for Push { type Target = String; type Output = (); - fn apply(&mut self, s: &mut String) { + fn edit(&mut self, s: &mut String) { s.push(self.0); } @@ -425,44 +425,44 @@ mod tests { // a let mut target = String::new(); let mut history = History::new(); - history.apply(&mut target, Push('a')); - history.apply(&mut target, Push('b')); - history.apply(&mut target, Push('c')); - history.apply(&mut target, Push('d')); - history.apply(&mut target, Push('e')); + history.edit(&mut target, Push('a')); + history.edit(&mut target, Push('b')); + history.edit(&mut target, Push('c')); + history.edit(&mut target, Push('d')); + history.edit(&mut target, Push('e')); assert_eq!(target, "abcde"); history.undo(&mut target).unwrap(); history.undo(&mut target).unwrap(); assert_eq!(target, "abc"); let abcde = history.branch(); - history.apply(&mut target, Push('f')); - history.apply(&mut target, Push('g')); + history.edit(&mut target, Push('f')); + history.edit(&mut target, Push('g')); assert_eq!(target, "abcfg"); history.undo(&mut target).unwrap(); let abcfg = history.branch(); - history.apply(&mut target, Push('h')); - history.apply(&mut target, Push('i')); - history.apply(&mut target, Push('j')); + history.edit(&mut target, Push('h')); + history.edit(&mut target, Push('i')); + history.edit(&mut target, Push('j')); assert_eq!(target, "abcfhij"); history.undo(&mut target).unwrap(); let abcfhij = history.branch(); - history.apply(&mut target, Push('k')); + history.edit(&mut target, Push('k')); assert_eq!(target, "abcfhik"); history.undo(&mut target).unwrap(); let abcfhik = history.branch(); - history.apply(&mut target, Push('l')); + history.edit(&mut target, Push('l')); assert_eq!(target, "abcfhil"); - history.apply(&mut target, Push('m')); + history.edit(&mut target, Push('m')); assert_eq!(target, "abcfhilm"); let abcfhilm = history.branch(); history.go_to(&mut target, abcde, 2); - history.apply(&mut target, Push('n')); - history.apply(&mut target, Push('o')); + history.edit(&mut target, Push('n')); + history.edit(&mut target, Push('o')); assert_eq!(target, "abno"); history.undo(&mut target).unwrap(); let abno = history.branch(); - history.apply(&mut target, Push('p')); - history.apply(&mut target, Push('q')); + history.edit(&mut target, Push('p')); + history.edit(&mut target, Push('q')); assert_eq!(target, "abnpq"); let abnpq = history.branch(); diff --git a/src/history/builder.rs b/src/history/builder.rs index 28d1dd79..7785ed6b 100644 --- a/src/history/builder.rs +++ b/src/history/builder.rs @@ -14,7 +14,7 @@ use crate::{History, Nop, Slot}; /// .capacity(100) /// .connect(|s| { dbg!(s); }) /// .build(); -/// # history.apply(&mut target, Push('a')); +/// # history.edit(&mut target, Push('a')); /// # } /// ``` #[derive(Debug)] diff --git a/src/history/checkpoint.rs b/src/history/checkpoint.rs index 067f8e77..4e12005a 100644 --- a/src/history/checkpoint.rs +++ b/src/history/checkpoint.rs @@ -1,10 +1,10 @@ use super::Queue; -use crate::{Action, History, Slot}; +use crate::{Edit, History, Slot}; use alloc::vec::Vec; #[derive(Debug)] enum CheckpointEntry { - Apply(usize), + Edit(usize), Undo, Redo, } @@ -28,21 +28,21 @@ impl Checkpoint<'_, A, S> { } } -impl Checkpoint<'_, A, S> { - /// Calls the `apply` method. - pub fn apply(&mut self, target: &mut A::Target, action: A) -> A::Output { +impl Checkpoint<'_, A, S> { + /// Calls the [`History::edit`] method. + pub fn edit(&mut self, target: &mut A::Target, edit: A) -> A::Output { let branch = self.history.branch(); - self.entries.push(CheckpointEntry::Apply(branch)); - self.history.apply(target, action) + self.entries.push(CheckpointEntry::Edit(branch)); + self.history.edit(target, edit) } - /// Calls the `undo` method. + /// Calls the [`History::undo`] method. pub fn undo(&mut self, target: &mut A::Target) -> Option { self.entries.push(CheckpointEntry::Undo); self.history.undo(target) } - /// Calls the `redo` method. + /// Calls the [`History::redo`] method. pub fn redo(&mut self, target: &mut A::Target) -> Option { self.entries.push(CheckpointEntry::Redo); self.history.redo(target) @@ -57,7 +57,7 @@ impl Checkpoint<'_, A, S> { .into_iter() .rev() .filter_map(|entry| match entry { - CheckpointEntry::Apply(branch) => { + CheckpointEntry::Edit(branch) => { let output = self.history.undo(target)?; let root = self.history.branch(); if root == branch { @@ -100,17 +100,17 @@ mod tests { let mut history = History::new(); let mut checkpoint = history.checkpoint(); - checkpoint.apply(&mut target, A); - checkpoint.apply(&mut target, B); - checkpoint.apply(&mut target, C); + checkpoint.edit(&mut target, A); + checkpoint.edit(&mut target, B); + checkpoint.edit(&mut target, C); assert_eq!(target, "abc"); checkpoint.undo(&mut target); checkpoint.undo(&mut target); assert_eq!(target, "a"); - checkpoint.apply(&mut target, D); - checkpoint.apply(&mut target, E); + checkpoint.edit(&mut target, D); + checkpoint.edit(&mut target, E); assert_eq!(target, "ade"); checkpoint.cancel(&mut target); diff --git a/src/history/display.rs b/src/history/display.rs index c5a9657f..1105452e 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -31,13 +31,13 @@ impl Display<'_, A, S> { self } - /// Show the position of the action (on by default). + /// Show the position of the edit (on by default). pub fn position(&mut self, on: bool) -> &mut Self { self.format.position = on; self } - /// Show the saved action (on by default). + /// Show the saved edit (on by default). pub fn saved(&mut self, on: bool) -> &mut Self { self.format.saved = on; self diff --git a/src/history/queue.rs b/src/history/queue.rs index 1aba7746..de5fef32 100644 --- a/src/history/queue.rs +++ b/src/history/queue.rs @@ -1,28 +1,28 @@ use super::Checkpoint; -use crate::{Action, History, Slot}; +use crate::{Edit, History, Slot}; use alloc::vec::Vec; #[derive(Debug)] enum QueueEntry { - Apply(A), + Edit(A), Undo, Redo, } -/// Wraps a record and gives it batch queue functionality. +/// Wraps a history and gives it batch queue functionality. /// /// # Examples /// ``` /// # include!("../doctest.rs"); /// # fn main() { -/// # use undo::Record; +/// # use undo::History; /// let mut string = String::new(); -/// let mut record = Record::new(); +/// let mut record = History::new(); /// let mut queue = record.queue(); /// -/// queue.apply(Push('a')); -/// queue.apply(Push('b')); -/// queue.apply(Push('c')); +/// queue.edit(Push('a')); +/// queue.edit(Push('b')); +/// queue.edit(Push('c')); /// assert_eq!(string, ""); /// /// queue.commit(&mut string); @@ -47,35 +47,35 @@ impl Queue<'_, A, S> { } } -impl Queue<'_, A, S> { - /// Queues an `apply` action. - pub fn apply(&mut self, action: A) { - self.entries.push(QueueEntry::Apply(action)); +impl Queue<'_, A, S> { + /// Queues a [`History::edit`] call. + pub fn edit(&mut self, edit: A) { + self.entries.push(QueueEntry::Edit(edit)); } - /// Queues an `undo` action. + /// Queues a [`History::undo`] call. pub fn undo(&mut self) { self.entries.push(QueueEntry::Undo); } - /// Queues a `redo` action. + /// Queues a [`History::redo`] call. pub fn redo(&mut self) { self.entries.push(QueueEntry::Redo); } - /// Applies the queued actions. + /// Applies the queued edits. pub fn commit(self, target: &mut A::Target) -> Vec { self.entries .into_iter() .filter_map(|entry| match entry { - QueueEntry::Apply(action) => Some(self.history.apply(target, action)), + QueueEntry::Edit(edit) => Some(self.history.edit(target, edit)), QueueEntry::Undo => self.history.undo(target), QueueEntry::Redo => self.history.redo(target), }) .collect() } - /// Cancels the queued actions. + /// Cancels the queued edits. pub fn cancel(self) {} } diff --git a/src/join.rs b/src/join.rs index 2c41b407..7d4aa5b7 100644 --- a/src/join.rs +++ b/src/join.rs @@ -1,9 +1,9 @@ -use crate::Action; +use crate::Edit; use core::fmt::{self, Display, Formatter}; -/// Two actions joined together. +/// Two edits joined together. /// -/// Can be used to build more complex actions from simpler ones. +/// Can be used to build more complex edits from simpler ones. /// /// # Examples /// ``` @@ -14,7 +14,7 @@ use core::fmt::{self, Display, Formatter}; /// let mut record = Record::new(); /// /// let abc = Join::new(Push('a'), Push('b')).join(Push('c')); -/// record.apply(&mut target, abc); +/// record.edit(&mut target, abc); /// assert_eq!(target, "abc"); /// record.undo(&mut target); /// assert_eq!(target, ""); @@ -40,18 +40,18 @@ impl Join { } } -impl Action for Join +impl Edit for Join where - A: Action, - B: Action, + A: Edit, + B: Edit, { type Target = A::Target; type Output = A::Output; /// The output of a will be discarded. - fn apply(&mut self, target: &mut A::Target) -> Self::Output { - self.a.apply(target); - self.b.apply(target) + fn edit(&mut self, target: &mut A::Target) -> Self::Output { + self.a.edit(target); + self.b.edit(target) } /// The output of b will be discarded. @@ -77,9 +77,9 @@ where } } -/// Joins two fallible actions together. +/// Joins two fallible edits together. /// -/// Same as [`Join`] but for actions that outputs [`Result`]. +/// Same as [`Join`] but for edits that outputs [`Result`]. #[derive(Clone, Debug)] pub struct TryJoin { a: A, @@ -98,18 +98,18 @@ impl TryJoin { } } -impl Action for TryJoin +impl Edit for TryJoin where - A: Action>, - B: Action, + A: Edit>, + B: Edit, { type Target = A::Target; type Output = A::Output; /// The output of a will be discarded if success. - fn apply(&mut self, target: &mut A::Target) -> Self::Output { - self.a.apply(target)?; - self.b.apply(target) + fn edit(&mut self, target: &mut A::Target) -> Self::Output { + self.a.edit(target)?; + self.b.edit(target) } /// The output of b will be discarded if success. diff --git a/src/lib.rs b/src/lib.rs index b28789d1..01e80ace 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,12 +3,12 @@ //! It is an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), //! where all modifications are done by creating objects that applies the modifications. //! All objects knows how to undo the changes it applies, and by using the provided data -//! structures it is easy to apply, undo, and redo changes made to a target. +//! structures it is easy to undo and redo edits made to a target. //! //! # Features //! -//! * [`Action`] provides the base functionality for all actions. Multiple [`Action`]s can be merged into a single action -//! by implementing the [`merge`](Action::merge) method on the action. This allows smaller actions to be used to build +//! * [`Edit`] provides the base functionality for all edit commands. Multiple [`Edit`]s can be merged into a single edit +//! by implementing the [`Edit::merge`] method on the edit. This allows smaller edits to be used to build //! more complex operations, or smaller incremental changes to be merged into larger changes that can be undone and //! redone in a single step. //! * [`Record`] provides basic stack based undo-redo functionality. @@ -72,27 +72,27 @@ pub use record::Record; #[cfg(feature = "alloc")] pub use socket::{Nop, Signal, Slot}; -/// Base functionality for all actions. -pub trait Action { +/// Base functionality for all edits. +pub trait Edit { /// The target type. type Target; /// The output type. type Output; - /// Applies the action on the target. - fn apply(&mut self, target: &mut Self::Target) -> Self::Output; + /// Applies the edit on the target. + fn edit(&mut self, target: &mut Self::Target) -> Self::Output; - /// Restores the state of the target as it was before the action was applied. + /// Restores the state of the target as it was before the edit was applied. fn undo(&mut self, target: &mut Self::Target) -> Self::Output; - /// Reapplies the action on the target. + /// Reapplies the edit on the target. /// - /// The default implementation uses the [`Action::apply`] implementation. + /// The default implementation uses the [`Edit::edit`] implementation. fn redo(&mut self, target: &mut Self::Target) -> Self::Output { - self.apply(target) + self.edit(target) } - /// Used for manual merging of actions. See [`Merged`] for more information. + /// Used for manual merging of edits. See [`Merged`] for more information. fn merge(&mut self, other: Self) -> Merged where Self: Sized, @@ -101,21 +101,21 @@ pub trait Action { } } -/// Says if the action have been merged with another action. +/// Says if the edit have been merged with another edit. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub enum Merged { - /// The actions have been merged. + /// The edits have been merged. /// - /// This means that the `other` action will not be added to the stack. + /// This means that the `other` edit will not be added to the stack. Yes, - /// The actions have not been merged. + /// The edits have not been merged. /// - /// We need to return the `other` action so it can be added to the stack. + /// We need to return the `other` edit so it can be added to the stack. No(A), - /// The two actions cancels each other out. + /// The two edits cancels each other out. /// - /// This means that both action will be removed from the stack. + /// This means that both edits will be removed from the stack. Annul, } diff --git a/src/record.rs b/src/record.rs index 85a7aa95..ef5d458b 100644 --- a/src/record.rs +++ b/src/record.rs @@ -1,4 +1,4 @@ -//! A linear record of actions. +//! A linear record of edits. mod builder; mod checkpoint; @@ -11,7 +11,7 @@ pub use display::Display; pub use queue::Queue; use crate::socket::{Nop, Slot, Socket}; -use crate::{Action, Entry, History, Merged, Signal}; +use crate::{Edit, Entry, History, Merged, Signal}; use alloc::collections::VecDeque; use alloc::string::{String, ToString}; use alloc::vec::Vec; @@ -19,7 +19,7 @@ use core::num::NonZeroUsize; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// A linear record of actions. +/// A linear record of edits. /// /// The record can roll the targets state backwards and forwards by using /// the undo and redo methods. In addition, the record can notify the user @@ -35,9 +35,9 @@ use serde::{Deserialize, Serialize}; /// let mut target = String::new(); /// let mut record = Record::new(); /// -/// record.apply(&mut target, Push('a')); -/// record.apply(&mut target, Push('b')); -/// record.apply(&mut target, Push('c')); +/// record.edit(&mut target, Push('a')); +/// record.edit(&mut target, Push('b')); +/// record.edit(&mut target, Push('c')); /// assert_eq!(target, "abc"); /// /// record.undo(&mut target); @@ -53,28 +53,28 @@ use serde::{Deserialize, Serialize}; /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug)] -pub struct Record { - pub(crate) entries: VecDeque>, +pub struct Record { + pub(crate) entries: VecDeque>, pub(crate) limit: NonZeroUsize, pub(crate) current: usize, pub(crate) saved: Option, pub(crate) socket: Socket, } -impl Record { +impl Record { /// Returns a new record. - pub fn new() -> Record { + pub fn new() -> Record { Record::builder().build() } } -impl Record { +impl Record { /// Returns a new record builder. - pub fn builder() -> Builder { + pub fn builder() -> Builder { Builder::new() } - /// Reserves capacity for at least `additional` more actions. + /// Reserves capacity for at least `additional` more edits. /// /// # Panics /// Panics if the new capacity overflows usize. @@ -92,7 +92,7 @@ impl Record { self.entries.shrink_to_fit(); } - /// Returns the number of actions in the record. + /// Returns the number of edits in the record. pub fn len(&self) -> usize { self.entries.len() } @@ -132,45 +132,45 @@ impl Record { self.saved == Some(self.current) } - /// Returns the position of the current action. + /// Returns the position of the current edit. pub fn current(&self) -> usize { self.current } /// Returns a structure for configurable formatting of the record. - pub fn display(&self) -> Display { + pub fn display(&self) -> Display { Display::from(self) } - /// Returns an iterator over the actions. - pub fn actions(&self) -> impl Iterator { - self.entries.iter().map(|e| &e.action) + /// Returns an iterator over the edits. + pub fn edits(&self) -> impl Iterator { + self.entries.iter().map(|e| &e.edit) } /// Returns a queue. - pub fn queue(&mut self) -> Queue { + pub fn queue(&mut self) -> Queue { Queue::from(self) } /// Returns a checkpoint. - pub fn checkpoint(&mut self) -> Checkpoint { + pub fn checkpoint(&mut self) -> Checkpoint { Checkpoint::from(self) } } -impl Record { - /// Pushes the action on top of the record and executes its [`Action::apply`] method. - pub fn apply(&mut self, target: &mut A::Target, action: A) -> A::Output { - let (output, _, _) = self.__apply(target, action); +impl Record { + /// Pushes the edit on top of the record and executes its [`Edit::edit`] method. + pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { + let (output, _, _) = self.edit_inner(target, edit); output } - pub(crate) fn __apply( + pub(crate) fn edit_inner( &mut self, - target: &mut A::Target, - mut action: A, - ) -> (A::Output, bool, VecDeque>) { - let output = action.apply(target); + target: &mut E::Target, + mut edit: E, + ) -> (E::Output, bool, VecDeque>) { + let output = edit.edit(target); // We store the state of the stack before adding the entry. let current = self.current; let could_undo = self.can_undo(); @@ -180,10 +180,10 @@ impl Record { let tail = self.entries.split_off(current); // Check if the saved state was popped off. self.saved = self.saved.filter(|&saved| saved <= current); - // Try to merge actions unless the target is in a saved state. + // Try to merge edits unless the target is in a saved state. let merged = match self.entries.back_mut() { - Some(last) if !was_saved => last.action.merge(action), - _ => Merged::No(action), + Some(last) if !was_saved => last.edit.merge(edit), + _ => Merged::No(edit), }; let merged_or_annulled = match merged { @@ -193,16 +193,16 @@ impl Record { self.current -= 1; true } - // If actions are not merged or annulled push it onto the storage. - Merged::No(action) => { - // If limit is reached, pop off the first action. + // If edits are not merged or annulled push it onto the storage. + Merged::No(edit) => { + // If limit is reached, pop off the first edit. if self.limit() == self.current { self.entries.pop_front(); self.saved = self.saved.and_then(|saved| saved.checked_sub(1)); } else { self.current += 1; } - self.entries.push_back(Entry::from(action)); + self.entries.push_back(Entry::from(edit)); false } }; @@ -213,9 +213,9 @@ impl Record { (output, merged_or_annulled, tail) } - /// Calls the [`Action::undo`] method for the active action and sets + /// Calls the [`Edit::undo`] method for the active edit and sets /// the previous one as the new active one. - pub fn undo(&mut self, target: &mut A::Target) -> Option { + pub fn undo(&mut self, target: &mut E::Target) -> Option { self.can_undo().then(|| { let was_saved = self.is_saved(); let old = self.current; @@ -231,9 +231,9 @@ impl Record { }) } - /// Calls the [`Action::redo`] method for the active action and sets + /// Calls the [`Edit::redo`] method for the active edit and sets /// the next one as the new active one. - pub fn redo(&mut self, target: &mut A::Target) -> Option { + pub fn redo(&mut self, target: &mut E::Target) -> Option { self.can_redo().then(|| { let was_saved = self.is_saved(); let old = self.current; @@ -261,7 +261,7 @@ impl Record { } } - /// Removes all actions from the record without undoing them. + /// Removes all edits from the record without undoing them. pub fn clear(&mut self) { let could_undo = self.can_undo(); let could_redo = self.can_redo(); @@ -273,13 +273,13 @@ impl Record { } /// Revert the changes done to the target since the saved state. - pub fn revert(&mut self, target: &mut A::Target) -> Vec { + pub fn revert(&mut self, target: &mut E::Target) -> Vec { self.saved .map_or_else(Vec::new, |saved| self.go_to(target, saved)) } - /// Repeatedly calls [`Action::undo`] or [`Action::redo`] until the action at `current` is reached. - pub fn go_to(&mut self, target: &mut A::Target, current: usize) -> Vec { + /// Repeatedly calls [`Edit::undo`] or [`Edit::redo`] until the edit at `current` is reached. + pub fn go_to(&mut self, target: &mut E::Target, current: usize) -> Vec { if current > self.len() { return Vec::new(); } @@ -318,32 +318,32 @@ impl Record { } } -impl Record { - /// Returns the string of the action which will be undone +impl Record { + /// Returns the string of the edit which will be undone /// in the next call to [`Record::undo`]. pub fn undo_string(&self) -> Option { self.current.checked_sub(1).and_then(|i| self.string_at(i)) } - /// Returns the string of the action which will be redone + /// Returns the string of the edit which will be redone /// in the next call to [`Record::redo`]. pub fn redo_string(&self) -> Option { self.string_at(self.current) } fn string_at(&self, i: usize) -> Option { - self.entries.get(i).map(|e| e.action.to_string()) + self.entries.get(i).map(|e| e.edit.to_string()) } } -impl Default for Record { - fn default() -> Record { +impl Default for Record { + fn default() -> Record { Record::new() } } -impl From> for Record { - fn from(history: History) -> Record { +impl From> for Record { + fn from(history: History) -> Record { history.record } } @@ -354,26 +354,26 @@ mod tests { use alloc::string::String; use alloc::vec::Vec; - enum Edit { + enum Editor { Push(Push), Pop(Pop), } - impl Action for Edit { + impl Edit for Editor { type Target = String; type Output = (); - fn apply(&mut self, s: &mut String) { + fn edit(&mut self, s: &mut String) { match self { - Edit::Push(add) => add.apply(s), - Edit::Pop(del) => del.apply(s), + Editor::Push(add) => add.edit(s), + Editor::Pop(del) => del.edit(s), } } fn undo(&mut self, s: &mut String) { match self { - Edit::Push(add) => add.undo(s), - Edit::Pop(del) => del.undo(s), + Editor::Push(add) => add.undo(s), + Editor::Pop(del) => del.undo(s), } } @@ -382,8 +382,8 @@ mod tests { Self: Sized, { match (self, edit) { - (Edit::Push(_), Edit::Pop(_)) => Merged::Annul, - (Edit::Pop(Pop(Some(a))), Edit::Push(Push(b))) if a == &b => Merged::Annul, + (Editor::Push(_), Editor::Pop(_)) => Merged::Annul, + (Editor::Pop(Pop(Some(a))), Editor::Push(Push(b))) if a == &b => Merged::Annul, (_, edit) => Merged::No(edit), } } @@ -392,11 +392,11 @@ mod tests { #[derive(Debug, PartialEq)] struct Push(char); - impl Action for Push { + impl Edit for Push { type Target = String; type Output = (); - fn apply(&mut self, s: &mut String) { + fn edit(&mut self, s: &mut String) { s.push(self.0); } @@ -408,11 +408,11 @@ mod tests { #[derive(Default)] struct Pop(Option); - impl Action for Pop { + impl Edit for Pop { type Target = String; type Output = (); - fn apply(&mut self, s: &mut String) { + fn edit(&mut self, s: &mut String) { self.0 = s.pop(); } @@ -426,11 +426,11 @@ mod tests { fn go_to() { let mut target = String::new(); let mut record = Record::new(); - record.apply(&mut target, Push('a')); - record.apply(&mut target, Push('b')); - record.apply(&mut target, Push('c')); - record.apply(&mut target, Push('d')); - record.apply(&mut target, Push('e')); + record.edit(&mut target, Push('a')); + record.edit(&mut target, Push('b')); + record.edit(&mut target, Push('c')); + record.edit(&mut target, Push('d')); + record.edit(&mut target, Push('e')); record.go_to(&mut target, 0); assert_eq!(record.current(), 0); @@ -458,19 +458,19 @@ mod tests { fn annul() { let mut target = String::new(); let mut record = Record::new(); - record.apply(&mut target, Edit::Push(Push('a'))); - record.apply(&mut target, Edit::Pop(Pop::default())); - record.apply(&mut target, Edit::Push(Push('b'))); + record.edit(&mut target, Editor::Push(Push('a'))); + record.edit(&mut target, Editor::Pop(Pop::default())); + record.edit(&mut target, Editor::Push(Push('b'))); assert_eq!(record.len(), 1); } #[test] - fn actions() { + fn edits() { let mut target = String::new(); let mut record = Record::new(); - record.apply(&mut target, Push('a')); - record.apply(&mut target, Push('b')); - let collected = record.actions().collect::>(); + record.edit(&mut target, Push('a')); + record.edit(&mut target, Push('b')); + let collected = record.edits().collect::>(); assert_eq!(&collected[..], &[&Push('a'), &Push('b')][..]); } } diff --git a/src/record/builder.rs b/src/record/builder.rs index 19393eda..9bc5c324 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -17,7 +17,7 @@ use core::num::NonZeroUsize; /// .capacity(100) /// .connect(|s| { dbg!(s); }) /// .build(); -/// # record.apply(&mut target, Push('a')); +/// # record.edit(&mut target, Push('a')); /// # } /// ``` #[derive(Debug)] diff --git a/src/record/checkpoint.rs b/src/record/checkpoint.rs index 685b48b2..8775dcc7 100644 --- a/src/record/checkpoint.rs +++ b/src/record/checkpoint.rs @@ -1,11 +1,11 @@ use super::Queue; -use crate::{Action, Entry, Record, Slot}; +use crate::{Edit, Entry, Record, Slot}; use alloc::collections::VecDeque; use alloc::vec::Vec; #[derive(Debug)] enum CheckpointEntry { - Apply(Option, VecDeque>), + Edit(Option, VecDeque>), Undo, Redo, } @@ -29,12 +29,12 @@ impl Checkpoint<'_, A, S> { } } -impl Checkpoint<'_, A, S> { +impl Checkpoint<'_, A, S> { /// Calls the `apply` method. - pub fn apply(&mut self, target: &mut A::Target, action: A) -> A::Output { + pub fn edit(&mut self, target: &mut A::Target, edit: A) -> A::Output { let saved = self.record.saved; - let (output, _, tail) = self.record.__apply(target, action); - self.entries.push(CheckpointEntry::Apply(saved, tail)); + let (output, _, tail) = self.record.edit_inner(target, edit); + self.entries.push(CheckpointEntry::Edit(saved, tail)); output } @@ -61,7 +61,7 @@ impl Checkpoint<'_, A, S> { .into_iter() .rev() .filter_map(|entry| match entry { - CheckpointEntry::Apply(saved, mut entries) => { + CheckpointEntry::Edit(saved, mut entries) => { let output = self.record.undo(target)?; self.record.entries.pop_back(); self.record.entries.append(&mut entries); @@ -103,19 +103,19 @@ mod tests { let mut target = String::new(); let mut record = Record::new(); let mut cp1 = record.checkpoint(); - cp1.apply(&mut target, A); - cp1.apply(&mut target, B); - cp1.apply(&mut target, C); + cp1.edit(&mut target, A); + cp1.edit(&mut target, B); + cp1.edit(&mut target, C); assert_eq!(target, "abc"); let mut cp2 = cp1.checkpoint(); - cp2.apply(&mut target, D); - cp2.apply(&mut target, E); - cp2.apply(&mut target, F); + cp2.edit(&mut target, D); + cp2.edit(&mut target, E); + cp2.edit(&mut target, F); assert_eq!(target, "abcdef"); let mut cp3 = cp2.checkpoint(); - cp3.apply(&mut target, G); - cp3.apply(&mut target, H); - cp3.apply(&mut target, I); + cp3.edit(&mut target, G); + cp3.edit(&mut target, H); + cp3.edit(&mut target, I); assert_eq!(target, "abcdefghi"); cp3.commit(); cp2.commit(); @@ -128,17 +128,17 @@ mod tests { let mut target = String::new(); let mut record = Record::new(); let mut cp1 = record.checkpoint(); - cp1.apply(&mut target, A); - cp1.apply(&mut target, B); - cp1.apply(&mut target, C); + cp1.edit(&mut target, A); + cp1.edit(&mut target, B); + cp1.edit(&mut target, C); let mut cp2 = cp1.checkpoint(); - cp2.apply(&mut target, D); - cp2.apply(&mut target, E); - cp2.apply(&mut target, F); + cp2.edit(&mut target, D); + cp2.edit(&mut target, E); + cp2.edit(&mut target, F); let mut cp3 = cp2.checkpoint(); - cp3.apply(&mut target, G); - cp3.apply(&mut target, H); - cp3.apply(&mut target, I); + cp3.edit(&mut target, G); + cp3.edit(&mut target, H); + cp3.edit(&mut target, I); assert_eq!(target, "abcdefghi"); cp3.cancel(&mut target); assert_eq!(target, "abcdef"); @@ -152,17 +152,17 @@ mod tests { fn checkpoint_saved() { let mut target = String::new(); let mut record = Record::new(); - record.apply(&mut target, A); - record.apply(&mut target, B); - record.apply(&mut target, C); + record.edit(&mut target, A); + record.edit(&mut target, B); + record.edit(&mut target, C); record.set_saved(true); record.undo(&mut target).unwrap(); record.undo(&mut target).unwrap(); record.undo(&mut target).unwrap(); let mut cp = record.checkpoint(); - cp.apply(&mut target, D); - cp.apply(&mut target, E); - cp.apply(&mut target, F); + cp.edit(&mut target, D); + cp.edit(&mut target, E); + cp.edit(&mut target, F); assert_eq!(target, "def"); cp.cancel(&mut target); assert_eq!(target, ""); diff --git a/src/record/display.rs b/src/record/display.rs index e7b85fe3..ba46c424 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -31,13 +31,13 @@ impl Display<'_, A, S> { self } - /// Show the position of the action (on by default). + /// Show the position of the edit (on by default). pub fn position(&mut self, on: bool) -> &mut Self { self.format.position = on; self } - /// Show the saved action (on by default). + /// Show the saved edit (on by default). pub fn saved(&mut self, on: bool) -> &mut Self { self.format.saved = on; self diff --git a/src/record/queue.rs b/src/record/queue.rs index c378579d..aa202e5a 100644 --- a/src/record/queue.rs +++ b/src/record/queue.rs @@ -1,10 +1,10 @@ use super::Checkpoint; -use crate::{Action, Record, Slot}; +use crate::{Edit, Record, Slot}; use alloc::vec::Vec; #[derive(Debug)] enum QueueEntry { - Apply(A), + Edit(A), Undo, Redo, } @@ -20,9 +20,9 @@ enum QueueEntry { /// let mut record = Record::new(); /// let mut queue = record.queue(); /// -/// queue.apply(Push('a')); -/// queue.apply(Push('b')); -/// queue.apply(Push('c')); +/// queue.edit(Push('a')); +/// queue.edit(Push('b')); +/// queue.edit(Push('c')); /// assert_eq!(string, ""); /// /// queue.commit(&mut string); @@ -47,35 +47,35 @@ impl Queue<'_, A, S> { } } -impl Queue<'_, A, S> { - /// Queues an `apply` action. - pub fn apply(&mut self, action: A) { - self.entries.push(QueueEntry::Apply(action)); +impl Queue<'_, A, S> { + /// Queues a [`Record::edit`] call. + pub fn edit(&mut self, edit: A) { + self.entries.push(QueueEntry::Edit(edit)); } - /// Queues an `undo` action. + /// Queues a [`Record::undo`] call. pub fn undo(&mut self) { self.entries.push(QueueEntry::Undo); } - /// Queues a `redo` action. + /// Queues a [`Record::redo`] call. pub fn redo(&mut self) { self.entries.push(QueueEntry::Redo); } - /// Applies the queued actions. + /// Applies the queued edits. pub fn commit(self, target: &mut A::Target) -> Vec { self.entries .into_iter() .filter_map(|entry| match entry { - QueueEntry::Apply(action) => Some(self.record.apply(target, action)), + QueueEntry::Edit(edit) => Some(self.record.edit(target, edit)), QueueEntry::Undo => self.record.undo(target), QueueEntry::Redo => self.record.redo(target), }) .collect() } - /// Cancels the queued actions. + /// Cancels the queued edits. pub fn cancel(self) {} } @@ -110,9 +110,9 @@ mod tests { q2.undo(); q2.undo(); let mut q3 = q2.queue(); - q3.apply(A); - q3.apply(B); - q3.apply(C); + q3.edit(A); + q3.edit(B); + q3.edit(C); assert_eq!(target, ""); q3.commit(&mut target); assert_eq!(target, "abc"); diff --git a/src/socket.rs b/src/socket.rs index f8192abf..3d564c37 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -57,7 +57,7 @@ impl Slot for F { /// The signal used for communicating state changes. /// -/// For example, if the history tree can no longer redo any actions, +/// For example, if the history tree can no longer redo any edits, /// it sends a `Redo(false)` signal to tell the user. #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[non_exhaustive] From d4489ca34bd99c50037ecf6bd680bd19688d6d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Sun, 9 Apr 2023 15:32:32 +0200 Subject: [PATCH 02/33] Rename Push to Add --- README.md | 10 ++++---- examples/history.rs | 20 +++++++-------- examples/record.rs | 14 +++++------ src/any.rs | 2 +- src/doctest.rs | 4 +-- src/history.rs | 48 ++++++++++++++++++------------------ src/history/builder.rs | 2 +- src/history/queue.rs | 6 ++--- src/join.rs | 2 +- src/record.rs | 56 +++++++++++++++++++++--------------------- src/record/builder.rs | 2 +- src/record/queue.rs | 6 ++--- 12 files changed, 86 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index e1763979..5d5cc58d 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ structures it is easy to undo and redo edits made to a target. ```rust use undo::{Edit, Record}; -struct Push(char); +struct Add(char); -impl Edit for Push { +impl Edit for Add { type Target = String; type Output = (); @@ -35,9 +35,9 @@ fn main() { let mut target = String::new(); let mut record = Record::new(); - record.edit(&mut target, Push('a')); - record.edit(&mut target, Push('b')); - record.edit(&mut target, Push('c')); + record.edit(&mut target, Add('a')); + record.edit(&mut target, Add('b')); + record.edit(&mut target, Add('c')); assert_eq!(target, "abc"); record.undo(&mut target); diff --git a/examples/history.rs b/examples/history.rs index fb7beb00..5143449e 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -1,9 +1,9 @@ use core::fmt::{self, Display, Formatter}; use undo::{Edit, History}; -struct Push(char); +struct Add(char); -impl Edit for Push { +impl Edit for Add { type Target = String; type Output = (); @@ -16,9 +16,9 @@ impl Edit for Push { } } -impl Display for Push { +impl Display for Add { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Push '{}'", self.0) + write!(f, "Add '{}'", self.0) } } @@ -26,9 +26,9 @@ fn main() { let mut target = String::new(); let mut history = History::new(); - history.edit(&mut target, Push('a')); - history.edit(&mut target, Push('b')); - history.edit(&mut target, Push('c')); + history.edit(&mut target, Add('a')); + history.edit(&mut target, Add('b')); + history.edit(&mut target, Add('c')); assert_eq!(target, "abc"); let abc_branch = history.branch(); @@ -37,9 +37,9 @@ fn main() { history.undo(&mut target); assert_eq!(target, "ab"); - history.edit(&mut target, Push('d')); - history.edit(&mut target, Push('e')); - history.edit(&mut target, Push('f')); + history.edit(&mut target, Add('d')); + history.edit(&mut target, Add('e')); + history.edit(&mut target, Add('f')); assert_eq!(target, "abdef"); let abdef_branch = history.branch(); diff --git a/examples/record.rs b/examples/record.rs index 20a6337e..b81ff9b7 100644 --- a/examples/record.rs +++ b/examples/record.rs @@ -1,9 +1,9 @@ use core::fmt::{self, Display, Formatter}; use undo::{Edit, Record}; -struct Push(char); +struct Add(char); -impl Edit for Push { +impl Edit for Add { type Target = String; type Output = (); @@ -16,9 +16,9 @@ impl Edit for Push { } } -impl Display for Push { +impl Display for Add { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Push '{}'", self.0) + write!(f, "Add '{}'", self.0) } } @@ -26,9 +26,9 @@ fn main() { let mut target = String::new(); let mut record = Record::new(); - record.edit(&mut target, Push('a')); - record.edit(&mut target, Push('b')); - record.edit(&mut target, Push('c')); + record.edit(&mut target, Add('a')); + record.edit(&mut target, Add('b')); + record.edit(&mut target, Add('c')); assert_eq!(target, "abc"); record.undo(&mut target); diff --git a/src/any.rs b/src/any.rs index 3468429d..734c0f84 100644 --- a/src/any.rs +++ b/src/any.rs @@ -16,7 +16,7 @@ use core::fmt::{self, Debug, Display, Formatter}; /// let mut target = String::new(); /// let mut record = Record::new(); /// -/// record.edit(&mut target, Any::new(Push('a'))); +/// record.edit(&mut target, Any::new(Add('a'))); /// record.edit(&mut target, Any::new(FromFn::new(|s: &mut String| s.push('b')))); /// assert_eq!(target, "ab"); /// # } diff --git a/src/doctest.rs b/src/doctest.rs index d9923bfe..42207de9 100644 --- a/src/doctest.rs +++ b/src/doctest.rs @@ -1,9 +1,9 @@ // This file is included in the documentation examples to avoid some boilerplate. /// This is the edit used in all the examples. -pub struct Push(char); +pub struct Add(char); -impl undo::Edit for Push { +impl undo::Edit for Add { type Target = String; type Output = (); diff --git a/src/history.rs b/src/history.rs index 525aaddd..36669066 100644 --- a/src/history.rs +++ b/src/history.rs @@ -31,14 +31,14 @@ use serde::{Deserialize, Serialize}; /// let mut target = String::new(); /// let mut history = History::new(); /// -/// history.edit(&mut target, Push('a')); -/// history.edit(&mut target, Push('b')); -/// history.edit(&mut target, Push('c')); +/// history.edit(&mut target, Add('a')); +/// history.edit(&mut target, Add('b')); +/// history.edit(&mut target, Add('c')); /// let abc = history.branch(); /// /// history.go_to(&mut target, abc, 1); -/// history.edit(&mut target, Push('f')); -/// history.edit(&mut target, Push('g')); +/// history.edit(&mut target, Add('f')); +/// history.edit(&mut target, Add('g')); /// assert_eq!(target, "afg"); /// /// history.go_to(&mut target, abc, 3); @@ -391,9 +391,9 @@ impl Branch { mod tests { use crate::*; - struct Push(char); + struct Add(char); - impl Edit for Push { + impl Edit for Add { type Target = String; type Output = (); @@ -425,44 +425,44 @@ mod tests { // a let mut target = String::new(); let mut history = History::new(); - history.edit(&mut target, Push('a')); - history.edit(&mut target, Push('b')); - history.edit(&mut target, Push('c')); - history.edit(&mut target, Push('d')); - history.edit(&mut target, Push('e')); + history.edit(&mut target, Add('a')); + history.edit(&mut target, Add('b')); + history.edit(&mut target, Add('c')); + history.edit(&mut target, Add('d')); + history.edit(&mut target, Add('e')); assert_eq!(target, "abcde"); history.undo(&mut target).unwrap(); history.undo(&mut target).unwrap(); assert_eq!(target, "abc"); let abcde = history.branch(); - history.edit(&mut target, Push('f')); - history.edit(&mut target, Push('g')); + history.edit(&mut target, Add('f')); + history.edit(&mut target, Add('g')); assert_eq!(target, "abcfg"); history.undo(&mut target).unwrap(); let abcfg = history.branch(); - history.edit(&mut target, Push('h')); - history.edit(&mut target, Push('i')); - history.edit(&mut target, Push('j')); + history.edit(&mut target, Add('h')); + history.edit(&mut target, Add('i')); + history.edit(&mut target, Add('j')); assert_eq!(target, "abcfhij"); history.undo(&mut target).unwrap(); let abcfhij = history.branch(); - history.edit(&mut target, Push('k')); + history.edit(&mut target, Add('k')); assert_eq!(target, "abcfhik"); history.undo(&mut target).unwrap(); let abcfhik = history.branch(); - history.edit(&mut target, Push('l')); + history.edit(&mut target, Add('l')); assert_eq!(target, "abcfhil"); - history.edit(&mut target, Push('m')); + history.edit(&mut target, Add('m')); assert_eq!(target, "abcfhilm"); let abcfhilm = history.branch(); history.go_to(&mut target, abcde, 2); - history.edit(&mut target, Push('n')); - history.edit(&mut target, Push('o')); + history.edit(&mut target, Add('n')); + history.edit(&mut target, Add('o')); assert_eq!(target, "abno"); history.undo(&mut target).unwrap(); let abno = history.branch(); - history.edit(&mut target, Push('p')); - history.edit(&mut target, Push('q')); + history.edit(&mut target, Add('p')); + history.edit(&mut target, Add('q')); assert_eq!(target, "abnpq"); let abnpq = history.branch(); diff --git a/src/history/builder.rs b/src/history/builder.rs index 7785ed6b..1fdf8db1 100644 --- a/src/history/builder.rs +++ b/src/history/builder.rs @@ -14,7 +14,7 @@ use crate::{History, Nop, Slot}; /// .capacity(100) /// .connect(|s| { dbg!(s); }) /// .build(); -/// # history.edit(&mut target, Push('a')); +/// # history.edit(&mut target, Add('a')); /// # } /// ``` #[derive(Debug)] diff --git a/src/history/queue.rs b/src/history/queue.rs index de5fef32..1e7e3db3 100644 --- a/src/history/queue.rs +++ b/src/history/queue.rs @@ -20,9 +20,9 @@ enum QueueEntry { /// let mut record = History::new(); /// let mut queue = record.queue(); /// -/// queue.edit(Push('a')); -/// queue.edit(Push('b')); -/// queue.edit(Push('c')); +/// queue.edit(Add('a')); +/// queue.edit(Add('b')); +/// queue.edit(Add('c')); /// assert_eq!(string, ""); /// /// queue.commit(&mut string); diff --git a/src/join.rs b/src/join.rs index 7d4aa5b7..5fe803e7 100644 --- a/src/join.rs +++ b/src/join.rs @@ -13,7 +13,7 @@ use core::fmt::{self, Display, Formatter}; /// let mut target = String::new(); /// let mut record = Record::new(); /// -/// let abc = Join::new(Push('a'), Push('b')).join(Push('c')); +/// let abc = Join::new(Add('a'), Add('b')).join(Add('c')); /// record.edit(&mut target, abc); /// assert_eq!(target, "abc"); /// record.undo(&mut target); diff --git a/src/record.rs b/src/record.rs index ef5d458b..fc1e55c5 100644 --- a/src/record.rs +++ b/src/record.rs @@ -35,9 +35,9 @@ use serde::{Deserialize, Serialize}; /// let mut target = String::new(); /// let mut record = Record::new(); /// -/// record.edit(&mut target, Push('a')); -/// record.edit(&mut target, Push('b')); -/// record.edit(&mut target, Push('c')); +/// record.edit(&mut target, Add('a')); +/// record.edit(&mut target, Add('b')); +/// record.edit(&mut target, Add('c')); /// assert_eq!(target, "abc"); /// /// record.undo(&mut target); @@ -354,26 +354,26 @@ mod tests { use alloc::string::String; use alloc::vec::Vec; - enum Editor { - Push(Push), - Pop(Pop), + enum Op { + Add(Add), + Del(Del), } - impl Edit for Editor { + impl Edit for Op { type Target = String; type Output = (); fn edit(&mut self, s: &mut String) { match self { - Editor::Push(add) => add.edit(s), - Editor::Pop(del) => del.edit(s), + Op::Add(add) => add.edit(s), + Op::Del(del) => del.edit(s), } } fn undo(&mut self, s: &mut String) { match self { - Editor::Push(add) => add.undo(s), - Editor::Pop(del) => del.undo(s), + Op::Add(add) => add.undo(s), + Op::Del(del) => del.undo(s), } } @@ -382,17 +382,17 @@ mod tests { Self: Sized, { match (self, edit) { - (Editor::Push(_), Editor::Pop(_)) => Merged::Annul, - (Editor::Pop(Pop(Some(a))), Editor::Push(Push(b))) if a == &b => Merged::Annul, + (Op::Add(_), Op::Del(_)) => Merged::Annul, + (Op::Del(Del(Some(a))), Op::Add(Add(b))) if a == &b => Merged::Annul, (_, edit) => Merged::No(edit), } } } #[derive(Debug, PartialEq)] - struct Push(char); + struct Add(char); - impl Edit for Push { + impl Edit for Add { type Target = String; type Output = (); @@ -406,9 +406,9 @@ mod tests { } #[derive(Default)] - struct Pop(Option); + struct Del(Option); - impl Edit for Pop { + impl Edit for Del { type Target = String; type Output = (); @@ -426,11 +426,11 @@ mod tests { fn go_to() { let mut target = String::new(); let mut record = Record::new(); - record.edit(&mut target, Push('a')); - record.edit(&mut target, Push('b')); - record.edit(&mut target, Push('c')); - record.edit(&mut target, Push('d')); - record.edit(&mut target, Push('e')); + record.edit(&mut target, Add('a')); + record.edit(&mut target, Add('b')); + record.edit(&mut target, Add('c')); + record.edit(&mut target, Add('d')); + record.edit(&mut target, Add('e')); record.go_to(&mut target, 0); assert_eq!(record.current(), 0); @@ -458,9 +458,9 @@ mod tests { fn annul() { let mut target = String::new(); let mut record = Record::new(); - record.edit(&mut target, Editor::Push(Push('a'))); - record.edit(&mut target, Editor::Pop(Pop::default())); - record.edit(&mut target, Editor::Push(Push('b'))); + record.edit(&mut target, Op::Add(Add('a'))); + record.edit(&mut target, Op::Del(Del::default())); + record.edit(&mut target, Op::Add(Add('b'))); assert_eq!(record.len(), 1); } @@ -468,9 +468,9 @@ mod tests { fn edits() { let mut target = String::new(); let mut record = Record::new(); - record.edit(&mut target, Push('a')); - record.edit(&mut target, Push('b')); + record.edit(&mut target, Add('a')); + record.edit(&mut target, Add('b')); let collected = record.edits().collect::>(); - assert_eq!(&collected[..], &[&Push('a'), &Push('b')][..]); + assert_eq!(&collected[..], &[&Add('a'), &Add('b')][..]); } } diff --git a/src/record/builder.rs b/src/record/builder.rs index 9bc5c324..81dbe751 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -17,7 +17,7 @@ use core::num::NonZeroUsize; /// .capacity(100) /// .connect(|s| { dbg!(s); }) /// .build(); -/// # record.edit(&mut target, Push('a')); +/// # record.edit(&mut target, Add('a')); /// # } /// ``` #[derive(Debug)] diff --git a/src/record/queue.rs b/src/record/queue.rs index aa202e5a..11567a22 100644 --- a/src/record/queue.rs +++ b/src/record/queue.rs @@ -20,9 +20,9 @@ enum QueueEntry { /// let mut record = Record::new(); /// let mut queue = record.queue(); /// -/// queue.edit(Push('a')); -/// queue.edit(Push('b')); -/// queue.edit(Push('c')); +/// queue.edit(Add('a')); +/// queue.edit(Add('b')); +/// queue.edit(Add('c')); /// assert_eq!(string, ""); /// /// queue.commit(&mut string); From 94abbe247cc413e02c16a4e8763cb6f6ae0ba101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Sun, 9 Apr 2023 17:49:43 +0200 Subject: [PATCH 03/33] Improve documentation --- src/any.rs | 10 +++++----- src/entry.rs | 2 +- src/from_fn.rs | 4 ++-- src/history.rs | 4 ++-- src/history/builder.rs | 20 ++++++++++---------- src/history/checkpoint.rs | 24 ++++++++++++------------ src/history/display.rs | 18 +++++++++--------- src/history/queue.rs | 26 +++++++++++++------------- src/join.rs | 8 ++++---- src/lib.rs | 8 ++++---- src/record.rs | 7 +++++-- src/record/builder.rs | 22 +++++++++++----------- src/record/checkpoint.rs | 30 +++++++++++++++--------------- src/record/display.rs | 16 ++++++++-------- src/record/queue.rs | 26 +++++++++++++------------- src/socket.rs | 2 +- 16 files changed, 115 insertions(+), 112 deletions(-) diff --git a/src/any.rs b/src/any.rs index 734c0f84..bd816caf 100644 --- a/src/any.rs +++ b/src/any.rs @@ -3,7 +3,7 @@ use alloc::boxed::Box; use alloc::string::String; use core::fmt::{self, Debug, Display, Formatter}; -/// Any edit type. +/// Any [`Edit`] command. /// /// This allows you to use multiple types of edits at the same time /// as long as they all share the same target and output type. @@ -27,11 +27,11 @@ pub struct Any { } impl Any { - /// Creates an `Any` from the provided edit. - pub fn new(edit: A) -> Any + /// Creates an [`Any`] from the provided edit. + pub fn new(edit: E) -> Any where - A: Edit, - A: 'static, + E: Edit, + E: 'static, { Any { edit: Box::new(edit), diff --git a/src/entry.rs b/src/entry.rs index 94016f80..29fb76d4 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "std")] use std::time::SystemTime; -/// Wrapper around an edit that contains additional metadata. +/// Wrapper around an [`Edit`] command that contains additional metadata. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Entry { diff --git a/src/from_fn.rs b/src/from_fn.rs index c041b3bc..4fce63e3 100644 --- a/src/from_fn.rs +++ b/src/from_fn.rs @@ -2,7 +2,7 @@ use crate::Edit; use core::fmt::{self, Debug, Formatter}; use core::mem; -/// An edit made from a function. +/// An [`Edit`] command made from a function. /// /// # Examples /// ``` @@ -72,7 +72,7 @@ impl Debug for FromFn { } } -/// An edit made from a fallible function. +/// An [`Edit`] command made from a fallible function. /// /// Same as [`FromFn`] but for functions that outputs [`Result`]. #[derive(Clone, Debug)] diff --git a/src/history.rs b/src/history.rs index 36669066..ea264d91 100644 --- a/src/history.rs +++ b/src/history.rs @@ -1,4 +1,4 @@ -//! A history tree of edits. +//! A history tree of edit commands. mod builder; mod checkpoint; @@ -18,7 +18,7 @@ use alloc::vec::Vec; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// A history tree of edits. +/// A history tree of [`Edit`] commands. /// /// Unlike [`Record`] which maintains a linear undo history, /// [`History`] maintains an undo tree containing every edit made to the target. diff --git a/src/history/builder.rs b/src/history/builder.rs index 1fdf8db1..98992717 100644 --- a/src/history/builder.rs +++ b/src/history/builder.rs @@ -18,16 +18,16 @@ use crate::{History, Nop, Slot}; /// # } /// ``` #[derive(Debug)] -pub struct Builder(RecordBuilder); +pub struct Builder(RecordBuilder); -impl Builder { +impl Builder { /// Returns a builder for a history. - pub fn new() -> Builder { + pub fn new() -> Builder { Builder(RecordBuilder::new()) } /// Sets the capacity for the history. - pub fn capacity(self, capacity: usize) -> Builder { + pub fn capacity(self, capacity: usize) -> Builder { Builder(self.0.capacity(capacity)) } @@ -35,30 +35,30 @@ impl Builder { /// /// # Panics /// Panics if `limit` is `0`. - pub fn limit(self, limit: usize) -> Builder { + pub fn limit(self, limit: usize) -> Builder { Builder(self.0.limit(limit)) } /// Sets if the target is initially in a saved state. /// By default the target is in a saved state. - pub fn saved(self, saved: bool) -> Builder { + pub fn saved(self, saved: bool) -> Builder { Builder(self.0.saved(saved)) } /// Builds the history. - pub fn build(self) -> History { + pub fn build(self) -> History { History::from(self.0.build()) } } -impl Builder { +impl Builder { /// Connects the slot. - pub fn connect(self, slot: S) -> Builder { + pub fn connect(self, slot: S) -> Builder { Builder(self.0.connect(slot)) } } -impl Default for Builder { +impl Default for Builder { fn default() -> Self { Builder::new() } diff --git a/src/history/checkpoint.rs b/src/history/checkpoint.rs index 4e12005a..fdd4a139 100644 --- a/src/history/checkpoint.rs +++ b/src/history/checkpoint.rs @@ -11,39 +11,39 @@ enum CheckpointEntry { /// Wraps a history and gives it checkpoint functionality. #[derive(Debug)] -pub struct Checkpoint<'a, A, S> { - history: &'a mut History, +pub struct Checkpoint<'a, E, S> { + history: &'a mut History, entries: Vec, } -impl Checkpoint<'_, A, S> { +impl Checkpoint<'_, E, S> { /// Returns a queue. - pub fn queue(&mut self) -> Queue { + pub fn queue(&mut self) -> Queue { self.history.queue() } /// Returns a checkpoint. - pub fn checkpoint(&mut self) -> Checkpoint { + pub fn checkpoint(&mut self) -> Checkpoint { self.history.checkpoint() } } -impl Checkpoint<'_, A, S> { +impl Checkpoint<'_, E, S> { /// Calls the [`History::edit`] method. - pub fn edit(&mut self, target: &mut A::Target, edit: A) -> A::Output { + pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { let branch = self.history.branch(); self.entries.push(CheckpointEntry::Edit(branch)); self.history.edit(target, edit) } /// Calls the [`History::undo`] method. - pub fn undo(&mut self, target: &mut A::Target) -> Option { + pub fn undo(&mut self, target: &mut E::Target) -> Option { self.entries.push(CheckpointEntry::Undo); self.history.undo(target) } /// Calls the [`History::redo`] method. - pub fn redo(&mut self, target: &mut A::Target) -> Option { + pub fn redo(&mut self, target: &mut E::Target) -> Option { self.entries.push(CheckpointEntry::Redo); self.history.redo(target) } @@ -52,7 +52,7 @@ impl Checkpoint<'_, A, S> { pub fn commit(self) {} /// Cancels the changes and consumes the checkpoint. - pub fn cancel(self, target: &mut A::Target) -> Vec { + pub fn cancel(self, target: &mut E::Target) -> Vec { self.entries .into_iter() .rev() @@ -75,8 +75,8 @@ impl Checkpoint<'_, A, S> { } } -impl<'a, A, S> From<&'a mut History> for Checkpoint<'a, A, S> { - fn from(history: &'a mut History) -> Self { +impl<'a, E, S> From<&'a mut History> for Checkpoint<'a, E, S> { + fn from(history: &'a mut History) -> Self { Checkpoint { history, entries: Vec::new(), diff --git a/src/history/display.rs b/src/history/display.rs index 1105452e..94126370 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -4,12 +4,12 @@ use core::fmt::{self, Write}; use std::time::SystemTime; /// Configurable display formatting for the history. -pub struct Display<'a, A, S> { - history: &'a History, +pub struct Display<'a, E, S> { + history: &'a History, format: Format, } -impl Display<'_, A, S> { +impl Display<'_, E, S> { /// Show colored output (on by default). /// /// Requires the `colored` feature to be enabled. @@ -44,12 +44,12 @@ impl Display<'_, A, S> { } } -impl Display<'_, A, S> { +impl Display<'_, E, S> { fn fmt_list( &self, f: &mut fmt::Formatter, at: At, - entry: Option<&Entry>, + entry: Option<&Entry>, level: usize, #[cfg(feature = "std")] now: SystemTime, ) -> fmt::Result { @@ -93,7 +93,7 @@ impl Display<'_, A, S> { &self, f: &mut fmt::Formatter, at: At, - entry: Option<&Entry>, + entry: Option<&Entry>, level: usize, #[cfg(feature = "std")] now: SystemTime, ) -> fmt::Result { @@ -140,8 +140,8 @@ impl Display<'_, A, S> { } } -impl<'a, A, S> From<&'a History> for Display<'a, A, S> { - fn from(history: &'a History) -> Self { +impl<'a, E, S> From<&'a History> for Display<'a, E, S> { + fn from(history: &'a History) -> Self { Display { history, format: Format::default(), @@ -149,7 +149,7 @@ impl<'a, A, S> From<&'a History> for Display<'a, A, S> { } } -impl fmt::Display for Display<'_, A, S> { +impl fmt::Display for Display<'_, E, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[cfg(feature = "std")] let now = SystemTime::now(); diff --git a/src/history/queue.rs b/src/history/queue.rs index 1e7e3db3..fda5d2e7 100644 --- a/src/history/queue.rs +++ b/src/history/queue.rs @@ -3,8 +3,8 @@ use crate::{Edit, History, Slot}; use alloc::vec::Vec; #[derive(Debug)] -enum QueueEntry { - Edit(A), +enum QueueEntry { + Edit(E), Undo, Redo, } @@ -30,26 +30,26 @@ enum QueueEntry { /// # } /// ``` #[derive(Debug)] -pub struct Queue<'a, A, S> { - history: &'a mut History, - entries: Vec>, +pub struct Queue<'a, E, S> { + history: &'a mut History, + entries: Vec>, } -impl Queue<'_, A, S> { +impl Queue<'_, E, S> { /// Returns a queue. - pub fn queue(&mut self) -> Queue { + pub fn queue(&mut self) -> Queue { self.history.queue() } /// Returns a checkpoint. - pub fn checkpoint(&mut self) -> Checkpoint { + pub fn checkpoint(&mut self) -> Checkpoint { self.history.checkpoint() } } -impl Queue<'_, A, S> { +impl Queue<'_, E, S> { /// Queues a [`History::edit`] call. - pub fn edit(&mut self, edit: A) { + pub fn edit(&mut self, edit: E) { self.entries.push(QueueEntry::Edit(edit)); } @@ -64,7 +64,7 @@ impl Queue<'_, A, S> { } /// Applies the queued edits. - pub fn commit(self, target: &mut A::Target) -> Vec { + pub fn commit(self, target: &mut E::Target) -> Vec { self.entries .into_iter() .filter_map(|entry| match entry { @@ -79,8 +79,8 @@ impl Queue<'_, A, S> { pub fn cancel(self) {} } -impl<'a, A, S> From<&'a mut History> for Queue<'a, A, S> { - fn from(history: &'a mut History) -> Self { +impl<'a, E, S> From<&'a mut History> for Queue<'a, E, S> { + fn from(history: &'a mut History) -> Self { Queue { history, entries: Vec::new(), diff --git a/src/join.rs b/src/join.rs index 5fe803e7..8bb2e102 100644 --- a/src/join.rs +++ b/src/join.rs @@ -1,7 +1,7 @@ use crate::Edit; use core::fmt::{self, Display, Formatter}; -/// Two edits joined together. +/// Two [`Edit`] commands joined together. /// /// Can be used to build more complex edits from simpler ones. /// @@ -29,7 +29,7 @@ pub struct Join { } impl Join { - /// Creates a new `Join` from `a` and `b`. + /// Creates a new [`Join`] from `a` and `b`. pub const fn new(a: A, b: B) -> Self { Join { a, b } } @@ -77,7 +77,7 @@ where } } -/// Joins two fallible edits together. +/// Two fallible [`Edit`] commands joined together. /// /// Same as [`Join`] but for edits that outputs [`Result`]. #[derive(Clone, Debug)] @@ -87,7 +87,7 @@ pub struct TryJoin { } impl TryJoin { - /// Creates a new `TryJoin` from `a` and `b`. + /// Creates a new [`TryJoin`] from `a` and `b`. pub const fn new(a: A, b: B) -> Self { TryJoin { a, b } } diff --git a/src/lib.rs b/src/lib.rs index 01e80ace..0315fa52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,7 +72,7 @@ pub use record::Record; #[cfg(feature = "alloc")] pub use socket::{Nop, Signal, Slot}; -/// Base functionality for all edits. +/// Base functionality for all edit commands. pub trait Edit { /// The target type. type Target; @@ -101,10 +101,10 @@ pub trait Edit { } } -/// Says if the edit have been merged with another edit. +/// Says if the [Edit] command have been merged with another command. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum Merged { +pub enum Merged { /// The edits have been merged. /// /// This means that the `other` edit will not be added to the stack. @@ -112,7 +112,7 @@ pub enum Merged { /// The edits have not been merged. /// /// We need to return the `other` edit so it can be added to the stack. - No(A), + No(E), /// The two edits cancels each other out. /// /// This means that both edits will be removed from the stack. diff --git a/src/record.rs b/src/record.rs index fc1e55c5..05875f36 100644 --- a/src/record.rs +++ b/src/record.rs @@ -1,4 +1,4 @@ -//! A linear record of edits. +//! A linear record of edit commands. mod builder; mod checkpoint; @@ -19,7 +19,7 @@ use core::num::NonZeroUsize; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// A linear record of edits. +/// A linear record of [`Edit`] commands. /// /// The record can roll the targets state backwards and forwards by using /// the undo and redo methods. In addition, the record can notify the user @@ -27,6 +27,9 @@ use serde::{Deserialize, Serialize}; /// The user can give the record a function that is called each time the state /// changes by using the [`Builder`]. /// +/// When adding a new edit command to the record the previously undone commands +/// will be discarded. If you want to keep all edits you can use [`History`] instead. +/// /// # Examples /// ``` /// # include!("doctest.rs"); diff --git a/src/record/builder.rs b/src/record/builder.rs index 81dbe751..cde0eca7 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -21,17 +21,17 @@ use core::num::NonZeroUsize; /// # } /// ``` #[derive(Debug)] -pub struct Builder { +pub struct Builder { capacity: usize, limit: NonZeroUsize, saved: bool, socket: Socket, - pd: PhantomData, + pd: PhantomData, } -impl Builder { +impl Builder { /// Returns a builder for a record. - pub fn new() -> Builder { + pub fn new() -> Builder { Builder { capacity: 0, limit: NonZeroUsize::new(usize::MAX).unwrap(), @@ -42,7 +42,7 @@ impl Builder { } /// Sets the capacity for the record. - pub fn capacity(mut self, capacity: usize) -> Builder { + pub fn capacity(mut self, capacity: usize) -> Builder { self.capacity = capacity; self } @@ -51,20 +51,20 @@ impl Builder { /// /// # Panics /// Panics if `limit` is `0`. - pub fn limit(mut self, limit: usize) -> Builder { + pub fn limit(mut self, limit: usize) -> Builder { self.limit = NonZeroUsize::new(limit).expect("limit can not be `0`"); self } /// Sets if the target is initially in a saved state. /// By default the target is in a saved state. - pub fn saved(mut self, saved: bool) -> Builder { + pub fn saved(mut self, saved: bool) -> Builder { self.saved = saved; self } /// Builds the record. - pub fn build(self) -> Record { + pub fn build(self) -> Record { Record { entries: VecDeque::with_capacity(self.capacity), limit: self.limit, @@ -75,15 +75,15 @@ impl Builder { } } -impl Builder { +impl Builder { /// Connects the slot. - pub fn connect(mut self, slot: S) -> Builder { + pub fn connect(mut self, slot: S) -> Builder { self.socket = Socket::new(slot); self } } -impl Default for Builder { +impl Default for Builder { fn default() -> Self { Builder::new() } diff --git a/src/record/checkpoint.rs b/src/record/checkpoint.rs index 8775dcc7..48ec2658 100644 --- a/src/record/checkpoint.rs +++ b/src/record/checkpoint.rs @@ -4,34 +4,34 @@ use alloc::collections::VecDeque; use alloc::vec::Vec; #[derive(Debug)] -enum CheckpointEntry { - Edit(Option, VecDeque>), +enum CheckpointEntry { + Edit(Option, VecDeque>), Undo, Redo, } /// Wraps a record and gives it checkpoint functionality. #[derive(Debug)] -pub struct Checkpoint<'a, A, S> { - record: &'a mut Record, - entries: Vec>, +pub struct Checkpoint<'a, E, S> { + record: &'a mut Record, + entries: Vec>, } -impl Checkpoint<'_, A, S> { +impl Checkpoint<'_, E, S> { /// Returns a queue. - pub fn queue(&mut self) -> Queue { + pub fn queue(&mut self) -> Queue { self.record.queue() } /// Returns a checkpoint. - pub fn checkpoint(&mut self) -> Checkpoint { + pub fn checkpoint(&mut self) -> Checkpoint { self.record.checkpoint() } } -impl Checkpoint<'_, A, S> { +impl Checkpoint<'_, E, S> { /// Calls the `apply` method. - pub fn edit(&mut self, target: &mut A::Target, edit: A) -> A::Output { + pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { let saved = self.record.saved; let (output, _, tail) = self.record.edit_inner(target, edit); self.entries.push(CheckpointEntry::Edit(saved, tail)); @@ -39,14 +39,14 @@ impl Checkpoint<'_, A, S> { } /// Calls the `undo` method. - pub fn undo(&mut self, target: &mut A::Target) -> Option { + pub fn undo(&mut self, target: &mut E::Target) -> Option { let output = self.record.undo(target)?; self.entries.push(CheckpointEntry::Undo); Some(output) } /// Calls the `redo` method. - pub fn redo(&mut self, target: &mut A::Target) -> Option { + pub fn redo(&mut self, target: &mut E::Target) -> Option { let output = self.record.redo(target)?; self.entries.push(CheckpointEntry::Redo); Some(output) @@ -56,7 +56,7 @@ impl Checkpoint<'_, A, S> { pub fn commit(self) {} /// Cancels the changes and consumes the checkpoint. - pub fn cancel(self, target: &mut A::Target) -> Vec { + pub fn cancel(self, target: &mut E::Target) -> Vec { self.entries .into_iter() .rev() @@ -75,8 +75,8 @@ impl Checkpoint<'_, A, S> { } } -impl<'a, A, S> From<&'a mut Record> for Checkpoint<'a, A, S> { - fn from(record: &'a mut Record) -> Self { +impl<'a, E, S> From<&'a mut Record> for Checkpoint<'a, E, S> { + fn from(record: &'a mut Record) -> Self { Checkpoint { record, entries: Vec::new(), diff --git a/src/record/display.rs b/src/record/display.rs index ba46c424..32cab25c 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -4,12 +4,12 @@ use core::fmt::{self, Write}; use std::time::SystemTime; /// Configurable display formatting for the record. -pub struct Display<'a, A, S> { - record: &'a Record, +pub struct Display<'a, E, S> { + record: &'a Record, format: Format, } -impl Display<'_, A, S> { +impl Display<'_, E, S> { /// Show colored output (on by default). /// /// Requires the `colored` feature to be enabled. @@ -44,12 +44,12 @@ impl Display<'_, A, S> { } } -impl Display<'_, A, S> { +impl Display<'_, E, S> { fn fmt_list( &self, f: &mut fmt::Formatter, current: usize, - entry: Option<&Entry>, + entry: Option<&Entry>, #[cfg(feature = "std")] now: SystemTime, ) -> fmt::Result { let at = At::root(current); @@ -85,8 +85,8 @@ impl Display<'_, A, S> { } } -impl<'a, A, S> From<&'a Record> for Display<'a, A, S> { - fn from(record: &'a Record) -> Self { +impl<'a, E, S> From<&'a Record> for Display<'a, E, S> { + fn from(record: &'a Record) -> Self { Display { record, format: Format::default(), @@ -94,7 +94,7 @@ impl<'a, A, S> From<&'a Record> for Display<'a, A, S> { } } -impl fmt::Display for Display<'_, A, S> { +impl fmt::Display for Display<'_, E, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[cfg(feature = "std")] let now = SystemTime::now(); diff --git a/src/record/queue.rs b/src/record/queue.rs index 11567a22..c1354a60 100644 --- a/src/record/queue.rs +++ b/src/record/queue.rs @@ -3,8 +3,8 @@ use crate::{Edit, Record, Slot}; use alloc::vec::Vec; #[derive(Debug)] -enum QueueEntry { - Edit(A), +enum QueueEntry { + Edit(E), Undo, Redo, } @@ -30,26 +30,26 @@ enum QueueEntry { /// # } /// ``` #[derive(Debug)] -pub struct Queue<'a, A, S> { - record: &'a mut Record, - entries: Vec>, +pub struct Queue<'a, E, S> { + record: &'a mut Record, + entries: Vec>, } -impl Queue<'_, A, S> { +impl Queue<'_, E, S> { /// Returns a queue. - pub fn queue(&mut self) -> Queue { + pub fn queue(&mut self) -> Queue { self.record.queue() } /// Returns a checkpoint. - pub fn checkpoint(&mut self) -> Checkpoint { + pub fn checkpoint(&mut self) -> Checkpoint { self.record.checkpoint() } } -impl Queue<'_, A, S> { +impl Queue<'_, E, S> { /// Queues a [`Record::edit`] call. - pub fn edit(&mut self, edit: A) { + pub fn edit(&mut self, edit: E) { self.entries.push(QueueEntry::Edit(edit)); } @@ -64,7 +64,7 @@ impl Queue<'_, A, S> { } /// Applies the queued edits. - pub fn commit(self, target: &mut A::Target) -> Vec { + pub fn commit(self, target: &mut E::Target) -> Vec { self.entries .into_iter() .filter_map(|entry| match entry { @@ -79,8 +79,8 @@ impl Queue<'_, A, S> { pub fn cancel(self) {} } -impl<'a, A, S> From<&'a mut Record> for Queue<'a, A, S> { - fn from(record: &'a mut Record) -> Self { +impl<'a, E, S> From<&'a mut Record> for Queue<'a, E, S> { + fn from(record: &'a mut Record) -> Self { Queue { record, entries: Vec::new(), diff --git a/src/socket.rs b/src/socket.rs index 3d564c37..979e425f 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -70,7 +70,7 @@ pub enum Signal { Saved(bool), } -/// Default slot that does nothing. +/// Default [`Slot`] that does nothing. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct Nop; From 7078c1f04d40d88aaa17267ee744fcf987c12e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Sun, 9 Apr 2023 20:17:03 +0200 Subject: [PATCH 04/33] Add more links in documentation --- src/history/builder.rs | 2 +- src/history/checkpoint.rs | 2 +- src/history/display.rs | 2 +- src/history/queue.rs | 2 +- src/lib.rs | 2 +- src/record/builder.rs | 2 +- src/record/checkpoint.rs | 2 +- src/record/display.rs | 2 +- src/record/queue.rs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/history/builder.rs b/src/history/builder.rs index 98992717..8104694b 100644 --- a/src/history/builder.rs +++ b/src/history/builder.rs @@ -1,7 +1,7 @@ use crate::record::Builder as RecordBuilder; use crate::{History, Nop, Slot}; -/// Builder for a History. +/// Builder for a [`History`]. /// /// # Examples /// ``` diff --git a/src/history/checkpoint.rs b/src/history/checkpoint.rs index fdd4a139..fb7f1494 100644 --- a/src/history/checkpoint.rs +++ b/src/history/checkpoint.rs @@ -9,7 +9,7 @@ enum CheckpointEntry { Redo, } -/// Wraps a history and gives it checkpoint functionality. +/// Wraps a [`History`] and gives it checkpoint functionality. #[derive(Debug)] pub struct Checkpoint<'a, E, S> { history: &'a mut History, diff --git a/src/history/display.rs b/src/history/display.rs index 94126370..8e557e19 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -3,7 +3,7 @@ use core::fmt::{self, Write}; #[cfg(feature = "std")] use std::time::SystemTime; -/// Configurable display formatting for the history. +/// Configurable display formatting for the [`History`]. pub struct Display<'a, E, S> { history: &'a History, format: Format, diff --git a/src/history/queue.rs b/src/history/queue.rs index fda5d2e7..30b7e142 100644 --- a/src/history/queue.rs +++ b/src/history/queue.rs @@ -9,7 +9,7 @@ enum QueueEntry { Redo, } -/// Wraps a history and gives it batch queue functionality. +/// Wraps a [`History`] and gives it batch queue functionality. /// /// # Examples /// ``` diff --git a/src/lib.rs b/src/lib.rs index 0315fa52..58a80550 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,7 +101,7 @@ pub trait Edit { } } -/// Says if the [Edit] command have been merged with another command. +/// Says if the [`Edit`] command have been merged with another command. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub enum Merged { diff --git a/src/record/builder.rs b/src/record/builder.rs index cde0eca7..5227a77f 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -4,7 +4,7 @@ use alloc::collections::VecDeque; use core::marker::PhantomData; use core::num::NonZeroUsize; -/// Builder for a record. +/// Builder for a [`Record`]. /// /// # Examples /// ``` diff --git a/src/record/checkpoint.rs b/src/record/checkpoint.rs index 48ec2658..e67723f6 100644 --- a/src/record/checkpoint.rs +++ b/src/record/checkpoint.rs @@ -10,7 +10,7 @@ enum CheckpointEntry { Redo, } -/// Wraps a record and gives it checkpoint functionality. +/// Wraps a [`Record`] and gives it checkpoint functionality. #[derive(Debug)] pub struct Checkpoint<'a, E, S> { record: &'a mut Record, diff --git a/src/record/display.rs b/src/record/display.rs index 32cab25c..e94be59e 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -3,7 +3,7 @@ use core::fmt::{self, Write}; #[cfg(feature = "std")] use std::time::SystemTime; -/// Configurable display formatting for the record. +/// Configurable display formatting for the [`Record`]. pub struct Display<'a, E, S> { record: &'a Record, format: Format, diff --git a/src/record/queue.rs b/src/record/queue.rs index c1354a60..1b229672 100644 --- a/src/record/queue.rs +++ b/src/record/queue.rs @@ -9,7 +9,7 @@ enum QueueEntry { Redo, } -/// Wraps a record and gives it batch queue functionality. +/// Wraps a [`Record`] and gives it batch queue functionality. /// /// # Examples /// ``` From 01efcd84fa37add46fc9ddd3213f2f6487703c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Tue, 11 Apr 2023 09:30:43 +0200 Subject: [PATCH 05/33] Make emit methods lazy --- src/history.rs | 4 ++-- src/record.rs | 32 ++++++++++++++++---------------- src/socket.rs | 8 ++++---- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/history.rs b/src/history.rs index ea264d91..cd147f91 100644 --- a/src/history.rs +++ b/src/history.rs @@ -254,11 +254,11 @@ impl History { { self.saved = None; self.record.saved = Some(saved); - self.record.socket.emit(Signal::Saved(true)); + self.record.socket.emit(|| Signal::Saved(true)); } else if let Some(saved) = self.record.saved { self.saved = Some(At::new(old, saved)); self.record.saved = None; - self.record.socket.emit(Signal::Saved(false)); + self.record.socket.emit(|| Signal::Saved(false)); } } diff --git a/src/record.rs b/src/record.rs index 05875f36..eccea765 100644 --- a/src/record.rs +++ b/src/record.rs @@ -210,9 +210,9 @@ impl Record { } }; - self.socket.emit_if(could_redo, Signal::Redo(false)); - self.socket.emit_if(!could_undo, Signal::Undo(true)); - self.socket.emit_if(was_saved, Signal::Saved(false)); + self.socket.emit_if(could_redo, || Signal::Redo(false)); + self.socket.emit_if(!could_undo, || Signal::Undo(true)); + self.socket.emit_if(was_saved, || Signal::Saved(false)); (output, merged_or_annulled, tail) } @@ -226,10 +226,10 @@ impl Record { self.current -= 1; let is_saved = self.is_saved(); self.socket - .emit_if(old == self.entries.len(), Signal::Redo(true)); - self.socket.emit_if(old == 1, Signal::Undo(false)); + .emit_if(old == self.entries.len(), || Signal::Redo(true)); + self.socket.emit_if(old == 1, || Signal::Undo(false)); self.socket - .emit_if(was_saved != is_saved, Signal::Saved(is_saved)); + .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); output }) } @@ -244,10 +244,10 @@ impl Record { self.current += 1; let is_saved = self.is_saved(); self.socket - .emit_if(old == self.len() - 1, Signal::Redo(false)); - self.socket.emit_if(old == 0, Signal::Undo(true)); + .emit_if(old == self.len() - 1, || Signal::Redo(false)); + self.socket.emit_if(old == 0, || Signal::Undo(true)); self.socket - .emit_if(was_saved != is_saved, Signal::Saved(is_saved)); + .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); output }) } @@ -257,10 +257,10 @@ impl Record { let was_saved = self.is_saved(); if saved { self.saved = Some(self.current); - self.socket.emit_if(!was_saved, Signal::Saved(true)); + self.socket.emit_if(!was_saved, || Signal::Saved(true)); } else { self.saved = None; - self.socket.emit_if(was_saved, Signal::Saved(false)); + self.socket.emit_if(was_saved, || Signal::Saved(false)); } } @@ -271,8 +271,8 @@ impl Record { self.entries.clear(); self.saved = self.is_saved().then_some(0); self.current = 0; - self.socket.emit_if(could_undo, Signal::Undo(false)); - self.socket.emit_if(could_redo, Signal::Redo(false)); + self.socket.emit_if(could_undo, || Signal::Undo(false)); + self.socket.emit_if(could_redo, || Signal::Redo(false)); } /// Revert the changes done to the target since the saved state. @@ -311,11 +311,11 @@ impl Record { let is_saved = self.is_saved(); self.socket.connect(slot); self.socket - .emit_if(could_undo != can_undo, Signal::Undo(can_undo)); + .emit_if(could_undo != can_undo, || Signal::Undo(can_undo)); self.socket - .emit_if(could_redo != can_redo, Signal::Redo(can_redo)); + .emit_if(could_redo != can_redo, || Signal::Redo(can_redo)); self.socket - .emit_if(was_saved != is_saved, Signal::Saved(is_saved)); + .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); outputs } diff --git a/src/socket.rs b/src/socket.rs index 979e425f..48fe398c 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -30,13 +30,13 @@ impl Default for Socket { } impl Socket { - pub fn emit(&mut self, signal: Signal) { + pub fn emit(&mut self, signal: impl FnOnce() -> Signal) { if let Some(slot) = &mut self.0 { - slot.emit(signal); + slot.emit(signal()); } } - pub fn emit_if(&mut self, cond: bool, signal: Signal) { + pub fn emit_if(&mut self, cond: bool, signal: impl FnOnce() -> Signal) { if cond { self.emit(signal); } @@ -59,7 +59,7 @@ impl Slot for F { /// /// For example, if the history tree can no longer redo any edits, /// it sends a `Redo(false)` signal to tell the user. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, PartialEq)] #[non_exhaustive] pub enum Signal { /// Says if the structures can undo. From 4856e903a65493939aa7a34231e50c66d05e5953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Wed, 12 Apr 2023 10:42:19 +0200 Subject: [PATCH 06/33] Hide new method from builder --- src/any.rs | 3 ++- src/format.rs | 8 ++++---- src/from_fn.rs | 22 +++++++++++++++++++--- src/history/builder.rs | 2 +- src/join.rs | 2 +- src/record/builder.rs | 2 +- 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/any.rs b/src/any.rs index bd816caf..ac7d5808 100644 --- a/src/any.rs +++ b/src/any.rs @@ -18,7 +18,8 @@ use core::fmt::{self, Debug, Display, Formatter}; /// /// record.edit(&mut target, Any::new(Add('a'))); /// record.edit(&mut target, Any::new(FromFn::new(|s: &mut String| s.push('b')))); -/// assert_eq!(target, "ab"); +/// record.edit(&mut target, Any::new(FromFn::new(|s: &mut String| s.push('c')))); +/// assert_eq!(target, "abc"); /// # } /// ``` pub struct Any { diff --git a/src/format.rs b/src/format.rs index 4a3311a9..4821d275 100644 --- a/src/format.rs +++ b/src/format.rs @@ -179,18 +179,18 @@ impl Format { } #[cfg(feature = "std")] - pub fn text(self, f: &mut fmt::Formatter, text: &str, i: usize) -> fmt::Result { + pub fn text(self, f: &mut fmt::Formatter, text: &str, level: usize) -> fmt::Result { #[cfg(feature = "colored")] if self.colored { - return write!(f, "{}", text.color(color_of_level(i))); + return write!(f, "{}", text.color(color_of_level(level))); } f.write_str(text) } } #[cfg(feature = "colored")] -fn color_of_level(i: usize) -> Color { - match i % 6 { +fn color_of_level(level: usize) -> Color { + match level % 6 { 0 => Color::Cyan, 1 => Color::Red, 2 => Color::Magenta, diff --git a/src/from_fn.rs b/src/from_fn.rs index 4fce63e3..89d890ce 100644 --- a/src/from_fn.rs +++ b/src/from_fn.rs @@ -66,16 +66,21 @@ where } } -impl Debug for FromFn { +impl Debug for FromFn +where + T: Debug, +{ fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("FromFn").finish_non_exhaustive() + f.debug_struct("FromFn") + .field("target", &self.target) + .finish_non_exhaustive() } } /// An [`Edit`] command made from a fallible function. /// /// Same as [`FromFn`] but for functions that outputs [`Result`]. -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct TryFromFn { f: F, target: Option, @@ -115,3 +120,14 @@ where Ok(()) } } + +impl Debug for TryFromFn +where + T: Debug, +{ + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("TryFromFn") + .field("target", &self.target) + .finish_non_exhaustive() + } +} diff --git a/src/history/builder.rs b/src/history/builder.rs index 8104694b..1e9b3a56 100644 --- a/src/history/builder.rs +++ b/src/history/builder.rs @@ -22,7 +22,7 @@ pub struct Builder(RecordBuilder); impl Builder { /// Returns a builder for a history. - pub fn new() -> Builder { + pub(crate) fn new() -> Builder { Builder(RecordBuilder::new()) } diff --git a/src/join.rs b/src/join.rs index 8bb2e102..9da19269 100644 --- a/src/join.rs +++ b/src/join.rs @@ -3,7 +3,7 @@ use core::fmt::{self, Display, Formatter}; /// Two [`Edit`] commands joined together. /// -/// Can be used to build more complex edits from simpler ones. +/// Can be used to build more complex edit commands from simpler ones. /// /// # Examples /// ``` diff --git a/src/record/builder.rs b/src/record/builder.rs index 5227a77f..6eeff7e0 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -31,7 +31,7 @@ pub struct Builder { impl Builder { /// Returns a builder for a record. - pub fn new() -> Builder { + pub(crate) fn new() -> Builder { Builder { capacity: 0, limit: NonZeroUsize::new(usize::MAX).unwrap(), From eaed6d9b0d0bf2ba19236e5927f593f84e40d558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Wed, 12 Apr 2023 19:20:01 +0200 Subject: [PATCH 07/33] Add updated_now method to entry --- src/entry.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/entry.rs b/src/entry.rs index 29fb76d4..e1e633ed 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -19,19 +19,20 @@ pub struct Entry { impl Entry { pub fn undo(&mut self, target: &mut E::Target) -> E::Output { #[cfg(feature = "std")] - { - self.updated_at = SystemTime::now(); - } + self.updated_now(); self.edit.undo(target) } pub fn redo(&mut self, target: &mut E::Target) -> E::Output { #[cfg(feature = "std")] - { - self.updated_at = SystemTime::now(); - } + self.updated_now(); self.edit.redo(target) } + + #[cfg(feature = "std")] + fn updated_now(&mut self) { + self.updated_at = SystemTime::now(); + } } impl From for Entry { From f78a4c163dae0e4eafd4fd3481a88c03ba092d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Thu, 13 Apr 2023 16:38:45 +0200 Subject: [PATCH 08/33] Add more documentation --- README.md | 10 ++++++---- src/lib.rs | 12 +++++++----- src/record.rs | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 5d5cc58d..981e04bd 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,12 @@ [![Crates.io](https://img.shields.io/crates/v/undo.svg)](https://crates.io/crates/undo) [![Docs](https://docs.rs/undo/badge.svg)](https://docs.rs/undo) -It is an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), -where all modifications are done by creating objects that applies the modifications. -All objects knows how to undo the changes it applies, and by using the provided data -structures it is easy to undo and redo edits made to a target. +> It is an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), +> where all edits are done by creating objects that applies the modifications. +> All objects knows how to undo the changes it applies, and by using the provided data +> structures it is easy to undo and redo edits made to a target. + +See the [documentation](https://docs.rs/undo) and [examples](https://github.com/evenorog/undo/tree/master/examples) for more information. ## Examples diff --git a/src/lib.rs b/src/lib.rs index 58a80550..8575ac95 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,11 @@ //! **An undo-redo library.** //! -//! It is an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), -//! where all modifications are done by creating objects that applies the modifications. -//! All objects knows how to undo the changes it applies, and by using the provided data -//! structures it is easy to undo and redo edits made to a target. +//! > It is an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), +//! > where all edits are done by creating objects that applies the modifications. +//! > All objects knows how to undo the changes it applies, and by using the provided data +//! > structures it is easy to undo and redo edits made to a target. +//! +//! See the [examples](https://github.com/evenorog/undo/tree/master/examples) for more information. //! //! # Features //! @@ -79,7 +81,7 @@ pub trait Edit { /// The output type. type Output; - /// Applies the edit on the target. + /// Applies the edit command on the target. fn edit(&mut self, target: &mut Self::Target) -> Self::Output; /// Restores the state of the target as it was before the edit was applied. diff --git a/src/record.rs b/src/record.rs index eccea765..10a91149 100644 --- a/src/record.rs +++ b/src/record.rs @@ -198,7 +198,7 @@ impl Record { } // If edits are not merged or annulled push it onto the storage. Merged::No(edit) => { - // If limit is reached, pop off the first edit. + // If limit is reached, pop off the first edit command. if self.limit() == self.current { self.entries.pop_front(); self.saved = self.saved.and_then(|saved| saved.checked_sub(1)); From bc509691d37c405aa23f786c50dfddf92e388630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Thu, 13 Apr 2023 20:32:34 +0200 Subject: [PATCH 09/33] Remove Try helper types --- README.md | 2 +- src/from_fn.rs | 59 +++------------------------------------- src/history.rs | 9 +++++-- src/history/builder.rs | 23 ++++++---------- src/join.rs | 61 ++---------------------------------------- src/lib.rs | 10 +++---- src/record.rs | 9 ++++--- src/record/builder.rs | 37 +++++++++++-------------- 8 files changed, 48 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index 981e04bd..b359cb99 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Crates.io](https://img.shields.io/crates/v/undo.svg)](https://crates.io/crates/undo) [![Docs](https://docs.rs/undo/badge.svg)](https://docs.rs/undo) -> It is an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), +> An implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), > where all edits are done by creating objects that applies the modifications. > All objects knows how to undo the changes it applies, and by using the provided data > structures it is easy to undo and redo edits made to a target. diff --git a/src/from_fn.rs b/src/from_fn.rs index 89d890ce..cfb71a11 100644 --- a/src/from_fn.rs +++ b/src/from_fn.rs @@ -4,6 +4,10 @@ use core::mem; /// An [`Edit`] command made from a function. /// +/// This is a convenient way to make simple edits without having to +/// create a new type for each one. But for more complex edits it is +/// probably better to create a custom edit command. +/// /// # Examples /// ``` /// # include!("doctest.rs"); @@ -76,58 +80,3 @@ where .finish_non_exhaustive() } } - -/// An [`Edit`] command made from a fallible function. -/// -/// Same as [`FromFn`] but for functions that outputs [`Result`]. -#[derive(Clone)] -pub struct TryFromFn { - f: F, - target: Option, -} - -impl TryFromFn { - /// Creates a new `TryFromFn` from `f`. - pub const fn new(f: F) -> Self { - TryFromFn { f, target: None } - } -} - -impl Edit for TryFromFn -where - F: FnMut(&mut T) -> Result<(), E>, - T: Clone, -{ - type Target = T; - type Output = Result<(), E>; - - fn edit(&mut self, target: &mut Self::Target) -> Self::Output { - self.target = Some(target.clone()); - (self.f)(target) - } - - fn undo(&mut self, target: &mut Self::Target) -> Self::Output { - if let Some(old) = self.target.as_mut() { - mem::swap(old, target); - } - Ok(()) - } - - fn redo(&mut self, target: &mut Self::Target) -> Self::Output { - if let Some(new) = self.target.as_mut() { - mem::swap(new, target); - } - Ok(()) - } -} - -impl Debug for TryFromFn -where - T: Debug, -{ - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("TryFromFn") - .field("target", &self.target) - .finish_non_exhaustive() - } -} diff --git a/src/history.rs b/src/history.rs index cd147f91..381f768d 100644 --- a/src/history.rs +++ b/src/history.rs @@ -36,11 +36,16 @@ use serde::{Deserialize, Serialize}; /// history.edit(&mut target, Add('c')); /// let abc = history.branch(); /// -/// history.go_to(&mut target, abc, 1); +/// history.undo(&mut target); +/// history.undo(&mut target); +/// assert_eq!(target, "a"); +/// +/// // Instead of discarding 'b' and 'c', a new branch is created. /// history.edit(&mut target, Add('f')); /// history.edit(&mut target, Add('g')); /// assert_eq!(target, "afg"); /// +/// // We can now switch back to the original branch. /// history.go_to(&mut target, abc, 3); /// assert_eq!(target, "abc"); /// # } @@ -65,7 +70,7 @@ impl History { impl History { /// Returns a new history builder. pub fn builder() -> Builder { - Builder::new() + Builder::default() } /// Reserves capacity for at least `additional` more edits. diff --git a/src/history/builder.rs b/src/history/builder.rs index 1e9b3a56..8162b7b3 100644 --- a/src/history/builder.rs +++ b/src/history/builder.rs @@ -1,5 +1,5 @@ use crate::record::Builder as RecordBuilder; -use crate::{History, Nop, Slot}; +use crate::{History, Nop}; /// Builder for a [`History`]. /// @@ -21,11 +21,6 @@ use crate::{History, Nop, Slot}; pub struct Builder(RecordBuilder); impl Builder { - /// Returns a builder for a history. - pub(crate) fn new() -> Builder { - Builder(RecordBuilder::new()) - } - /// Sets the capacity for the history. pub fn capacity(self, capacity: usize) -> Builder { Builder(self.0.capacity(capacity)) @@ -45,21 +40,19 @@ impl Builder { Builder(self.0.saved(saved)) } - /// Builds the history. - pub fn build(self) -> History { - History::from(self.0.build()) - } -} - -impl Builder { /// Connects the slot. pub fn connect(self, slot: S) -> Builder { Builder(self.0.connect(slot)) } + + /// Builds the history. + pub fn build(self) -> History { + History::from(self.0.build()) + } } -impl Default for Builder { +impl Default for Builder { fn default() -> Self { - Builder::new() + Builder(RecordBuilder::default()) } } diff --git a/src/join.rs b/src/join.rs index 9da19269..bc8dd973 100644 --- a/src/join.rs +++ b/src/join.rs @@ -3,7 +3,8 @@ use core::fmt::{self, Display, Formatter}; /// Two [`Edit`] commands joined together. /// -/// Can be used to build more complex edit commands from simpler ones. +/// This is a convenient way to build more complex edit commands from simpler ones, +/// but for more complex edits it is probably better to create a custom edit command. /// /// # Examples /// ``` @@ -76,61 +77,3 @@ where write!(f, "{} & {}", self.a, self.b) } } - -/// Two fallible [`Edit`] commands joined together. -/// -/// Same as [`Join`] but for edits that outputs [`Result`]. -#[derive(Clone, Debug)] -pub struct TryJoin { - a: A, - b: B, -} - -impl TryJoin { - /// Creates a new [`TryJoin`] from `a` and `b`. - pub const fn new(a: A, b: B) -> Self { - TryJoin { a, b } - } - - /// Joins `self` with `c`. - pub const fn join(self, c: C) -> TryJoin { - TryJoin::new(self, c) - } -} - -impl Edit for TryJoin -where - A: Edit>, - B: Edit, -{ - type Target = A::Target; - type Output = A::Output; - - /// The output of a will be discarded if success. - fn edit(&mut self, target: &mut A::Target) -> Self::Output { - self.a.edit(target)?; - self.b.edit(target) - } - - /// The output of b will be discarded if success. - fn undo(&mut self, target: &mut A::Target) -> Self::Output { - self.b.undo(target)?; - self.a.undo(target) - } - - /// The output of a will be discarded if success. - fn redo(&mut self, target: &mut A::Target) -> Self::Output { - self.a.redo(target)?; - self.b.redo(target) - } -} - -impl Display for TryJoin -where - A: Display, - B: Display, -{ - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{} & {}", self.a, self.b) - } -} diff --git a/src/lib.rs b/src/lib.rs index 8575ac95..4e83df80 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,6 @@ //! **An undo-redo library.** //! -//! > It is an implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), +//! > An implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), //! > where all edits are done by creating objects that applies the modifications. //! > All objects knows how to undo the changes it applies, and by using the provided data //! > structures it is easy to undo and redo edits made to a target. @@ -9,8 +9,8 @@ //! //! # Features //! -//! * [`Edit`] provides the base functionality for all edit commands. Multiple [`Edit`]s can be merged into a single edit -//! by implementing the [`Edit::merge`] method on the edit. This allows smaller edits to be used to build +//! * [`Edit`] provides the base functionality for all edit commands. Multiple edit commands can be merged into a single edit +//! by implementing the [`merge`](Edit::merge) method on the edit. This allows smaller edits to be used to build //! more complex operations, or smaller incremental changes to be merged into larger changes that can be undone and //! redone in a single step. //! * [`Record`] provides basic stack based undo-redo functionality. @@ -65,10 +65,10 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "alloc")] pub use any::Any; -pub use from_fn::{FromFn, TryFromFn}; +pub use from_fn::FromFn; #[cfg(feature = "alloc")] pub use history::History; -pub use join::{Join, TryJoin}; +pub use join::Join; #[cfg(feature = "alloc")] pub use record::Record; #[cfg(feature = "alloc")] diff --git a/src/record.rs b/src/record.rs index 10a91149..fb90865e 100644 --- a/src/record.rs +++ b/src/record.rs @@ -50,8 +50,11 @@ use serde::{Deserialize, Serialize}; /// /// record.redo(&mut target); /// record.redo(&mut target); -/// record.redo(&mut target); -/// assert_eq!(target, "abc"); +/// assert_eq!(target, "ab"); +/// +/// // 'c' will be discarded. +/// record.edit(&mut target, Add('d')); +/// assert_eq!(target, "abd"); /// # } /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -74,7 +77,7 @@ impl Record { impl Record { /// Returns a new record builder. pub fn builder() -> Builder { - Builder::new() + Builder::default() } /// Reserves capacity for at least `additional` more edits. diff --git a/src/record/builder.rs b/src/record/builder.rs index 6eeff7e0..66f686ac 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -1,5 +1,5 @@ use super::Socket; -use crate::{Nop, Record, Slot}; +use crate::{Nop, Record}; use alloc::collections::VecDeque; use core::marker::PhantomData; use core::num::NonZeroUsize; @@ -30,17 +30,6 @@ pub struct Builder { } impl Builder { - /// Returns a builder for a record. - pub(crate) fn new() -> Builder { - Builder { - capacity: 0, - limit: NonZeroUsize::new(usize::MAX).unwrap(), - saved: true, - socket: Socket::default(), - pd: PhantomData, - } - } - /// Sets the capacity for the record. pub fn capacity(mut self, capacity: usize) -> Builder { self.capacity = capacity; @@ -63,6 +52,12 @@ impl Builder { self } + /// Connects the slot. + pub fn connect(mut self, slot: S) -> Builder { + self.socket = Socket::new(slot); + self + } + /// Builds the record. pub fn build(self) -> Record { Record { @@ -75,16 +70,14 @@ impl Builder { } } -impl Builder { - /// Connects the slot. - pub fn connect(mut self, slot: S) -> Builder { - self.socket = Socket::new(slot); - self - } -} - -impl Default for Builder { +impl Default for Builder { fn default() -> Self { - Builder::new() + Builder { + capacity: 0, + limit: NonZeroUsize::new(usize::MAX).unwrap(), + saved: true, + socket: Socket::default(), + pd: PhantomData, + } } } From 7f9c912529e7f72ad8bda5e26e2956e3dd0b0e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Fri, 14 Apr 2023 22:34:33 +0200 Subject: [PATCH 10/33] Rename emit on Slot to on_emit --- src/record.rs | 4 ++-- src/socket.rs | 43 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/record.rs b/src/record.rs index fb90865e..04342170 100644 --- a/src/record.rs +++ b/src/record.rs @@ -228,9 +228,9 @@ impl Record { let output = self.entries[self.current - 1].undo(target); self.current -= 1; let is_saved = self.is_saved(); + self.socket.emit_if(old == 1, || Signal::Undo(false)); self.socket .emit_if(old == self.entries.len(), || Signal::Redo(true)); - self.socket.emit_if(old == 1, || Signal::Undo(false)); self.socket .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); output @@ -246,9 +246,9 @@ impl Record { let output = self.entries[self.current].redo(target); self.current += 1; let is_saved = self.is_saved(); + self.socket.emit_if(old == 0, || Signal::Undo(true)); self.socket .emit_if(old == self.len() - 1, || Signal::Redo(false)); - self.socket.emit_if(old == 0, || Signal::Undo(true)); self.socket .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); output diff --git a/src/socket.rs b/src/socket.rs index 48fe398c..aae9d993 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -32,7 +32,7 @@ impl Default for Socket { impl Socket { pub fn emit(&mut self, signal: impl FnOnce() -> Signal) { if let Some(slot) = &mut self.0 { - slot.emit(signal()); + slot.on_emit(signal()); } } @@ -43,22 +43,49 @@ impl Socket { } } -/// Use this to receive signals from [`History`](crate::History) or [`Record`](crate::Record). +/// Use this to handle signals emitted. +/// +/// This allows you to trigger events on certain state changes. +/// +/// # Examples +/// ``` +/// # include!("doctest.rs"); +/// # use std::sync::mpsc; +/// # use undo::{FromFn, Record, Signal}; +/// # fn main() { +/// let (sender, receiver) = mpsc::channel(); +/// let mut iter = receiver.try_iter(); +/// +/// let mut target = String::new(); +/// let mut record = Record::builder() +/// .connect(|s| sender.send(s).unwrap()) +/// .build(); +/// +/// record.edit(&mut target, Add('a')); +/// assert_eq!(iter.next(), Some(Signal::Undo(true))); +/// assert_eq!(iter.next(), Some(Signal::Saved(false))); +/// +/// record.undo(&mut target); +/// assert_eq!(iter.next(), Some(Signal::Undo(false))); +/// assert_eq!(iter.next(), Some(Signal::Redo(true))); +/// assert_eq!(iter.next(), Some(Signal::Saved(true))); +/// assert_eq!(iter.next(), None); +/// # } +/// ``` pub trait Slot { /// Receives a signal that describes the state change done to the data structures. - fn emit(&mut self, signal: Signal); + fn on_emit(&mut self, signal: Signal); } impl Slot for F { - fn emit(&mut self, signal: Signal) { + fn on_emit(&mut self, signal: Signal) { self(signal) } } -/// The signal used for communicating state changes. +/// The `Signal` describes the state change done to the data structures. /// -/// For example, if the history tree can no longer redo any edits, -/// it sends a `Redo(false)` signal to tell the user. +/// See [`Slot`] for more information. #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] pub enum Signal { @@ -76,5 +103,5 @@ pub enum Signal { pub struct Nop; impl Slot for Nop { - fn emit(&mut self, _: Signal) {} + fn on_emit(&mut self, _: Signal) {} } From 2d79db0abc603d9e83bc6f67300d842d6beb406f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Sat, 15 Apr 2023 15:48:35 +0200 Subject: [PATCH 11/33] Impl Slot for mpsc senders --- src/entry.rs | 11 ++++++----- src/socket.rs | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/entry.rs b/src/entry.rs index e1e633ed..41c87a77 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -18,21 +18,22 @@ pub struct Entry { impl Entry { pub fn undo(&mut self, target: &mut E::Target) -> E::Output { - #[cfg(feature = "std")] - self.updated_now(); + self.pre_edit(); self.edit.undo(target) } pub fn redo(&mut self, target: &mut E::Target) -> E::Output { - #[cfg(feature = "std")] - self.updated_now(); + self.pre_edit(); self.edit.redo(target) } #[cfg(feature = "std")] - fn updated_now(&mut self) { + fn pre_edit(&mut self) { self.updated_at = SystemTime::now(); } + + #[cfg(not(feature = "std"))] + fn pre_edit(&mut self) {} } impl From for Entry { diff --git a/src/socket.rs b/src/socket.rs index aae9d993..0dad9e9f 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -3,6 +3,8 @@ use core::mem; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] +use std::sync::mpsc::{Sender, SyncSender}; /// Slot wrapper that adds some additional functionality. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -58,12 +60,13 @@ impl Socket { /// /// let mut target = String::new(); /// let mut record = Record::builder() -/// .connect(|s| sender.send(s).unwrap()) +/// .connect(sender) /// .build(); /// /// record.edit(&mut target, Add('a')); /// assert_eq!(iter.next(), Some(Signal::Undo(true))); /// assert_eq!(iter.next(), Some(Signal::Saved(false))); +/// assert_eq!(iter.next(), None); /// /// record.undo(&mut target); /// assert_eq!(iter.next(), Some(Signal::Undo(false))); @@ -83,6 +86,20 @@ impl Slot for F { } } +#[cfg(feature = "std")] +impl Slot for Sender { + fn on_emit(&mut self, signal: Signal) { + self.send(signal).ok(); + } +} + +#[cfg(feature = "std")] +impl Slot for SyncSender { + fn on_emit(&mut self, signal: Signal) { + self.send(signal).ok(); + } +} + /// The `Signal` describes the state change done to the data structures. /// /// See [`Slot`] for more information. From 6fa09fdeccf874128dbb30685450caaae6468934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Sun, 16 Apr 2023 14:44:59 +0200 Subject: [PATCH 12/33] Rename current to index --- examples/history.rs | 4 +-- src/history.rs | 14 ++++---- src/record.rs | 74 +++++++++++++++++++++---------------------- src/record/builder.rs | 2 +- src/record/display.rs | 2 +- 5 files changed, 48 insertions(+), 48 deletions(-) diff --git a/examples/history.rs b/examples/history.rs index 5143449e..c2966ce0 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -32,7 +32,7 @@ fn main() { assert_eq!(target, "abc"); let abc_branch = history.branch(); - let abc_current = history.current(); + let abc_current = history.index(); history.undo(&mut target); assert_eq!(target, "ab"); @@ -43,7 +43,7 @@ fn main() { assert_eq!(target, "abdef"); let abdef_branch = history.branch(); - let abdef_current = history.current(); + let abdef_current = history.index(); history.go_to(&mut target, abc_branch, abc_current); assert_eq!(target, "abc"); diff --git a/src/history.rs b/src/history.rs index 381f768d..d7457bd5 100644 --- a/src/history.rs +++ b/src/history.rs @@ -136,9 +136,9 @@ impl History { self.root } - /// Returns the position of the current edit. - pub fn current(&self) -> usize { - self.record.current() + /// Returns the index of the next edit. + pub fn index(&self) -> usize { + self.record.index() } /// Returns a structure for configurable formatting of the history. @@ -162,7 +162,7 @@ impl History { } pub(crate) fn at(&self) -> At { - At::new(self.root, self.current()) + At::new(self.root, self.index()) } } @@ -173,7 +173,7 @@ impl History { let saved = self.record.saved.filter(|&saved| saved > at.current); let (output, merged, tail) = self.record.edit_inner(target, edit); // Check if the limit has been reached. - if !merged && at.current == self.current() { + if !merged && at.current == self.index() { let root = self.branch(); self.rm_child(root, 0); self.branches @@ -222,7 +222,7 @@ impl History { pub(crate) fn jump_to(&mut self, root: usize) { let mut branch = self.branches.remove(&root).unwrap(); debug_assert_eq!(branch.parent, self.at()); - let current = self.current(); + let current = self.index(); let saved = self.record.saved.filter(|&saved| saved > current); let tail = self.record.entries.split_off(current); self.record.entries.append(&mut branch.entries); @@ -327,7 +327,7 @@ impl History { outputs.extend(o); // Apply the edits in the branch and move older edits into their own branch. for entry in branch.entries { - let current = self.current(); + let current = self.index(); let saved = self.record.saved.filter(|&saved| saved > current); let (_, _, entries) = self.record.edit_inner(target, entry.edit); if !entries.is_empty() { diff --git a/src/record.rs b/src/record.rs index 04342170..4ac1a3af 100644 --- a/src/record.rs +++ b/src/record.rs @@ -62,7 +62,7 @@ use serde::{Deserialize, Serialize}; pub struct Record { pub(crate) entries: VecDeque>, pub(crate) limit: NonZeroUsize, - pub(crate) current: usize, + pub(crate) index: usize, pub(crate) saved: Option, pub(crate) socket: Socket, } @@ -125,22 +125,22 @@ impl Record { /// Returns `true` if the record can undo. pub fn can_undo(&self) -> bool { - self.current > 0 + self.index > 0 } /// Returns `true` if the record can redo. pub fn can_redo(&self) -> bool { - self.current < self.len() + self.index < self.len() } /// Returns `true` if the target is in a saved state, `false` otherwise. pub fn is_saved(&self) -> bool { - self.saved == Some(self.current) + self.saved == Some(self.index) } - /// Returns the position of the current edit. - pub fn current(&self) -> usize { - self.current + /// Returns the index of the next edit. + pub fn index(&self) -> usize { + self.index } /// Returns a structure for configurable formatting of the record. @@ -178,14 +178,14 @@ impl Record { ) -> (E::Output, bool, VecDeque>) { let output = edit.edit(target); // We store the state of the stack before adding the entry. - let current = self.current; + let index = self.index; let could_undo = self.can_undo(); let could_redo = self.can_redo(); let was_saved = self.is_saved(); // Pop off all elements after len from entries. - let tail = self.entries.split_off(current); + let tail = self.entries.split_off(index); // Check if the saved state was popped off. - self.saved = self.saved.filter(|&saved| saved <= current); + self.saved = self.saved.filter(|&saved| saved <= index); // Try to merge edits unless the target is in a saved state. let merged = match self.entries.back_mut() { Some(last) if !was_saved => last.edit.merge(edit), @@ -196,17 +196,17 @@ impl Record { Merged::Yes => true, Merged::Annul => { self.entries.pop_back(); - self.current -= 1; + self.index -= 1; true } // If edits are not merged or annulled push it onto the storage. Merged::No(edit) => { // If limit is reached, pop off the first edit command. - if self.limit() == self.current { + if self.limit() == self.index { self.entries.pop_front(); self.saved = self.saved.and_then(|saved| saved.checked_sub(1)); } else { - self.current += 1; + self.index += 1; } self.entries.push_back(Entry::from(edit)); false @@ -224,9 +224,9 @@ impl Record { pub fn undo(&mut self, target: &mut E::Target) -> Option { self.can_undo().then(|| { let was_saved = self.is_saved(); - let old = self.current; - let output = self.entries[self.current - 1].undo(target); - self.current -= 1; + let old = self.index; + let output = self.entries[self.index - 1].undo(target); + self.index -= 1; let is_saved = self.is_saved(); self.socket.emit_if(old == 1, || Signal::Undo(false)); self.socket @@ -242,9 +242,9 @@ impl Record { pub fn redo(&mut self, target: &mut E::Target) -> Option { self.can_redo().then(|| { let was_saved = self.is_saved(); - let old = self.current; - let output = self.entries[self.current].redo(target); - self.current += 1; + let old = self.index; + let output = self.entries[self.index].redo(target); + self.index += 1; let is_saved = self.is_saved(); self.socket.emit_if(old == 0, || Signal::Undo(true)); self.socket @@ -259,7 +259,7 @@ impl Record { pub fn set_saved(&mut self, saved: bool) { let was_saved = self.is_saved(); if saved { - self.saved = Some(self.current); + self.saved = Some(self.index); self.socket.emit_if(!was_saved, || Signal::Saved(true)); } else { self.saved = None; @@ -273,7 +273,7 @@ impl Record { let could_redo = self.can_redo(); self.entries.clear(); self.saved = self.is_saved().then_some(0); - self.current = 0; + self.index = 0; self.socket.emit_if(could_undo, || Signal::Undo(false)); self.socket.emit_if(could_redo, || Signal::Redo(false)); } @@ -284,9 +284,9 @@ impl Record { .map_or_else(Vec::new, |saved| self.go_to(target, saved)) } - /// Repeatedly calls [`Edit::undo`] or [`Edit::redo`] until the edit at `current` is reached. - pub fn go_to(&mut self, target: &mut E::Target, current: usize) -> Vec { - if current > self.len() { + /// Repeatedly calls [`Edit::undo`] or [`Edit::redo`] until the edit at `index` is reached. + pub fn go_to(&mut self, target: &mut E::Target, index: usize) -> Vec { + if index > self.len() { return Vec::new(); } @@ -295,16 +295,16 @@ impl Record { let was_saved = self.is_saved(); // Temporarily remove slot so they are not called each iteration. let slot = self.socket.disconnect(); - // Decide if we need to undo or redo to reach current. - let undo_or_redo = if current > self.current { + // Decide if we need to undo or redo to reach index. + let undo_or_redo = if index > self.index { Record::redo } else { Record::undo }; - let capacity = self.current.abs_diff(current); + let capacity = self.index.abs_diff(index); let mut outputs = Vec::with_capacity(capacity); - while self.current != current { + while self.index != index { let output = undo_or_redo(self, target).unwrap(); outputs.push(output); } @@ -328,13 +328,13 @@ impl Record { /// Returns the string of the edit which will be undone /// in the next call to [`Record::undo`]. pub fn undo_string(&self) -> Option { - self.current.checked_sub(1).and_then(|i| self.string_at(i)) + self.index.checked_sub(1).and_then(|i| self.string_at(i)) } /// Returns the string of the edit which will be redone /// in the next call to [`Record::redo`]. pub fn redo_string(&self) -> Option { - self.string_at(self.current) + self.string_at(self.index) } fn string_at(&self, i: usize) -> Option { @@ -439,25 +439,25 @@ mod tests { record.edit(&mut target, Add('e')); record.go_to(&mut target, 0); - assert_eq!(record.current(), 0); + assert_eq!(record.index(), 0); assert_eq!(target, ""); record.go_to(&mut target, 5); - assert_eq!(record.current(), 5); + assert_eq!(record.index(), 5); assert_eq!(target, "abcde"); record.go_to(&mut target, 1); - assert_eq!(record.current(), 1); + assert_eq!(record.index(), 1); assert_eq!(target, "a"); record.go_to(&mut target, 4); - assert_eq!(record.current(), 4); + assert_eq!(record.index(), 4); assert_eq!(target, "abcd"); record.go_to(&mut target, 2); - assert_eq!(record.current(), 2); + assert_eq!(record.index(), 2); assert_eq!(target, "ab"); record.go_to(&mut target, 3); - assert_eq!(record.current(), 3); + assert_eq!(record.index(), 3); assert_eq!(target, "abc"); assert!(record.go_to(&mut target, 6).is_empty()); - assert_eq!(record.current(), 3); + assert_eq!(record.index(), 3); } #[test] diff --git a/src/record/builder.rs b/src/record/builder.rs index 66f686ac..165c00e9 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -63,7 +63,7 @@ impl Builder { Record { entries: VecDeque::with_capacity(self.capacity), limit: self.limit, - current: 0, + index: 0, saved: self.saved.then_some(0), socket: self.socket, } diff --git a/src/record/display.rs b/src/record/display.rs index e94be59e..edc847ff 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -67,7 +67,7 @@ impl Display<'_, E, S> { self.format.labels( f, at, - At::root(self.record.current()), + At::root(self.record.index()), self.record.saved.map(At::root), )?; From 1c14144ef701e0a7916ac47ef7106be05759f487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Mon, 17 Apr 2023 09:52:10 +0200 Subject: [PATCH 13/33] Replace Nop with () --- src/history.rs | 4 ++-- src/history/builder.rs | 4 ++-- src/lib.rs | 2 +- src/record.rs | 4 ++-- src/record/builder.rs | 4 ++-- src/socket.rs | 41 ++++++++++++++++++----------------------- 6 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/history.rs b/src/history.rs index d7457bd5..eeae588b 100644 --- a/src/history.rs +++ b/src/history.rs @@ -10,7 +10,7 @@ pub use checkpoint::Checkpoint; pub use display::Display; pub use queue::Queue; -use crate::socket::{Nop, Signal, Slot}; +use crate::socket::{Signal, Slot}; use crate::{At, Edit, Entry, Record}; use alloc::collections::{BTreeMap, VecDeque}; use alloc::string::{String, ToString}; @@ -52,7 +52,7 @@ use serde::{Deserialize, Serialize}; /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug)] -pub struct History { +pub struct History { root: usize, next: usize, saved: Option, diff --git a/src/history/builder.rs b/src/history/builder.rs index 8162b7b3..4db6fc88 100644 --- a/src/history/builder.rs +++ b/src/history/builder.rs @@ -1,5 +1,5 @@ use crate::record::Builder as RecordBuilder; -use crate::{History, Nop}; +use crate::History; /// Builder for a [`History`]. /// @@ -18,7 +18,7 @@ use crate::{History, Nop}; /// # } /// ``` #[derive(Debug)] -pub struct Builder(RecordBuilder); +pub struct Builder(RecordBuilder); impl Builder { /// Sets the capacity for the history. diff --git a/src/lib.rs b/src/lib.rs index 4e83df80..8fd4a9bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,7 +72,7 @@ pub use join::Join; #[cfg(feature = "alloc")] pub use record::Record; #[cfg(feature = "alloc")] -pub use socket::{Nop, Signal, Slot}; +pub use socket::{Signal, Slot}; /// Base functionality for all edit commands. pub trait Edit { diff --git a/src/record.rs b/src/record.rs index 4ac1a3af..bb39ffec 100644 --- a/src/record.rs +++ b/src/record.rs @@ -10,7 +10,7 @@ pub use checkpoint::Checkpoint; pub use display::Display; pub use queue::Queue; -use crate::socket::{Nop, Slot, Socket}; +use crate::socket::{Slot, Socket}; use crate::{Edit, Entry, History, Merged, Signal}; use alloc::collections::VecDeque; use alloc::string::{String, ToString}; @@ -59,7 +59,7 @@ use serde::{Deserialize, Serialize}; /// ``` #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug)] -pub struct Record { +pub struct Record { pub(crate) entries: VecDeque>, pub(crate) limit: NonZeroUsize, pub(crate) index: usize, diff --git a/src/record/builder.rs b/src/record/builder.rs index 165c00e9..ed6c8bdf 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -1,5 +1,5 @@ use super::Socket; -use crate::{Nop, Record}; +use crate::Record; use alloc::collections::VecDeque; use core::marker::PhantomData; use core::num::NonZeroUsize; @@ -21,7 +21,7 @@ use core::num::NonZeroUsize; /// # } /// ``` #[derive(Debug)] -pub struct Builder { +pub struct Builder { capacity: usize, limit: NonZeroUsize, saved: bool, diff --git a/src/socket.rs b/src/socket.rs index 0dad9e9f..59f67f96 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -45,6 +45,20 @@ impl Socket { } } +/// The `Signal` describes the state change done to the data structures. +/// +/// See [`Slot`] for more information. +#[derive(Clone, Debug, PartialEq)] +#[non_exhaustive] +pub enum Signal { + /// Says if the structures can undo. + Undo(bool), + /// Says if the structures can redo. + Redo(bool), + /// Says if the target is in a saved state. + Saved(bool), +} + /// Use this to handle signals emitted. /// /// This allows you to trigger events on certain state changes. @@ -80,6 +94,10 @@ pub trait Slot { fn on_emit(&mut self, signal: Signal); } +impl Slot for () { + fn on_emit(&mut self, _: Signal) {} +} + impl Slot for F { fn on_emit(&mut self, signal: Signal) { self(signal) @@ -99,26 +117,3 @@ impl Slot for SyncSender { self.send(signal).ok(); } } - -/// The `Signal` describes the state change done to the data structures. -/// -/// See [`Slot`] for more information. -#[derive(Clone, Debug, PartialEq)] -#[non_exhaustive] -pub enum Signal { - /// Says if the structures can undo. - Undo(bool), - /// Says if the structures can redo. - Redo(bool), - /// Says if the target is in a saved state. - Saved(bool), -} - -/// Default [`Slot`] that does nothing. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] -pub struct Nop; - -impl Slot for Nop { - fn on_emit(&mut self, _: Signal) {} -} From 9b902280549b51f7540247923228c027b7cb9445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Tue, 18 Apr 2023 22:34:14 +0200 Subject: [PATCH 14/33] Emit signal when index changes --- src/record.rs | 36 +++++++++++++++++++++++++----------- src/socket.rs | 10 +++++++--- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/record.rs b/src/record.rs index bb39ffec..aa025926 100644 --- a/src/record.rs +++ b/src/record.rs @@ -162,6 +162,13 @@ impl Record { pub fn checkpoint(&mut self) -> Checkpoint { Checkpoint::from(self) } + + /// Remove all elements after the index. + fn rm_tail(&mut self) -> VecDeque> { + // Check if the saved edit will be removed. + self.saved = self.saved.filter(|&saved| saved <= self.index); + self.entries.split_off(self.index) + } } impl Record { @@ -178,14 +185,12 @@ impl Record { ) -> (E::Output, bool, VecDeque>) { let output = edit.edit(target); // We store the state of the stack before adding the entry. - let index = self.index; + let old_index = self.index; let could_undo = self.can_undo(); let could_redo = self.can_redo(); let was_saved = self.is_saved(); - // Pop off all elements after len from entries. - let tail = self.entries.split_off(index); - // Check if the saved state was popped off. - self.saved = self.saved.filter(|&saved| saved <= index); + + let tail = self.rm_tail(); // Try to merge edits unless the target is in a saved state. let merged = match self.entries.back_mut() { Some(last) if !was_saved => last.edit.merge(edit), @@ -216,6 +221,8 @@ impl Record { self.socket.emit_if(could_redo, || Signal::Redo(false)); self.socket.emit_if(!could_undo, || Signal::Undo(true)); self.socket.emit_if(was_saved, || Signal::Saved(false)); + self.socket + .emit_if(old_index != self.index, || Signal::Index(self.index)); (output, merged_or_annulled, tail) } @@ -223,16 +230,17 @@ impl Record { /// the previous one as the new active one. pub fn undo(&mut self, target: &mut E::Target) -> Option { self.can_undo().then(|| { + let old_index = self.index; let was_saved = self.is_saved(); - let old = self.index; let output = self.entries[self.index - 1].undo(target); self.index -= 1; let is_saved = self.is_saved(); - self.socket.emit_if(old == 1, || Signal::Undo(false)); + self.socket.emit_if(old_index == 1, || Signal::Undo(false)); self.socket - .emit_if(old == self.entries.len(), || Signal::Redo(true)); + .emit_if(old_index == self.entries.len(), || Signal::Redo(true)); self.socket .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); + self.socket.emit(|| Signal::Index(self.index)); output }) } @@ -241,16 +249,17 @@ impl Record { /// the next one as the new active one. pub fn redo(&mut self, target: &mut E::Target) -> Option { self.can_redo().then(|| { + let old_index = self.index; let was_saved = self.is_saved(); - let old = self.index; let output = self.entries[self.index].redo(target); self.index += 1; let is_saved = self.is_saved(); - self.socket.emit_if(old == 0, || Signal::Undo(true)); + self.socket.emit_if(old_index == 0, || Signal::Undo(true)); self.socket - .emit_if(old == self.len() - 1, || Signal::Redo(false)); + .emit_if(old_index == self.len() - 1, || Signal::Redo(false)); self.socket .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); + self.socket.emit(|| Signal::Index(self.index)); output }) } @@ -269,6 +278,7 @@ impl Record { /// Removes all edits from the record without undoing them. pub fn clear(&mut self) { + let old_index = self.index; let could_undo = self.can_undo(); let could_redo = self.can_redo(); self.entries.clear(); @@ -276,6 +286,7 @@ impl Record { self.index = 0; self.socket.emit_if(could_undo, || Signal::Undo(false)); self.socket.emit_if(could_redo, || Signal::Redo(false)); + self.socket.emit_if(old_index != 0, || Signal::Index(0)); } /// Revert the changes done to the target since the saved state. @@ -290,6 +301,7 @@ impl Record { return Vec::new(); } + let old_index = self.index(); let could_undo = self.can_undo(); let could_redo = self.can_redo(); let was_saved = self.is_saved(); @@ -319,6 +331,8 @@ impl Record { .emit_if(could_redo != can_redo, || Signal::Redo(can_redo)); self.socket .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); + self.socket + .emit_if(old_index != self.index, || Signal::Index(self.index)); outputs } diff --git a/src/socket.rs b/src/socket.rs index 59f67f96..070e218c 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -51,12 +51,14 @@ impl Socket { #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] pub enum Signal { - /// Says if the structures can undo. + /// Emitted when the structures ability to undo has changed. Undo(bool), - /// Says if the structures can redo. + /// Emitted when the structures ability to redo has changed. Redo(bool), - /// Says if the target is in a saved state. + /// Emitted when the saved state has changed. Saved(bool), + /// Emitted when the index has changed. + Index(usize), } /// Use this to handle signals emitted. @@ -80,12 +82,14 @@ pub enum Signal { /// record.edit(&mut target, Add('a')); /// assert_eq!(iter.next(), Some(Signal::Undo(true))); /// assert_eq!(iter.next(), Some(Signal::Saved(false))); +/// assert_eq!(iter.next(), Some(Signal::Index(1))); /// assert_eq!(iter.next(), None); /// /// record.undo(&mut target); /// assert_eq!(iter.next(), Some(Signal::Undo(false))); /// assert_eq!(iter.next(), Some(Signal::Redo(true))); /// assert_eq!(iter.next(), Some(Signal::Saved(true))); +/// assert_eq!(iter.next(), Some(Signal::Index(0))); /// assert_eq!(iter.next(), None); /// # } /// ``` From 9e2bbb2f60fac9291924be519fa0ea9d60decda1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Fri, 15 Sep 2023 13:02:56 +0200 Subject: [PATCH 15/33] Use matches! instead of map_or(false, ..) --- src/format.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/format.rs b/src/format.rs index 4821d275..ceb52946 100644 --- a/src/format.rs +++ b/src/format.rs @@ -115,7 +115,7 @@ impl Format { ) -> fmt::Result { match ( self.current && at == current, - self.saved && saved.map_or(false, |saved| saved == at), + self.saved && matches!(saved, Some(saved) if saved == at), ) { (true, true) => { #[cfg(feature = "colored")] From 43069c20b06959c59a79dc6cd79a13b809b3f6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Sun, 17 Sep 2023 20:27:01 +0200 Subject: [PATCH 16/33] Remove Any, FromFn, and Join --- src/{doctest.rs => add.rs} | 9 +++-- src/any.rs | 78 ------------------------------------ src/from_fn.rs | 82 -------------------------------------- src/history.rs | 5 +-- src/history/builder.rs | 3 +- src/history/checkpoint.rs | 10 ++--- src/history/queue.rs | 3 +- src/join.rs | 79 ------------------------------------ src/lib.rs | 11 ++--- src/record.rs | 3 +- src/record/builder.rs | 3 +- src/record/checkpoint.rs | 20 +++++----- src/record/queue.rs | 11 +++-- src/socket.rs | 3 +- 14 files changed, 35 insertions(+), 285 deletions(-) rename src/{doctest.rs => add.rs} (67%) delete mode 100644 src/any.rs delete mode 100644 src/from_fn.rs delete mode 100644 src/join.rs diff --git a/src/doctest.rs b/src/add.rs similarity index 67% rename from src/doctest.rs rename to src/add.rs index 42207de9..f64164bd 100644 --- a/src/doctest.rs +++ b/src/add.rs @@ -1,9 +1,10 @@ -// This file is included in the documentation examples to avoid some boilerplate. - /// This is the edit used in all the examples. -pub struct Add(char); +/// +/// Not part of the API and can change at any time. +#[doc(hidden)] +pub struct Add(pub char); -impl undo::Edit for Add { +impl crate::Edit for Add { type Target = String; type Output = (); diff --git a/src/any.rs b/src/any.rs deleted file mode 100644 index ac7d5808..00000000 --- a/src/any.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::Edit; -use alloc::boxed::Box; -use alloc::string::String; -use core::fmt::{self, Debug, Display, Formatter}; - -/// Any [`Edit`] command. -/// -/// This allows you to use multiple types of edits at the same time -/// as long as they all share the same target and output type. -/// -/// # Examples -/// ``` -/// # include!("doctest.rs"); -/// # fn main() { -/// # use undo::{Any, Record, FromFn}; -/// let mut target = String::new(); -/// let mut record = Record::new(); -/// -/// record.edit(&mut target, Any::new(Add('a'))); -/// record.edit(&mut target, Any::new(FromFn::new(|s: &mut String| s.push('b')))); -/// record.edit(&mut target, Any::new(FromFn::new(|s: &mut String| s.push('c')))); -/// assert_eq!(target, "abc"); -/// # } -/// ``` -pub struct Any { - edit: Box>, - string: String, -} - -impl Any { - /// Creates an [`Any`] from the provided edit. - pub fn new(edit: E) -> Any - where - E: Edit, - E: 'static, - { - Any { - edit: Box::new(edit), - string: String::new(), - } - } - - /// Sets the display message of this edit. - pub fn set_string(&mut self, str: impl Into) { - self.string = str.into(); - } -} - -impl Edit for Any { - type Target = T; - type Output = O; - - fn edit(&mut self, target: &mut Self::Target) -> Self::Output { - self.edit.edit(target) - } - - fn undo(&mut self, target: &mut Self::Target) -> Self::Output { - self.edit.undo(target) - } - - fn redo(&mut self, target: &mut Self::Target) -> Self::Output { - self.edit.redo(target) - } -} - -impl Debug for Any { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("Any") - .field("string", &self.string) - .finish_non_exhaustive() - } -} - -impl Display for Any { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(&self.string, f) - } -} diff --git a/src/from_fn.rs b/src/from_fn.rs deleted file mode 100644 index cfb71a11..00000000 --- a/src/from_fn.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::Edit; -use core::fmt::{self, Debug, Formatter}; -use core::mem; - -/// An [`Edit`] command made from a function. -/// -/// This is a convenient way to make simple edits without having to -/// create a new type for each one. But for more complex edits it is -/// probably better to create a custom edit command. -/// -/// # Examples -/// ``` -/// # include!("doctest.rs"); -/// # fn main() { -/// # use undo::{Any, Record, FromFn}; -/// let mut target = String::new(); -/// let mut record = Record::new(); -/// -/// let a: fn(&mut String) = |s| s.push('a'); -/// let b: fn(&mut String) = |s| s.push('b'); -/// record.edit(&mut target, FromFn::new(a)); -/// record.edit(&mut target, FromFn::new(b)); -/// assert_eq!(target, "ab"); -/// -/// record.undo(&mut target); -/// record.undo(&mut target); -/// assert_eq!(target, ""); -/// -/// record.redo(&mut target); -/// record.redo(&mut target); -/// assert_eq!(target, "ab"); -/// # } -/// ``` -#[derive(Clone)] -pub struct FromFn { - f: F, - target: Option, -} - -impl FromFn { - /// Creates a new `FromFn` from `f`. - pub const fn new(f: F) -> Self { - FromFn { f, target: None } - } -} - -impl Edit for FromFn -where - F: FnMut(&mut T), - T: Clone, -{ - type Target = T; - type Output = (); - - fn edit(&mut self, target: &mut Self::Target) -> Self::Output { - self.target = Some(target.clone()); - (self.f)(target) - } - - fn undo(&mut self, target: &mut Self::Target) -> Self::Output { - if let Some(old) = self.target.as_mut() { - mem::swap(old, target); - } - } - - fn redo(&mut self, target: &mut Self::Target) -> Self::Output { - if let Some(new) = self.target.as_mut() { - mem::swap(new, target); - } - } -} - -impl Debug for FromFn -where - T: Debug, -{ - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("FromFn") - .field("target", &self.target) - .finish_non_exhaustive() - } -} diff --git a/src/history.rs b/src/history.rs index eeae588b..280dba56 100644 --- a/src/history.rs +++ b/src/history.rs @@ -25,9 +25,8 @@ use serde::{Deserialize, Serialize}; /// /// # Examples /// ``` -/// # include!("doctest.rs"); /// # fn main() { -/// # use undo::History; +/// # use undo::{Add, History}; /// let mut target = String::new(); /// let mut history = History::new(); /// @@ -318,7 +317,7 @@ impl History { // Walk the path from `root` to `branch`. let mut outputs = Vec::new(); - let Some(path) = self.mk_path(branch) else { + let Some(path) = self.mk_path(branch) else { return Vec::new(); }; for (new, branch) in path { diff --git a/src/history/builder.rs b/src/history/builder.rs index 4db6fc88..55aaf499 100644 --- a/src/history/builder.rs +++ b/src/history/builder.rs @@ -5,9 +5,8 @@ use crate::History; /// /// # Examples /// ``` -/// # include!("../doctest.rs"); /// # fn main() { -/// # use undo::History; +/// # use undo::{Add, History}; /// # let mut target = String::new(); /// let mut history = History::builder() /// .limit(100) diff --git a/src/history/checkpoint.rs b/src/history/checkpoint.rs index fb7f1494..bea03ad5 100644 --- a/src/history/checkpoint.rs +++ b/src/history/checkpoint.rs @@ -88,11 +88,11 @@ impl<'a, E, S> From<&'a mut History> for Checkpoint<'a, E, S> { mod tests { use crate::*; - const A: FromFn = FromFn::new(|s| s.push('a')); - const B: FromFn = FromFn::new(|s| s.push('b')); - const C: FromFn = FromFn::new(|s| s.push('c')); - const D: FromFn = FromFn::new(|s| s.push('d')); - const E: FromFn = FromFn::new(|s| s.push('e')); + const A: Add = Add('a'); + const B: Add = Add('b'); + const C: Add = Add('c'); + const D: Add = Add('d'); + const E: Add = Add('e'); #[test] fn checkpoint() { diff --git a/src/history/queue.rs b/src/history/queue.rs index 30b7e142..cf92f91c 100644 --- a/src/history/queue.rs +++ b/src/history/queue.rs @@ -13,9 +13,8 @@ enum QueueEntry { /// /// # Examples /// ``` -/// # include!("../doctest.rs"); /// # fn main() { -/// # use undo::History; +/// # use undo::{Add, History}; /// let mut string = String::new(); /// let mut record = History::new(); /// let mut queue = record.queue(); diff --git a/src/join.rs b/src/join.rs deleted file mode 100644 index bc8dd973..00000000 --- a/src/join.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::Edit; -use core::fmt::{self, Display, Formatter}; - -/// Two [`Edit`] commands joined together. -/// -/// This is a convenient way to build more complex edit commands from simpler ones, -/// but for more complex edits it is probably better to create a custom edit command. -/// -/// # Examples -/// ``` -/// # include!("doctest.rs"); -/// # fn main() { -/// # use undo::{Record, Join}; -/// let mut target = String::new(); -/// let mut record = Record::new(); -/// -/// let abc = Join::new(Add('a'), Add('b')).join(Add('c')); -/// record.edit(&mut target, abc); -/// assert_eq!(target, "abc"); -/// record.undo(&mut target); -/// assert_eq!(target, ""); -/// record.redo(&mut target); -/// assert_eq!(target, "abc"); -/// # } -/// ``` -#[derive(Clone, Debug)] -pub struct Join { - a: A, - b: B, -} - -impl Join { - /// Creates a new [`Join`] from `a` and `b`. - pub const fn new(a: A, b: B) -> Self { - Join { a, b } - } - - /// Joins `self` with `c`. - pub const fn join(self, c: C) -> Join { - Join::new(self, c) - } -} - -impl Edit for Join -where - A: Edit, - B: Edit, -{ - type Target = A::Target; - type Output = A::Output; - - /// The output of a will be discarded. - fn edit(&mut self, target: &mut A::Target) -> Self::Output { - self.a.edit(target); - self.b.edit(target) - } - - /// The output of b will be discarded. - fn undo(&mut self, target: &mut A::Target) -> Self::Output { - self.b.undo(target); - self.a.undo(target) - } - - /// The output of a will be discarded. - fn redo(&mut self, target: &mut A::Target) -> Self::Output { - self.a.redo(target); - self.b.redo(target) - } -} - -impl Display for Join -where - A: Display, - B: Display, -{ - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{} & {}", self.a, self.b) - } -} diff --git a/src/lib.rs b/src/lib.rs index 8fd4a9bf..a6da9b52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,16 +41,13 @@ extern crate alloc; #[doc = include_str!("../README.md")] pub struct ReadmeDocTest; -#[cfg(feature = "alloc")] -mod any; +mod add; #[cfg(feature = "alloc")] mod entry; #[cfg(feature = "alloc")] mod format; -mod from_fn; #[cfg(feature = "alloc")] pub mod history; -mod join; #[cfg(feature = "alloc")] pub mod record; #[cfg(feature = "alloc")] @@ -63,12 +60,10 @@ use format::Format; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "alloc")] -pub use any::Any; -pub use from_fn::FromFn; +#[doc(hidden)] +pub use add::Add; #[cfg(feature = "alloc")] pub use history::History; -pub use join::Join; #[cfg(feature = "alloc")] pub use record::Record; #[cfg(feature = "alloc")] diff --git a/src/record.rs b/src/record.rs index aa025926..b9bd36ae 100644 --- a/src/record.rs +++ b/src/record.rs @@ -32,9 +32,8 @@ use serde::{Deserialize, Serialize}; /// /// # Examples /// ``` -/// # include!("doctest.rs"); /// # fn main() { -/// # use undo::Record; +/// # use undo::{Add, Record}; /// let mut target = String::new(); /// let mut record = Record::new(); /// diff --git a/src/record/builder.rs b/src/record/builder.rs index ed6c8bdf..1125a8cf 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -8,9 +8,8 @@ use core::num::NonZeroUsize; /// /// # Examples /// ``` -/// # include!("../doctest.rs"); /// # fn main() { -/// # use undo::Record; +/// # use undo::{Add, Record}; /// # let mut target = String::new(); /// let mut record = Record::builder() /// .limit(100) diff --git a/src/record/checkpoint.rs b/src/record/checkpoint.rs index e67723f6..83f26215 100644 --- a/src/record/checkpoint.rs +++ b/src/record/checkpoint.rs @@ -86,17 +86,17 @@ impl<'a, E, S> From<&'a mut Record> for Checkpoint<'a, E, S> { #[cfg(test)] mod tests { - use crate::{FromFn, Record}; + use crate::{add::Add, Record}; - const A: FromFn = FromFn::new(|s| s.push('a')); - const B: FromFn = FromFn::new(|s| s.push('b')); - const C: FromFn = FromFn::new(|s| s.push('c')); - const D: FromFn = FromFn::new(|s| s.push('d')); - const E: FromFn = FromFn::new(|s| s.push('e')); - const F: FromFn = FromFn::new(|s| s.push('f')); - const G: FromFn = FromFn::new(|s| s.push('g')); - const H: FromFn = FromFn::new(|s| s.push('h')); - const I: FromFn = FromFn::new(|s| s.push('i')); + const A: Add = Add('a'); + const B: Add = Add('b'); + const C: Add = Add('c'); + const D: Add = Add('d'); + const E: Add = Add('e'); + const F: Add = Add('f'); + const G: Add = Add('g'); + const H: Add = Add('h'); + const I: Add = Add('i'); #[test] fn checkpoint_commit() { diff --git a/src/record/queue.rs b/src/record/queue.rs index 1b229672..d5c2a1ee 100644 --- a/src/record/queue.rs +++ b/src/record/queue.rs @@ -13,9 +13,8 @@ enum QueueEntry { /// /// # Examples /// ``` -/// # include!("../doctest.rs"); /// # fn main() { -/// # use undo::Record; +/// # use undo::{Add, Record}; /// let mut string = String::new(); /// let mut record = Record::new(); /// let mut queue = record.queue(); @@ -90,12 +89,12 @@ impl<'a, E, S> From<&'a mut Record> for Queue<'a, E, S> { #[cfg(test)] mod tests { - use crate::{FromFn, Record}; + use crate::{Add, Record}; use alloc::string::String; - const A: FromFn = FromFn::new(|s| s.push('a')); - const B: FromFn = FromFn::new(|s| s.push('b')); - const C: FromFn = FromFn::new(|s| s.push('c')); + const A: Add = Add('a'); + const B: Add = Add('b'); + const C: Add = Add('c'); #[test] fn queue_commit() { diff --git a/src/socket.rs b/src/socket.rs index 070e218c..e6549b09 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -67,9 +67,8 @@ pub enum Signal { /// /// # Examples /// ``` -/// # include!("doctest.rs"); /// # use std::sync::mpsc; -/// # use undo::{FromFn, Record, Signal}; +/// # use undo::{Add, Record, Signal}; /// # fn main() { /// let (sender, receiver) = mpsc::channel(); /// let mut iter = receiver.try_iter(); From 5390d56499c9519c005c8efdf5617bc739fd2063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Sun, 17 Sep 2023 21:29:06 +0200 Subject: [PATCH 17/33] Fix entry timestamps not working when switching branches --- README.md | 2 +- examples/history.rs | 24 +----------------------- examples/record.rs | 24 +----------------------- src/add.rs | 8 ++++++++ src/history.rs | 4 ++-- src/record.rs | 21 +++++++++++---------- src/record/checkpoint.rs | 2 +- 7 files changed, 25 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index b359cb99..0eca83c4 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ impl Edit for Add { } fn undo(&mut self, target: &mut String) { - self.0 = target.pop().expect("cannot pop empty string"); + self.0 = target.pop().unwrap(); } } diff --git a/examples/history.rs b/examples/history.rs index c2966ce0..bfdc0179 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -1,26 +1,4 @@ -use core::fmt::{self, Display, Formatter}; -use undo::{Edit, History}; - -struct Add(char); - -impl Edit for Add { - type Target = String; - type Output = (); - - fn edit(&mut self, string: &mut String) { - string.push(self.0); - } - - fn undo(&mut self, string: &mut String) { - self.0 = string.pop().expect("cannot pop empty string"); - } -} - -impl Display for Add { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Add '{}'", self.0) - } -} +use undo::{Add, History}; fn main() { let mut target = String::new(); diff --git a/examples/record.rs b/examples/record.rs index b81ff9b7..068dad7b 100644 --- a/examples/record.rs +++ b/examples/record.rs @@ -1,26 +1,4 @@ -use core::fmt::{self, Display, Formatter}; -use undo::{Edit, Record}; - -struct Add(char); - -impl Edit for Add { - type Target = String; - type Output = (); - - fn edit(&mut self, target: &mut String) { - target.push(self.0); - } - - fn undo(&mut self, target: &mut String) { - self.0 = target.pop().expect("cannot pop empty string"); - } -} - -impl Display for Add { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Add '{}'", self.0) - } -} +use undo::{Add, Record}; fn main() { let mut target = String::new(); diff --git a/src/add.rs b/src/add.rs index f64164bd..fd12fe53 100644 --- a/src/add.rs +++ b/src/add.rs @@ -1,3 +1,5 @@ +use core::fmt::{self, Display, Formatter}; + /// This is the edit used in all the examples. /// /// Not part of the API and can change at any time. @@ -16,3 +18,9 @@ impl crate::Edit for Add { self.0 = string.pop().unwrap(); } } + +impl Display for Add { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "Add '{}'", self.0) + } +} diff --git a/src/history.rs b/src/history.rs index 280dba56..68eb32d8 100644 --- a/src/history.rs +++ b/src/history.rs @@ -170,7 +170,7 @@ impl History { pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { let at = self.at(); let saved = self.record.saved.filter(|&saved| saved > at.current); - let (output, merged, tail) = self.record.edit_inner(target, edit); + let (output, merged, tail) = self.record.push(target, Entry::from(edit), E::edit); // Check if the limit has been reached. if !merged && at.current == self.index() { let root = self.branch(); @@ -328,7 +328,7 @@ impl History { for entry in branch.entries { let current = self.index(); let saved = self.record.saved.filter(|&saved| saved > current); - let (_, _, entries) = self.record.edit_inner(target, entry.edit); + let (_, _, entries) = self.record.push(target, entry, E::redo); if !entries.is_empty() { self.branches .insert(self.root, Branch::new(new, current, entries)); diff --git a/src/record.rs b/src/record.rs index b9bd36ae..6142d904 100644 --- a/src/record.rs +++ b/src/record.rs @@ -173,27 +173,28 @@ impl Record { impl Record { /// Pushes the edit on top of the record and executes its [`Edit::edit`] method. pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { - let (output, _, _) = self.edit_inner(target, edit); + let (output, _, _) = self.push(target, Entry::from(edit), E::edit); output } - pub(crate) fn edit_inner( + pub(crate) fn push( &mut self, target: &mut E::Target, - mut edit: E, + mut entry: Entry, + f: impl FnOnce(&mut E, &mut E::Target) -> E::Output, ) -> (E::Output, bool, VecDeque>) { - let output = edit.edit(target); - // We store the state of the stack before adding the entry. + let output = f(&mut entry.edit, target); + let old_index = self.index; let could_undo = self.can_undo(); let could_redo = self.can_redo(); let was_saved = self.is_saved(); let tail = self.rm_tail(); - // Try to merge edits unless the target is in a saved state. + // Try to merge unless the target is in a saved state. let merged = match self.entries.back_mut() { - Some(last) if !was_saved => last.edit.merge(edit), - _ => Merged::No(edit), + Some(last) if !was_saved => last.edit.merge(entry.edit), + _ => Merged::No(entry.edit), }; let merged_or_annulled = match merged { @@ -203,7 +204,6 @@ impl Record { self.index -= 1; true } - // If edits are not merged or annulled push it onto the storage. Merged::No(edit) => { // If limit is reached, pop off the first edit command. if self.limit() == self.index { @@ -212,7 +212,8 @@ impl Record { } else { self.index += 1; } - self.entries.push_back(Entry::from(edit)); + entry.edit = edit; + self.entries.push_back(entry); false } }; diff --git a/src/record/checkpoint.rs b/src/record/checkpoint.rs index 83f26215..36b5716f 100644 --- a/src/record/checkpoint.rs +++ b/src/record/checkpoint.rs @@ -33,7 +33,7 @@ impl Checkpoint<'_, E, S> { /// Calls the `apply` method. pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { let saved = self.record.saved; - let (output, _, tail) = self.record.edit_inner(target, edit); + let (output, _, tail) = self.record.push(target, Entry::from(edit), E::edit); self.entries.push(CheckpointEntry::Edit(saved, tail)); output } From 27e9635b61d49de8f53dfc7d4eb39d6c8a14e76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Sun, 17 Sep 2023 23:02:44 +0200 Subject: [PATCH 18/33] Rename position methods to head --- src/add.rs | 1 + src/format.rs | 60 +++++++++++++++++++----------------------- src/history/display.rs | 12 +++------ src/record.rs | 2 +- src/record/builder.rs | 2 +- src/record/display.rs | 12 +++------ 6 files changed, 36 insertions(+), 53 deletions(-) diff --git a/src/add.rs b/src/add.rs index fd12fe53..de7c1798 100644 --- a/src/add.rs +++ b/src/add.rs @@ -4,6 +4,7 @@ use core::fmt::{self, Display, Formatter}; /// /// Not part of the API and can change at any time. #[doc(hidden)] +#[derive(Clone, Copy, Debug)] pub struct Add(pub char); impl crate::Edit for Add { diff --git a/src/format.rs b/src/format.rs index ceb52946..8b33d0b5 100644 --- a/src/format.rs +++ b/src/format.rs @@ -12,9 +12,8 @@ use std::time::SystemTime; pub(crate) struct Format { #[cfg(feature = "colored")] pub colored: bool, - pub current: bool, pub detailed: bool, - pub position: bool, + pub head: bool, pub saved: bool, } @@ -23,9 +22,8 @@ impl Default for Format { Format { #[cfg(feature = "colored")] colored: true, - current: true, detailed: true, - position: true, + head: true, saved: true, } } @@ -86,23 +84,19 @@ impl Format { } pub fn position(self, f: &mut fmt::Formatter, at: At, use_branch: bool) -> fmt::Result { - if self.position { - #[cfg(feature = "colored")] - if self.colored { - let position = if use_branch { - alloc::format!("{}:{}", at.branch, at.current) - } else { - alloc::format!("{}", at.current) - }; - return write!(f, "{}", position.yellow().bold()); - } - if use_branch { - write!(f, "{}:{}", at.branch, at.current) + #[cfg(feature = "colored")] + if self.colored { + let position = if use_branch { + alloc::format!("{}:{}", at.branch, at.current) } else { - write!(f, "{}", at.current) - } + alloc::format!("{}", at.current) + }; + return write!(f, "{}", position.yellow().bold()); + } + if use_branch { + write!(f, "{}:{}", at.branch, at.current) } else { - Ok(()) + write!(f, "{}", at.current) } } @@ -114,7 +108,7 @@ impl Format { saved: Option, ) -> fmt::Result { match ( - self.current && at == current, + self.head && at == current, self.saved && matches!(saved, Some(saved) if saved == at), ) { (true, true) => { @@ -123,14 +117,14 @@ impl Format { return write!( f, " {}{}{} {}{}", - "(".yellow(), - "current".cyan().bold(), + "[".yellow(), + "HEAD".cyan().bold(), ",".yellow(), - "saved".green().bold(), - ")".yellow() + "SAVED".green().bold(), + "]".yellow() ); } - f.write_str(" (current, saved)") + f.write_str(" [HEAD, SAVED]") } (true, false) => { #[cfg(feature = "colored")] @@ -138,12 +132,12 @@ impl Format { return write!( f, " {}{}{}", - "(".yellow(), - "current".cyan().bold(), - ")".yellow() + "[".yellow(), + "HEAD".cyan().bold(), + "]".yellow() ); } - f.write_str(" (current)") + f.write_str(" [HEAD]") } (false, true) => { #[cfg(feature = "colored")] @@ -151,12 +145,12 @@ impl Format { return write!( f, " {}{}{}", - "(".yellow(), - "saved".green().bold(), - ")".yellow() + "[".yellow(), + "SAVED".green().bold(), + "]".yellow() ); } - f.write_str(" (saved)") + f.write_str(" [SAVED]") } (false, false) => Ok(()), } diff --git a/src/history/display.rs b/src/history/display.rs index 8e557e19..00a3d1f3 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -19,21 +19,15 @@ impl Display<'_, E, S> { self } - /// Show the current position in the output (on by default). - pub fn current(&mut self, on: bool) -> &mut Self { - self.format.current = on; - self - } - /// Show detailed output (on by default). pub fn detailed(&mut self, on: bool) -> &mut Self { self.format.detailed = on; self } - /// Show the position of the edit (on by default). - pub fn position(&mut self, on: bool) -> &mut Self { - self.format.position = on; + /// Show the current position in the output (on by default). + pub fn head(&mut self, on: bool) -> &mut Self { + self.format.head = on; self } diff --git a/src/record.rs b/src/record.rs index 6142d904..5233aea4 100644 --- a/src/record.rs +++ b/src/record.rs @@ -59,11 +59,11 @@ use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug)] pub struct Record { - pub(crate) entries: VecDeque>, pub(crate) limit: NonZeroUsize, pub(crate) index: usize, pub(crate) saved: Option, pub(crate) socket: Socket, + pub(crate) entries: VecDeque>, } impl Record { diff --git a/src/record/builder.rs b/src/record/builder.rs index 1125a8cf..0f6a8704 100644 --- a/src/record/builder.rs +++ b/src/record/builder.rs @@ -60,11 +60,11 @@ impl Builder { /// Builds the record. pub fn build(self) -> Record { Record { - entries: VecDeque::with_capacity(self.capacity), limit: self.limit, index: 0, saved: self.saved.then_some(0), socket: self.socket, + entries: VecDeque::with_capacity(self.capacity), } } } diff --git a/src/record/display.rs b/src/record/display.rs index edc847ff..bffaf55d 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -19,21 +19,15 @@ impl Display<'_, E, S> { self } - /// Show the current position in the output (on by default). - pub fn current(&mut self, on: bool) -> &mut Self { - self.format.current = on; - self - } - /// Show detailed output (on by default). pub fn detailed(&mut self, on: bool) -> &mut Self { self.format.detailed = on; self } - /// Show the position of the edit (on by default). - pub fn position(&mut self, on: bool) -> &mut Self { - self.format.position = on; + /// Show the current position in the output (on by default). + pub fn head(&mut self, on: bool) -> &mut Self { + self.format.head = on; self } From 12570c6619ab8ccf2e0a960668dabcf17f928ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Mon, 18 Sep 2023 12:45:13 +0200 Subject: [PATCH 19/33] Add support for custom system time formatter --- Cargo.toml | 3 +++ examples/history.rs | 9 ++++++++- examples/record.rs | 9 ++++++++- src/format.rs | 15 +++++++-------- src/history/display.rs | 23 ++++++++++++++++++++--- src/record/display.rs | 23 ++++++++++++++++++++--- 6 files changed, 66 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f7f8c893..04da2bfa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,9 @@ edition = "2021" colored = { version = "2", optional = true } serde = { version = "1", optional = true, default-features = false, features = ["derive"] } +[dev-dependencies] +chrono = "0.4" + [features] default = ["std"] std = ["alloc", "serde?/std"] diff --git a/examples/history.rs b/examples/history.rs index bfdc0179..0335f245 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -1,5 +1,12 @@ +use chrono::{DateTime, Local}; +use std::time::SystemTime; use undo::{Add, History}; +fn custom_st_fmt(_: SystemTime, at: SystemTime) -> String { + let time: DateTime = at.into(); + time.time().to_string() +} + fn main() { let mut target = String::new(); let mut history = History::new(); @@ -29,5 +36,5 @@ fn main() { history.go_to(&mut target, abdef_branch, abdef_current); assert_eq!(target, "abdef"); - println!("{}", history.display()); + println!("{}", history.display().set_st_fmt(&custom_st_fmt)); } diff --git a/examples/record.rs b/examples/record.rs index 068dad7b..6e0beb5f 100644 --- a/examples/record.rs +++ b/examples/record.rs @@ -1,5 +1,12 @@ +use chrono::{DateTime, Local}; +use std::time::SystemTime; use undo::{Add, Record}; +fn custom_st_fmt(_: SystemTime, at: SystemTime) -> String { + let time: DateTime = at.into(); + time.time().to_string() +} + fn main() { let mut target = String::new(); let mut record = Record::new(); @@ -17,5 +24,5 @@ fn main() { record.redo(&mut target); assert_eq!(target, "abc"); - println!("{}", record.display()); + println!("{}", record.display().set_st_fmt(&custom_st_fmt)); } diff --git a/src/format.rs b/src/format.rs index 8b33d0b5..c5e0fb7f 100644 --- a/src/format.rs +++ b/src/format.rs @@ -8,6 +8,12 @@ use core::fmt::{self, Write}; #[cfg(feature = "std")] use std::time::SystemTime; +#[cfg(feature = "std")] +pub(crate) fn default_st_fmt(now: SystemTime, at: SystemTime) -> String { + let elapsed = now.duration_since(at).unwrap_or_else(|e| e.duration()); + format!("{elapsed:.1?}") +} + #[derive(Copy, Clone, Debug)] pub(crate) struct Format { #[cfg(feature = "colored")] @@ -157,14 +163,7 @@ impl Format { } #[cfg(feature = "std")] - pub fn elapsed( - self, - f: &mut fmt::Formatter, - now: SystemTime, - earlier: SystemTime, - ) -> fmt::Result { - let elapsed = now.duration_since(earlier).unwrap_or_else(|e| e.duration()); - let string = format!("{elapsed:.1?}"); + pub fn elapsed(self, f: &mut fmt::Formatter, string: String) -> fmt::Result { #[cfg(feature = "colored")] if self.colored { return write!(f, " {}", string.yellow()); diff --git a/src/history/display.rs b/src/history/display.rs index 00a3d1f3..fbb579d7 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -7,9 +7,11 @@ use std::time::SystemTime; pub struct Display<'a, E, S> { history: &'a History, format: Format, + #[cfg(feature = "std")] + st_fmt: &'a dyn Fn(SystemTime, SystemTime) -> String, } -impl Display<'_, E, S> { +impl<'a, E, S> Display<'a, E, S> { /// Show colored output (on by default). /// /// Requires the `colored` feature to be enabled. @@ -36,6 +38,18 @@ impl Display<'_, E, S> { self.format.saved = on; self } + + /// Set the function used to format the elapsed time. + /// + /// The first input parameter is the current system time. + /// The second input parameter is the system time of the event. + pub fn set_st_fmt( + &mut self, + st_fmt: &'a dyn Fn(SystemTime, SystemTime) -> String, + ) -> &mut Self { + self.st_fmt = st_fmt; + self + } } impl Display<'_, E, S> { @@ -53,9 +67,10 @@ impl Display<'_, E, S> { #[cfg(feature = "std")] if let Some(entry) = entry { if self.format.detailed { - self.format.elapsed(f, now, entry.created_at)?; + let st_fmt = self.st_fmt; + self.format.elapsed(f, st_fmt(now, entry.created_at))?; self.format.text(f, ",", 3)?; - self.format.elapsed(f, now, entry.updated_at)?; + self.format.elapsed(f, st_fmt(now, entry.updated_at))?; } } @@ -139,6 +154,8 @@ impl<'a, E, S> From<&'a History> for Display<'a, E, S> { Display { history, format: Format::default(), + #[cfg(feature = "std")] + st_fmt: &crate::format::default_st_fmt, } } } diff --git a/src/record/display.rs b/src/record/display.rs index bffaf55d..879ad156 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -7,9 +7,11 @@ use std::time::SystemTime; pub struct Display<'a, E, S> { record: &'a Record, format: Format, + #[cfg(feature = "std")] + st_fmt: &'a dyn Fn(SystemTime, SystemTime) -> String, } -impl Display<'_, E, S> { +impl<'a, E, S> Display<'a, E, S> { /// Show colored output (on by default). /// /// Requires the `colored` feature to be enabled. @@ -36,6 +38,18 @@ impl Display<'_, E, S> { self.format.saved = on; self } + + /// Set the function used to format the elapsed time. + /// + /// The first input parameter is the current system time. + /// The second input parameter is the system time of the event. + pub fn set_st_fmt( + &mut self, + st_fmt: &'a dyn Fn(SystemTime, SystemTime) -> String, + ) -> &mut Self { + self.st_fmt = st_fmt; + self + } } impl Display<'_, E, S> { @@ -52,9 +66,10 @@ impl Display<'_, E, S> { #[cfg(feature = "std")] if let Some(entry) = entry { if self.format.detailed { - self.format.elapsed(f, now, entry.created_at)?; + let st_fmt = self.st_fmt; + self.format.elapsed(f, st_fmt(now, entry.created_at))?; self.format.text(f, ",", 3)?; - self.format.elapsed(f, now, entry.updated_at)?; + self.format.elapsed(f, st_fmt(now, entry.updated_at))?; } } @@ -84,6 +99,8 @@ impl<'a, E, S> From<&'a Record> for Display<'a, E, S> { Display { record, format: Format::default(), + #[cfg(feature = "std")] + st_fmt: &crate::format::default_st_fmt, } } } From 87afef123c2120c10d43a2d1714f02ad92d08d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Mon, 18 Sep 2023 13:09:01 +0200 Subject: [PATCH 20/33] Add missing feature gating --- src/add.rs | 1 + src/history/display.rs | 1 + src/lib.rs | 2 ++ src/record/display.rs | 1 + 4 files changed, 5 insertions(+) diff --git a/src/add.rs b/src/add.rs index de7c1798..432147a0 100644 --- a/src/add.rs +++ b/src/add.rs @@ -1,3 +1,4 @@ +use alloc::string::String; use core::fmt::{self, Display, Formatter}; /// This is the edit used in all the examples. diff --git a/src/history/display.rs b/src/history/display.rs index fbb579d7..5392822e 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -43,6 +43,7 @@ impl<'a, E, S> Display<'a, E, S> { /// /// The first input parameter is the current system time. /// The second input parameter is the system time of the event. + #[cfg(feature = "std")] pub fn set_st_fmt( &mut self, st_fmt: &'a dyn Fn(SystemTime, SystemTime) -> String, diff --git a/src/lib.rs b/src/lib.rs index a6da9b52..19f2494b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,7 @@ extern crate alloc; #[doc = include_str!("../README.md")] pub struct ReadmeDocTest; +#[cfg(feature = "alloc")] mod add; #[cfg(feature = "alloc")] mod entry; @@ -61,6 +62,7 @@ use format::Format; use serde::{Deserialize, Serialize}; #[doc(hidden)] +#[cfg(feature = "alloc")] pub use add::Add; #[cfg(feature = "alloc")] pub use history::History; diff --git a/src/record/display.rs b/src/record/display.rs index 879ad156..f1ff9218 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -43,6 +43,7 @@ impl<'a, E, S> Display<'a, E, S> { /// /// The first input parameter is the current system time. /// The second input parameter is the system time of the event. + #[cfg(feature = "std")] pub fn set_st_fmt( &mut self, st_fmt: &'a dyn Fn(SystemTime, SystemTime) -> String, From da217a4c1faae942e2d6beebb890d6ad16e1661f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Mon, 18 Sep 2023 18:41:05 +0200 Subject: [PATCH 21/33] Split edit and redo functionality from push functionality --- src/history.rs | 4 ++-- src/record.rs | 26 +++++++++++++++++++------- src/record/checkpoint.rs | 2 +- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/history.rs b/src/history.rs index 68eb32d8..5168d64d 100644 --- a/src/history.rs +++ b/src/history.rs @@ -170,7 +170,7 @@ impl History { pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { let at = self.at(); let saved = self.record.saved.filter(|&saved| saved > at.current); - let (output, merged, tail) = self.record.push(target, Entry::from(edit), E::edit); + let (output, merged, tail) = self.record.edit_and_push(target, edit.into()); // Check if the limit has been reached. if !merged && at.current == self.index() { let root = self.branch(); @@ -328,7 +328,7 @@ impl History { for entry in branch.entries { let current = self.index(); let saved = self.record.saved.filter(|&saved| saved > current); - let (_, _, entries) = self.record.push(target, entry, E::redo); + let (_, _, entries) = self.record.redo_and_push(target, entry); if !entries.is_empty() { self.branches .insert(self.root, Branch::new(new, current, entries)); diff --git a/src/record.rs b/src/record.rs index 5233aea4..404e4919 100644 --- a/src/record.rs +++ b/src/record.rs @@ -173,18 +173,31 @@ impl Record { impl Record { /// Pushes the edit on top of the record and executes its [`Edit::edit`] method. pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { - let (output, _, _) = self.push(target, Entry::from(edit), E::edit); + let (output, _, _) = self.edit_and_push(target, edit.into()); output } - pub(crate) fn push( + pub(crate) fn edit_and_push( &mut self, target: &mut E::Target, mut entry: Entry, - f: impl FnOnce(&mut E, &mut E::Target) -> E::Output, ) -> (E::Output, bool, VecDeque>) { - let output = f(&mut entry.edit, target); + let output = entry.edit.edit(target); + let (merged_or_annulled, tail) = self.push(entry); + (output, merged_or_annulled, tail) + } + + pub(crate) fn redo_and_push( + &mut self, + target: &mut E::Target, + mut entry: Entry, + ) -> (E::Output, bool, VecDeque>) { + let output = entry.redo(target); + let (merged_or_annulled, tail) = self.push(entry); + (output, merged_or_annulled, tail) + } + fn push(&mut self, entry: Entry) -> (bool, VecDeque>) { let old_index = self.index; let could_undo = self.can_undo(); let could_redo = self.can_redo(); @@ -212,8 +225,7 @@ impl Record { } else { self.index += 1; } - entry.edit = edit; - self.entries.push_back(entry); + self.entries.push_back(Entry { edit, ..entry }); false } }; @@ -223,7 +235,7 @@ impl Record { self.socket.emit_if(was_saved, || Signal::Saved(false)); self.socket .emit_if(old_index != self.index, || Signal::Index(self.index)); - (output, merged_or_annulled, tail) + (merged_or_annulled, tail) } /// Calls the [`Edit::undo`] method for the active edit and sets diff --git a/src/record/checkpoint.rs b/src/record/checkpoint.rs index 36b5716f..1f70be5a 100644 --- a/src/record/checkpoint.rs +++ b/src/record/checkpoint.rs @@ -33,7 +33,7 @@ impl Checkpoint<'_, E, S> { /// Calls the `apply` method. pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { let saved = self.record.saved; - let (output, _, tail) = self.record.push(target, Entry::from(edit), E::edit); + let (output, _, tail) = self.record.edit_and_push(target, edit.into()); self.entries.push(CheckpointEntry::Edit(saved, tail)); output } From 15aa74e1bb9e314e2da5a5330b35d6f2f8ea41da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Mon, 18 Sep 2023 19:28:30 +0200 Subject: [PATCH 22/33] Rename current field to index --- examples/history.rs | 8 ++--- src/format.rs | 12 ++++---- src/history.rs | 69 ++++++++++++++++++++---------------------- src/history/display.rs | 2 +- src/history/queue.rs | 4 +-- src/lib.rs | 10 +++--- src/record/display.rs | 4 +-- src/socket.rs | 1 + 8 files changed, 54 insertions(+), 56 deletions(-) diff --git a/examples/history.rs b/examples/history.rs index 0335f245..4c7698a0 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -17,7 +17,7 @@ fn main() { assert_eq!(target, "abc"); let abc_branch = history.branch(); - let abc_current = history.index(); + let abc_index = history.index(); history.undo(&mut target); assert_eq!(target, "ab"); @@ -28,12 +28,12 @@ fn main() { assert_eq!(target, "abdef"); let abdef_branch = history.branch(); - let abdef_current = history.index(); + let abdef_index = history.index(); - history.go_to(&mut target, abc_branch, abc_current); + history.go_to(&mut target, abc_branch, abc_index); assert_eq!(target, "abc"); - history.go_to(&mut target, abdef_branch, abdef_current); + history.go_to(&mut target, abdef_branch, abdef_index); assert_eq!(target, "abdef"); println!("{}", history.display().set_st_fmt(&custom_st_fmt)); diff --git a/src/format.rs b/src/format.rs index c5e0fb7f..a6cb6184 100644 --- a/src/format.rs +++ b/src/format.rs @@ -93,16 +93,16 @@ impl Format { #[cfg(feature = "colored")] if self.colored { let position = if use_branch { - alloc::format!("{}:{}", at.branch, at.current) + alloc::format!("{}:{}", at.branch, at.index) } else { - alloc::format!("{}", at.current) + alloc::format!("{}", at.index) }; return write!(f, "{}", position.yellow().bold()); } if use_branch { - write!(f, "{}:{}", at.branch, at.current) + write!(f, "{}:{}", at.branch, at.index) } else { - write!(f, "{}", at.current) + write!(f, "{}", at.index) } } @@ -110,11 +110,11 @@ impl Format { self, f: &mut fmt::Formatter, at: At, - current: At, + head: At, saved: Option, ) -> fmt::Result { match ( - self.head && at == current, + self.head && at == head, self.saved && matches!(saved, Some(saved) if saved == at), ) { (true, true) => { diff --git a/src/history.rs b/src/history.rs index 5168d64d..a6a8d982 100644 --- a/src/history.rs +++ b/src/history.rs @@ -169,24 +169,26 @@ impl History { /// Pushes the [`Edit`] to the top of the history and executes its [`Edit::edit`] method. pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { let at = self.at(); - let saved = self.record.saved.filter(|&saved| saved > at.current); + let saved = self.record.saved.filter(|&saved| saved > at.index); let (output, merged, tail) = self.record.edit_and_push(target, edit.into()); + // Check if the limit has been reached. - if !merged && at.current == self.index() { + if !merged && at.index == self.index() { let root = self.branch(); self.rm_child(root, 0); self.branches .values_mut() .filter(|branch| branch.parent.branch == root) - .for_each(|branch| branch.parent.current -= 1); + .for_each(|branch| branch.parent.index -= 1); } + // Handle new branch. if !tail.is_empty() { let new = self.next; self.next += 1; self.branches - .insert(at.branch, Branch::new(new, at.current, tail)); - self.set_root(new, at.current, saved); + .insert(at.branch, Branch::new(new, at.index, tail)); + self.set_root(new, at.index, saved); } output } @@ -221,40 +223,40 @@ impl History { pub(crate) fn jump_to(&mut self, root: usize) { let mut branch = self.branches.remove(&root).unwrap(); debug_assert_eq!(branch.parent, self.at()); - let current = self.index(); - let saved = self.record.saved.filter(|&saved| saved > current); - let tail = self.record.entries.split_off(current); + let index = self.index(); + let saved = self.record.saved.filter(|&saved| saved > index); + let tail = self.record.entries.split_off(index); self.record.entries.append(&mut branch.entries); self.branches - .insert(self.root, Branch::new(root, current, tail)); - self.set_root(root, current, saved); + .insert(self.root, Branch::new(root, index, tail)); + self.set_root(root, index, saved); } - fn set_root(&mut self, root: usize, current: usize, saved: Option) { + fn set_root(&mut self, root: usize, index: usize, saved: Option) { let old = self.branch(); self.root = root; debug_assert_ne!(old, root); // Handle the child branches. self.branches .values_mut() - .filter(|branch| branch.parent.branch == old && branch.parent.current <= current) + .filter(|branch| branch.parent.branch == old && branch.parent.index <= index) .for_each(|branch| branch.parent.branch = root); match (self.record.saved, saved, self.saved) { - (Some(_), None, None) | (None, None, Some(_)) => self.swap_saved(root, old, current), + (Some(_), None, None) | (None, None, Some(_)) => self.swap_saved(root, old, index), (None, Some(_), None) => { self.record.saved = saved; - self.swap_saved(old, root, current); + self.swap_saved(old, root, index); } (None, None, None) => (), _ => unreachable!(), } } - fn swap_saved(&mut self, old: usize, new: usize, current: usize) { + fn swap_saved(&mut self, old: usize, new: usize, index: usize) { debug_assert_ne!(old, new); - if let Some(At { current: saved, .. }) = self + if let Some(At { index: saved, .. }) = self .saved - .filter(|at| at.branch == new && at.current <= current) + .filter(|at| at.branch == new && at.index <= index) { self.saved = None; self.record.saved = Some(saved); @@ -266,12 +268,12 @@ impl History { } } - fn rm_child(&mut self, branch: usize, current: usize) { + fn rm_child(&mut self, branch: usize, index: usize) { // We need to check if any of the branches had the removed node as root. let mut dead: Vec<_> = self .branches .iter() - .filter(|&(_, child)| child.parent == At::new(branch, current)) + .filter(|&(_, child)| child.parent == At::new(branch, index)) .map(|(&id, _)| id) .collect(); while let Some(parent) = dead.pop() { @@ -303,16 +305,11 @@ impl History { Some(path.into_iter().rev()) } - /// Repeatedly calls [`Edit::undo`] or [`Edit::redo`] until the edit in `branch` at `current` is reached. - pub fn go_to( - &mut self, - target: &mut E::Target, - branch: usize, - current: usize, - ) -> Vec { + /// Repeatedly calls [`Edit::undo`] or [`Edit::redo`] until the edit in `branch` at `index` is reached. + pub fn go_to(&mut self, target: &mut E::Target, branch: usize, index: usize) -> Vec { let root = self.root; if root == branch { - return self.record.go_to(target, current); + return self.record.go_to(target, index); } // Walk the path from `root` to `branch`. @@ -320,23 +317,23 @@ impl History { let Some(path) = self.mk_path(branch) else { return Vec::new(); }; + for (new, branch) in path { - // Walk to `branch.current` either by undoing or redoing. - let o = self.record.go_to(target, branch.parent.current); + let o = self.record.go_to(target, branch.parent.index); outputs.extend(o); // Apply the edits in the branch and move older edits into their own branch. for entry in branch.entries { - let current = self.index(); - let saved = self.record.saved.filter(|&saved| saved > current); + let index = self.index(); + let saved = self.record.saved.filter(|&saved| saved > index); let (_, _, entries) = self.record.redo_and_push(target, entry); if !entries.is_empty() { self.branches - .insert(self.root, Branch::new(new, current, entries)); - self.set_root(new, current, saved); + .insert(self.root, Branch::new(new, index, entries)); + self.set_root(new, index, saved); } } } - let o = self.record.go_to(target, current); + let o = self.record.go_to(target, index); outputs.extend(o); outputs } @@ -383,9 +380,9 @@ pub(crate) struct Branch { } impl Branch { - fn new(branch: usize, current: usize, entries: VecDeque>) -> Branch { + fn new(branch: usize, index: usize, entries: VecDeque>) -> Branch { Branch { - parent: At::new(branch, current), + parent: At::new(branch, index), entries, } } diff --git a/src/history/display.rs b/src/history/display.rs index 5392822e..342f6342 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -114,7 +114,7 @@ impl Display<'_, E, S> { .filter(|(_, branch)| branch.parent == at) { for (j, entry) in branch.entries.iter().enumerate().rev() { - let at = At::new(i, j + branch.parent.current + 1); + let at = At::new(i, j + branch.parent.index + 1); self.fmt_graph( f, at, diff --git a/src/history/queue.rs b/src/history/queue.rs index cf92f91c..473b10a4 100644 --- a/src/history/queue.rs +++ b/src/history/queue.rs @@ -16,8 +16,8 @@ enum QueueEntry { /// # fn main() { /// # use undo::{Add, History}; /// let mut string = String::new(); -/// let mut record = History::new(); -/// let mut queue = record.queue(); +/// let mut history = History::new(); +/// let mut queue = history.queue(); /// /// queue.edit(Add('a')); /// queue.edit(Add('b')); diff --git a/src/lib.rs b/src/lib.rs index 19f2494b..1ae61aab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,16 +124,16 @@ pub enum Merged { #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)] struct At { branch: usize, - current: usize, + index: usize, } #[cfg(feature = "alloc")] impl At { - const fn new(branch: usize, current: usize) -> At { - At { branch, current } + const fn new(branch: usize, index: usize) -> At { + At { branch, index } } - const fn root(current: usize) -> At { - At::new(0, current) + const fn root(index: usize) -> At { + At::new(0, index) } } diff --git a/src/record/display.rs b/src/record/display.rs index f1ff9218..39b46c32 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -57,11 +57,11 @@ impl Display<'_, E, S> { fn fmt_list( &self, f: &mut fmt::Formatter, - current: usize, + index: usize, entry: Option<&Entry>, #[cfg(feature = "std")] now: SystemTime, ) -> fmt::Result { - let at = At::root(current); + let at = At::root(index); self.format.position(f, at, false)?; #[cfg(feature = "std")] diff --git a/src/socket.rs b/src/socket.rs index e6549b09..5de741ff 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -8,6 +8,7 @@ use std::sync::mpsc::{Sender, SyncSender}; /// Slot wrapper that adds some additional functionality. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[repr(transparent)] #[derive(Clone, Debug)] pub(crate) struct Socket(Option); From 5e0d87aa8441358866e73a1a918bca9c0b61df0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Tue, 19 Sep 2023 13:35:30 +0200 Subject: [PATCH 23/33] When merging two commands use the updated_at field for the deleted command --- src/entry.rs | 39 +++++++++++++++++++++++++++++---------- src/record.rs | 10 +++++----- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/entry.rs b/src/entry.rs index 41c87a77..b21ed5ea 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,4 +1,4 @@ -use crate::Edit; +use crate::{Edit, Merged}; use core::fmt::{self, Debug, Display, Formatter}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -8,7 +8,7 @@ use std::time::SystemTime; /// Wrapper around an [`Edit`] command that contains additional metadata. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Hash, Eq, PartialEq)] -pub struct Entry { +pub(crate) struct Entry { pub edit: E, #[cfg(feature = "std")] pub created_at: SystemTime, @@ -17,23 +17,42 @@ pub struct Entry { } impl Entry { + pub fn edit(&mut self, target: &mut E::Target) -> E::Output { + self.edit.edit(target) + } + pub fn undo(&mut self, target: &mut E::Target) -> E::Output { - self.pre_edit(); + #[cfg(feature = "std")] + { + self.updated_at = SystemTime::now(); + } self.edit.undo(target) } pub fn redo(&mut self, target: &mut E::Target) -> E::Output { - self.pre_edit(); + #[cfg(feature = "std")] + { + self.updated_at = SystemTime::now(); + } self.edit.redo(target) } - #[cfg(feature = "std")] - fn pre_edit(&mut self) { - self.updated_at = SystemTime::now(); + pub fn merge(&mut self, other: Self) -> Merged + where + Self: Sized, + { + match self.edit.merge(other.edit) { + Merged::Yes => { + #[cfg(feature = "std")] + { + self.updated_at = other.updated_at; + } + Merged::Yes + } + Merged::No(edit) => Merged::No(Self { edit, ..other }), + Merged::Annul => Merged::Annul, + } } - - #[cfg(not(feature = "std"))] - fn pre_edit(&mut self) {} } impl From for Entry { diff --git a/src/record.rs b/src/record.rs index 404e4919..3b6ceb67 100644 --- a/src/record.rs +++ b/src/record.rs @@ -182,7 +182,7 @@ impl Record { target: &mut E::Target, mut entry: Entry, ) -> (E::Output, bool, VecDeque>) { - let output = entry.edit.edit(target); + let output = entry.edit(target); let (merged_or_annulled, tail) = self.push(entry); (output, merged_or_annulled, tail) } @@ -206,8 +206,8 @@ impl Record { let tail = self.rm_tail(); // Try to merge unless the target is in a saved state. let merged = match self.entries.back_mut() { - Some(last) if !was_saved => last.edit.merge(entry.edit), - _ => Merged::No(entry.edit), + Some(last) if !was_saved => last.merge(entry), + _ => Merged::No(entry), }; let merged_or_annulled = match merged { @@ -217,7 +217,7 @@ impl Record { self.index -= 1; true } - Merged::No(edit) => { + Merged::No(entry) => { // If limit is reached, pop off the first edit command. if self.limit() == self.index { self.entries.pop_front(); @@ -225,7 +225,7 @@ impl Record { } else { self.index += 1; } - self.entries.push_back(Entry { edit, ..entry }); + self.entries.push_back(entry); false } }; From c97821cbb1998ce9411aba807acf80fee6d25421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Wed, 20 Sep 2023 10:10:20 +0200 Subject: [PATCH 24/33] Rename Signal to Event --- src/history.rs | 8 ++++---- src/lib.rs | 2 +- src/record.rs | 48 +++++++++++++++++++++---------------------- src/socket.rs | 56 ++++++++++++++++++++++++-------------------------- 4 files changed, 56 insertions(+), 58 deletions(-) diff --git a/src/history.rs b/src/history.rs index a6a8d982..c1ebd532 100644 --- a/src/history.rs +++ b/src/history.rs @@ -10,7 +10,7 @@ pub use checkpoint::Checkpoint; pub use display::Display; pub use queue::Queue; -use crate::socket::{Signal, Slot}; +use crate::socket::{Event, Slot}; use crate::{At, Edit, Entry, Record}; use alloc::collections::{BTreeMap, VecDeque}; use alloc::string::{String, ToString}; @@ -105,7 +105,7 @@ impl History { self.record.limit() } - /// Sets how the signal should be handled when the state changes. + /// Sets how the event should be handled when the state changes. pub fn connect(&mut self, slot: S) -> Option { self.record.connect(slot) } @@ -260,11 +260,11 @@ impl History { { self.saved = None; self.record.saved = Some(saved); - self.record.socket.emit(|| Signal::Saved(true)); + self.record.socket.emit(|| Event::Saved(true)); } else if let Some(saved) = self.record.saved { self.saved = Some(At::new(old, saved)); self.record.saved = None; - self.record.socket.emit(|| Signal::Saved(false)); + self.record.socket.emit(|| Event::Saved(false)); } } diff --git a/src/lib.rs b/src/lib.rs index 1ae61aab..4ab49aa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,7 +69,7 @@ pub use history::History; #[cfg(feature = "alloc")] pub use record::Record; #[cfg(feature = "alloc")] -pub use socket::{Signal, Slot}; +pub use socket::{Event, Slot}; /// Base functionality for all edit commands. pub trait Edit { diff --git a/src/record.rs b/src/record.rs index 3b6ceb67..c3bdf086 100644 --- a/src/record.rs +++ b/src/record.rs @@ -11,7 +11,7 @@ pub use display::Display; pub use queue::Queue; use crate::socket::{Slot, Socket}; -use crate::{Edit, Entry, History, Merged, Signal}; +use crate::{Edit, Entry, Event, History, Merged}; use alloc::collections::VecDeque; use alloc::string::{String, ToString}; use alloc::vec::Vec; @@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize}; /// /// The record can roll the targets state backwards and forwards by using /// the undo and redo methods. In addition, the record can notify the user -/// about changes to the stack or the target through [`Signal`]. +/// about changes to the stack or the target through [`Event`]. /// The user can give the record a function that is called each time the state /// changes by using the [`Builder`]. /// @@ -112,7 +112,7 @@ impl Record { self.limit.get() } - /// Sets how the signal should be handled when the state changes. + /// Sets how the event should be handled when the state changes. pub fn connect(&mut self, slot: S) -> Option { self.socket.connect(Some(slot)) } @@ -230,11 +230,11 @@ impl Record { } }; - self.socket.emit_if(could_redo, || Signal::Redo(false)); - self.socket.emit_if(!could_undo, || Signal::Undo(true)); - self.socket.emit_if(was_saved, || Signal::Saved(false)); + self.socket.emit_if(could_redo, || Event::Redo(false)); + self.socket.emit_if(!could_undo, || Event::Undo(true)); + self.socket.emit_if(was_saved, || Event::Saved(false)); self.socket - .emit_if(old_index != self.index, || Signal::Index(self.index)); + .emit_if(old_index != self.index, || Event::Index(self.index)); (merged_or_annulled, tail) } @@ -247,12 +247,12 @@ impl Record { let output = self.entries[self.index - 1].undo(target); self.index -= 1; let is_saved = self.is_saved(); - self.socket.emit_if(old_index == 1, || Signal::Undo(false)); + self.socket.emit_if(old_index == 1, || Event::Undo(false)); self.socket - .emit_if(old_index == self.entries.len(), || Signal::Redo(true)); + .emit_if(old_index == self.entries.len(), || Event::Redo(true)); self.socket - .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); - self.socket.emit(|| Signal::Index(self.index)); + .emit_if(was_saved != is_saved, || Event::Saved(is_saved)); + self.socket.emit(|| Event::Index(self.index)); output }) } @@ -266,12 +266,12 @@ impl Record { let output = self.entries[self.index].redo(target); self.index += 1; let is_saved = self.is_saved(); - self.socket.emit_if(old_index == 0, || Signal::Undo(true)); + self.socket.emit_if(old_index == 0, || Event::Undo(true)); self.socket - .emit_if(old_index == self.len() - 1, || Signal::Redo(false)); + .emit_if(old_index == self.len() - 1, || Event::Redo(false)); self.socket - .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); - self.socket.emit(|| Signal::Index(self.index)); + .emit_if(was_saved != is_saved, || Event::Saved(is_saved)); + self.socket.emit(|| Event::Index(self.index)); output }) } @@ -281,10 +281,10 @@ impl Record { let was_saved = self.is_saved(); if saved { self.saved = Some(self.index); - self.socket.emit_if(!was_saved, || Signal::Saved(true)); + self.socket.emit_if(!was_saved, || Event::Saved(true)); } else { self.saved = None; - self.socket.emit_if(was_saved, || Signal::Saved(false)); + self.socket.emit_if(was_saved, || Event::Saved(false)); } } @@ -296,9 +296,9 @@ impl Record { self.entries.clear(); self.saved = self.is_saved().then_some(0); self.index = 0; - self.socket.emit_if(could_undo, || Signal::Undo(false)); - self.socket.emit_if(could_redo, || Signal::Redo(false)); - self.socket.emit_if(old_index != 0, || Signal::Index(0)); + self.socket.emit_if(could_undo, || Event::Undo(false)); + self.socket.emit_if(could_redo, || Event::Redo(false)); + self.socket.emit_if(old_index != 0, || Event::Index(0)); } /// Revert the changes done to the target since the saved state. @@ -338,13 +338,13 @@ impl Record { let is_saved = self.is_saved(); self.socket.connect(slot); self.socket - .emit_if(could_undo != can_undo, || Signal::Undo(can_undo)); + .emit_if(could_undo != can_undo, || Event::Undo(can_undo)); self.socket - .emit_if(could_redo != can_redo, || Signal::Redo(can_redo)); + .emit_if(could_redo != can_redo, || Event::Redo(can_redo)); self.socket - .emit_if(was_saved != is_saved, || Signal::Saved(is_saved)); + .emit_if(was_saved != is_saved, || Event::Saved(is_saved)); self.socket - .emit_if(old_index != self.index, || Signal::Index(self.index)); + .emit_if(old_index != self.index, || Event::Index(self.index)); outputs } diff --git a/src/socket.rs b/src/socket.rs index 5de741ff..809e5620 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -33,25 +33,25 @@ impl Default for Socket { } impl Socket { - pub fn emit(&mut self, signal: impl FnOnce() -> Signal) { + pub fn emit(&mut self, event: impl FnOnce() -> Event) { if let Some(slot) = &mut self.0 { - slot.on_emit(signal()); + slot.on_emit(event()); } } - pub fn emit_if(&mut self, cond: bool, signal: impl FnOnce() -> Signal) { + pub fn emit_if(&mut self, cond: bool, event: impl FnOnce() -> Event) { if cond { - self.emit(signal); + self.emit(event); } } } -/// The `Signal` describes the state change done to the data structures. +/// Describes an event on the structures. /// /// See [`Slot`] for more information. #[derive(Clone, Debug, PartialEq)] #[non_exhaustive] -pub enum Signal { +pub enum Event { /// Emitted when the structures ability to undo has changed. Undo(bool), /// Emitted when the structures ability to redo has changed. @@ -62,14 +62,12 @@ pub enum Signal { Index(usize), } -/// Use this to handle signals emitted. -/// -/// This allows you to trigger events on certain state changes. +/// Handles events. /// /// # Examples /// ``` /// # use std::sync::mpsc; -/// # use undo::{Add, Record, Signal}; +/// # use undo::{Add, Record, Event}; /// # fn main() { /// let (sender, receiver) = mpsc::channel(); /// let mut iter = receiver.try_iter(); @@ -80,44 +78,44 @@ pub enum Signal { /// .build(); /// /// record.edit(&mut target, Add('a')); -/// assert_eq!(iter.next(), Some(Signal::Undo(true))); -/// assert_eq!(iter.next(), Some(Signal::Saved(false))); -/// assert_eq!(iter.next(), Some(Signal::Index(1))); +/// assert_eq!(iter.next(), Some(Event::Undo(true))); +/// assert_eq!(iter.next(), Some(Event::Saved(false))); +/// assert_eq!(iter.next(), Some(Event::Index(1))); /// assert_eq!(iter.next(), None); /// /// record.undo(&mut target); -/// assert_eq!(iter.next(), Some(Signal::Undo(false))); -/// assert_eq!(iter.next(), Some(Signal::Redo(true))); -/// assert_eq!(iter.next(), Some(Signal::Saved(true))); -/// assert_eq!(iter.next(), Some(Signal::Index(0))); +/// assert_eq!(iter.next(), Some(Event::Undo(false))); +/// assert_eq!(iter.next(), Some(Event::Redo(true))); +/// assert_eq!(iter.next(), Some(Event::Saved(true))); +/// assert_eq!(iter.next(), Some(Event::Index(0))); /// assert_eq!(iter.next(), None); /// # } /// ``` pub trait Slot { - /// Receives a signal that describes the state change done to the data structures. - fn on_emit(&mut self, signal: Signal); + /// Receives an event that describes the state change done to the structures. + fn on_emit(&mut self, event: Event); } impl Slot for () { - fn on_emit(&mut self, _: Signal) {} + fn on_emit(&mut self, _: Event) {} } -impl Slot for F { - fn on_emit(&mut self, signal: Signal) { - self(signal) +impl Slot for F { + fn on_emit(&mut self, event: Event) { + self(event) } } #[cfg(feature = "std")] -impl Slot for Sender { - fn on_emit(&mut self, signal: Signal) { - self.send(signal).ok(); +impl Slot for Sender { + fn on_emit(&mut self, event: Event) { + self.send(event).ok(); } } #[cfg(feature = "std")] -impl Slot for SyncSender { - fn on_emit(&mut self, signal: Signal) { - self.send(signal).ok(); +impl Slot for SyncSender { + fn on_emit(&mut self, event: Event) { + self.send(event).ok(); } } From dc730d135b41096c28f59e7817a9105e46f248ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Wed, 20 Sep 2023 12:36:17 +0200 Subject: [PATCH 25/33] Make At public and change api to work with it --- README.md | 8 +-- examples/history.rs | 10 ++-- src/format.rs | 4 +- src/history.rs | 109 ++++++++++++++++++-------------------- src/history/checkpoint.rs | 6 +-- src/history/display.rs | 6 +-- src/lib.rs | 24 +++++---- src/record.rs | 2 +- src/record/display.rs | 6 +-- 9 files changed, 86 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 0eca83c4..88ef9817 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ [![Crates.io](https://img.shields.io/crates/v/undo.svg)](https://crates.io/crates/undo) [![Docs](https://docs.rs/undo/badge.svg)](https://docs.rs/undo) -> An implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), -> where all edits are done by creating objects that applies the modifications. -> All objects knows how to undo the changes it applies, and by using the provided data -> structures it is easy to undo and redo edits made to a target. +An implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), +where all edits are done by creating objects that applies the modifications. +All objects knows how to undo the changes it applies, and by using the provided data +structures it is easy to undo and redo edits made to a target. See the [documentation](https://docs.rs/undo) and [examples](https://github.com/evenorog/undo/tree/master/examples) for more information. diff --git a/examples/history.rs b/examples/history.rs index 4c7698a0..57abbcf8 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -16,8 +16,7 @@ fn main() { history.edit(&mut target, Add('c')); assert_eq!(target, "abc"); - let abc_branch = history.branch(); - let abc_index = history.index(); + let abc = history.head(); history.undo(&mut target); assert_eq!(target, "ab"); @@ -27,13 +26,12 @@ fn main() { history.edit(&mut target, Add('f')); assert_eq!(target, "abdef"); - let abdef_branch = history.branch(); - let abdef_index = history.index(); + let abdef = history.head(); - history.go_to(&mut target, abc_branch, abc_index); + history.go_to(&mut target, abc); assert_eq!(target, "abc"); - history.go_to(&mut target, abdef_branch, abdef_index); + history.go_to(&mut target, abdef); assert_eq!(target, "abdef"); println!("{}", history.display().set_st_fmt(&custom_st_fmt)); diff --git a/src/format.rs b/src/format.rs index a6cb6184..42019a7d 100644 --- a/src/format.rs +++ b/src/format.rs @@ -93,14 +93,14 @@ impl Format { #[cfg(feature = "colored")] if self.colored { let position = if use_branch { - alloc::format!("{}:{}", at.branch, at.index) + alloc::format!("{}:{}", at.root, at.index) } else { alloc::format!("{}", at.index) }; return write!(f, "{}", position.yellow().bold()); } if use_branch { - write!(f, "{}:{}", at.branch, at.index) + write!(f, "{}:{}", at.root, at.index) } else { write!(f, "{}", at.index) } diff --git a/src/history.rs b/src/history.rs index c1ebd532..c810be7c 100644 --- a/src/history.rs +++ b/src/history.rs @@ -33,7 +33,7 @@ use serde::{Deserialize, Serialize}; /// history.edit(&mut target, Add('a')); /// history.edit(&mut target, Add('b')); /// history.edit(&mut target, Add('c')); -/// let abc = history.branch(); +/// let abc = history.head(); /// /// history.undo(&mut target); /// history.undo(&mut target); @@ -45,7 +45,7 @@ use serde::{Deserialize, Serialize}; /// assert_eq!(target, "afg"); /// /// // We can now switch back to the original branch. -/// history.go_to(&mut target, abc, 3); +/// history.go_to(&mut target, abc); /// assert_eq!(target, "abc"); /// # } /// ``` @@ -130,14 +130,9 @@ impl History { self.record.can_redo() } - /// Returns the current branch. - pub fn branch(&self) -> usize { - self.root - } - - /// Returns the index of the next edit. - pub fn index(&self) -> usize { - self.record.index() + /// Returns the current position in the history. + pub fn head(&self) -> At { + At::new(self.root, self.record.index) } /// Returns a structure for configurable formatting of the history. @@ -159,26 +154,22 @@ impl History { pub fn checkpoint(&mut self) -> Checkpoint { Checkpoint::from(self) } - - pub(crate) fn at(&self) -> At { - At::new(self.root, self.index()) - } } impl History { /// Pushes the [`Edit`] to the top of the history and executes its [`Edit::edit`] method. pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { - let at = self.at(); - let saved = self.record.saved.filter(|&saved| saved > at.index); + let head = self.head(); + let saved = self.record.saved.filter(|&saved| saved > head.index); let (output, merged, tail) = self.record.edit_and_push(target, edit.into()); // Check if the limit has been reached. - if !merged && at.index == self.index() { - let root = self.branch(); + if !merged && head.index == self.record.index { + let root = self.root; self.rm_child(root, 0); self.branches .values_mut() - .filter(|branch| branch.parent.branch == root) + .filter(|branch| branch.parent.root == root) .for_each(|branch| branch.parent.index -= 1); } @@ -187,8 +178,8 @@ impl History { let new = self.next; self.next += 1; self.branches - .insert(at.branch, Branch::new(new, at.index, tail)); - self.set_root(new, at.index, saved); + .insert(head.root, Branch::new(new, head.index, tail)); + self.set_root(new, head.index, saved); } output } @@ -222,8 +213,8 @@ impl History { pub(crate) fn jump_to(&mut self, root: usize) { let mut branch = self.branches.remove(&root).unwrap(); - debug_assert_eq!(branch.parent, self.at()); - let index = self.index(); + debug_assert_eq!(branch.parent, self.head()); + let index = self.record.index; let saved = self.record.saved.filter(|&saved| saved > index); let tail = self.record.entries.split_off(index); self.record.entries.append(&mut branch.entries); @@ -233,14 +224,14 @@ impl History { } fn set_root(&mut self, root: usize, index: usize, saved: Option) { - let old = self.branch(); + let old = self.root; self.root = root; debug_assert_ne!(old, root); // Handle the child branches. self.branches .values_mut() - .filter(|branch| branch.parent.branch == old && branch.parent.index <= index) - .for_each(|branch| branch.parent.branch = root); + .filter(|branch| branch.parent.root == old && branch.parent.index <= index) + .for_each(|branch| branch.parent.root = root); match (self.record.saved, saved, self.saved) { (Some(_), None, None) | (None, None, Some(_)) => self.swap_saved(root, old, index), (None, Some(_), None) => { @@ -254,9 +245,8 @@ impl History { fn swap_saved(&mut self, old: usize, new: usize, index: usize) { debug_assert_ne!(old, new); - if let Some(At { index: saved, .. }) = self - .saved - .filter(|at| at.branch == new && at.index <= index) + if let Some(At { index: saved, .. }) = + self.saved.filter(|at| at.root == new && at.index <= index) { self.saved = None; self.record.saved = Some(saved); @@ -279,26 +269,26 @@ impl History { while let Some(parent) = dead.pop() { // Remove the dead branch. self.branches.remove(&parent).unwrap(); - self.saved = self.saved.filter(|saved| saved.branch != parent); + self.saved = self.saved.filter(|saved| saved.root != parent); // Add the children of the dead branch so they are removed too. dead.extend( self.branches .iter() - .filter(|&(_, child)| child.parent.branch == parent) + .filter(|&(_, child)| child.parent.root == parent) .map(|(&id, _)| id), ) } } fn mk_path(&mut self, mut to: usize) -> Option)>> { - debug_assert_ne!(self.branch(), to); + debug_assert_ne!(self.root, to); let mut dest = self.branches.remove(&to)?; - let mut i = dest.parent.branch; + let mut i = dest.parent.root; let mut path = alloc::vec![(to, dest)]; - while i != self.branch() { + while i != self.root { dest = self.branches.remove(&i).unwrap(); to = i; - i = dest.parent.branch; + i = dest.parent.root; path.push((to, dest)); } @@ -306,15 +296,15 @@ impl History { } /// Repeatedly calls [`Edit::undo`] or [`Edit::redo`] until the edit in `branch` at `index` is reached. - pub fn go_to(&mut self, target: &mut E::Target, branch: usize, index: usize) -> Vec { + pub fn go_to(&mut self, target: &mut E::Target, at: At) -> Vec { let root = self.root; - if root == branch { - return self.record.go_to(target, index); + if root == at.root { + return self.record.go_to(target, at.index); } // Walk the path from `root` to `branch`. let mut outputs = Vec::new(); - let Some(path) = self.mk_path(branch) else { + let Some(path) = self.mk_path(at.root) else { return Vec::new(); }; @@ -323,7 +313,7 @@ impl History { outputs.extend(o); // Apply the edits in the branch and move older edits into their own branch. for entry in branch.entries { - let index = self.index(); + let index = self.record.index; let saved = self.record.saved.filter(|&saved| saved > index); let (_, _, entries) = self.record.redo_and_push(target, entry); if !entries.is_empty() { @@ -333,7 +323,7 @@ impl History { } } } - let o = self.record.go_to(target, index); + let o = self.record.go_to(target, at.index); outputs.extend(o); outputs } @@ -435,51 +425,56 @@ mod tests { history.undo(&mut target).unwrap(); history.undo(&mut target).unwrap(); assert_eq!(target, "abc"); - let abcde = history.branch(); + let abc = history.head(); + history.edit(&mut target, Add('f')); history.edit(&mut target, Add('g')); assert_eq!(target, "abcfg"); + let abcfg = history.head(); + history.undo(&mut target).unwrap(); - let abcfg = history.branch(); history.edit(&mut target, Add('h')); history.edit(&mut target, Add('i')); history.edit(&mut target, Add('j')); assert_eq!(target, "abcfhij"); + let abcfhij = history.head(); + history.undo(&mut target).unwrap(); - let abcfhij = history.branch(); history.edit(&mut target, Add('k')); assert_eq!(target, "abcfhik"); + let abcfhik = history.head(); + history.undo(&mut target).unwrap(); - let abcfhik = history.branch(); history.edit(&mut target, Add('l')); assert_eq!(target, "abcfhil"); history.edit(&mut target, Add('m')); assert_eq!(target, "abcfhilm"); - let abcfhilm = history.branch(); - history.go_to(&mut target, abcde, 2); + let abcfhilm = history.head(); + history.go_to(&mut target, At::new(abc.root, 2)); history.edit(&mut target, Add('n')); history.edit(&mut target, Add('o')); assert_eq!(target, "abno"); + let abno = history.head(); + history.undo(&mut target).unwrap(); - let abno = history.branch(); history.edit(&mut target, Add('p')); history.edit(&mut target, Add('q')); assert_eq!(target, "abnpq"); - let abnpq = history.branch(); - history.go_to(&mut target, abcde, 5); - assert_eq!(target, "abcde"); - history.go_to(&mut target, abcfg, 5); + let abnpq = history.head(); + history.go_to(&mut target, abc); + assert_eq!(target, "abc"); + history.go_to(&mut target, abcfg); assert_eq!(target, "abcfg"); - history.go_to(&mut target, abcfhij, 7); + history.go_to(&mut target, abcfhij); assert_eq!(target, "abcfhij"); - history.go_to(&mut target, abcfhik, 7); + history.go_to(&mut target, abcfhik); assert_eq!(target, "abcfhik"); - history.go_to(&mut target, abcfhilm, 8); + history.go_to(&mut target, abcfhilm); assert_eq!(target, "abcfhilm"); - history.go_to(&mut target, abno, 4); + history.go_to(&mut target, abno); assert_eq!(target, "abno"); - history.go_to(&mut target, abnpq, 5); + history.go_to(&mut target, abnpq); assert_eq!(target, "abnpq"); } } diff --git a/src/history/checkpoint.rs b/src/history/checkpoint.rs index bea03ad5..fa90aafe 100644 --- a/src/history/checkpoint.rs +++ b/src/history/checkpoint.rs @@ -31,8 +31,8 @@ impl Checkpoint<'_, E, S> { impl Checkpoint<'_, E, S> { /// Calls the [`History::edit`] method. pub fn edit(&mut self, target: &mut E::Target, edit: E) -> E::Output { - let branch = self.history.branch(); - self.entries.push(CheckpointEntry::Edit(branch)); + let root = self.history.root; + self.entries.push(CheckpointEntry::Edit(root)); self.history.edit(target, edit) } @@ -59,7 +59,7 @@ impl Checkpoint<'_, E, S> { .filter_map(|entry| match entry { CheckpointEntry::Edit(branch) => { let output = self.history.undo(target)?; - let root = self.history.branch(); + let root = self.history.root; if root == branch { self.history.record.entries.pop_back(); } else { diff --git a/src/history/display.rs b/src/history/display.rs index 342f6342..ec8c1c92 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -78,11 +78,11 @@ impl Display<'_, E, S> { self.format.labels( f, at, - self.history.at(), + self.history.head(), self.history .record .saved - .map(|saved| At::new(self.history.branch(), saved)) + .map(|saved| At::new(self.history.root, saved)) .or(self.history.saved), )?; @@ -165,7 +165,7 @@ impl fmt::Display for Display<'_, E, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[cfg(feature = "std")] let now = SystemTime::now(); - let branch = self.history.branch(); + let branch = self.history.root; for (i, entry) in self.history.record.entries.iter().enumerate().rev() { let at = At::new(branch, i + 1); self.fmt_graph( diff --git a/src/lib.rs b/src/lib.rs index 4ab49aa4..76145bda 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ //! **An undo-redo library.** //! -//! > An implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), -//! > where all edits are done by creating objects that applies the modifications. -//! > All objects knows how to undo the changes it applies, and by using the provided data -//! > structures it is easy to undo and redo edits made to a target. +//! An implementation of the [command pattern](https://en.wikipedia.org/wiki/Command_pattern), +//! where all edits are done by creating objects that applies the modifications. +//! All objects knows how to undo the changes it applies, and by using the provided data +//! structures it is easy to undo and redo edits made to a target. //! //! See the [examples](https://github.com/evenorog/undo/tree/master/examples) for more information. //! @@ -122,18 +122,22 @@ pub enum Merged { #[cfg(feature = "alloc")] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)] -struct At { - branch: usize, - index: usize, +pub struct At { + /// The root branch. + pub root: usize, + /// The index of edit. + pub index: usize, } #[cfg(feature = "alloc")] impl At { - const fn new(branch: usize, index: usize) -> At { - At { branch, index } + /// Creates a new `At` with the provided root and index. + pub const fn new(root: usize, index: usize) -> At { + At { root, index } } - const fn root(index: usize) -> At { + /// Used for records to create a rootless `At`. + const fn rootless(index: usize) -> At { At::new(0, index) } } diff --git a/src/record.rs b/src/record.rs index c3bdf086..e19525d6 100644 --- a/src/record.rs +++ b/src/record.rs @@ -313,7 +313,7 @@ impl Record { return Vec::new(); } - let old_index = self.index(); + let old_index = self.index; let could_undo = self.can_undo(); let could_redo = self.can_redo(); let was_saved = self.is_saved(); diff --git a/src/record/display.rs b/src/record/display.rs index 39b46c32..b77b81b0 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -61,7 +61,7 @@ impl Display<'_, E, S> { entry: Option<&Entry>, #[cfg(feature = "std")] now: SystemTime, ) -> fmt::Result { - let at = At::root(index); + let at = At::rootless(index); self.format.position(f, at, false)?; #[cfg(feature = "std")] @@ -77,8 +77,8 @@ impl Display<'_, E, S> { self.format.labels( f, at, - At::root(self.record.index()), - self.record.saved.map(At::root), + At::rootless(self.record.index), + self.record.saved.map(At::rootless), )?; if let Some(entry) = entry { From 85047081a99c20bafcf141c72521b71a26050868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Wed, 20 Sep 2023 16:52:14 +0200 Subject: [PATCH 26/33] Improve documentation --- examples/history.rs | 4 ++-- examples/record.rs | 4 ++-- src/history/display.rs | 2 +- src/record/display.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/history.rs b/examples/history.rs index 57abbcf8..e6353138 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -3,8 +3,8 @@ use std::time::SystemTime; use undo::{Add, History}; fn custom_st_fmt(_: SystemTime, at: SystemTime) -> String { - let time: DateTime = at.into(); - time.time().to_string() + let dt = DateTime::::from(at); + dt.time().to_string() } fn main() { diff --git a/examples/record.rs b/examples/record.rs index 6e0beb5f..5a2fea17 100644 --- a/examples/record.rs +++ b/examples/record.rs @@ -3,8 +3,8 @@ use std::time::SystemTime; use undo::{Add, Record}; fn custom_st_fmt(_: SystemTime, at: SystemTime) -> String { - let time: DateTime = at.into(); - time.time().to_string() + let dt = DateTime::::from(at); + dt.time().to_string() } fn main() { diff --git a/src/history/display.rs b/src/history/display.rs index ec8c1c92..c6956f12 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -39,7 +39,7 @@ impl<'a, E, S> Display<'a, E, S> { self } - /// Set the function used to format the elapsed time. + /// Sets the format used to display [`SystemTime`]s. /// /// The first input parameter is the current system time. /// The second input parameter is the system time of the event. diff --git a/src/record/display.rs b/src/record/display.rs index b77b81b0..f907ebaf 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -39,7 +39,7 @@ impl<'a, E, S> Display<'a, E, S> { self } - /// Set the function used to format the elapsed time. + /// Sets the format used to display [`SystemTime`]s. /// /// The first input parameter is the current system time. /// The second input parameter is the system time of the event. From e7254c54e3d87c3b1e6d8ff49744aeb63dcf8877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Wed, 20 Sep 2023 20:37:19 +0200 Subject: [PATCH 27/33] Show just updated_at timestamp in display --- examples/history.rs | 2 +- examples/record.rs | 28 ------------------------- src/entry.rs | 6 +++--- src/format.rs | 47 ++++++++---------------------------------- src/history/display.rs | 5 ++--- src/record/display.rs | 5 ++--- 6 files changed, 17 insertions(+), 76 deletions(-) delete mode 100644 examples/record.rs diff --git a/examples/history.rs b/examples/history.rs index e6353138..0577eb51 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -4,7 +4,7 @@ use undo::{Add, History}; fn custom_st_fmt(_: SystemTime, at: SystemTime) -> String { let dt = DateTime::::from(at); - dt.time().to_string() + dt.format("%H:%M:%S").to_string() } fn main() { diff --git a/examples/record.rs b/examples/record.rs deleted file mode 100644 index 5a2fea17..00000000 --- a/examples/record.rs +++ /dev/null @@ -1,28 +0,0 @@ -use chrono::{DateTime, Local}; -use std::time::SystemTime; -use undo::{Add, Record}; - -fn custom_st_fmt(_: SystemTime, at: SystemTime) -> String { - let dt = DateTime::::from(at); - dt.time().to_string() -} - -fn main() { - let mut target = String::new(); - let mut record = Record::new(); - - record.edit(&mut target, Add('a')); - record.edit(&mut target, Add('b')); - record.edit(&mut target, Add('c')); - assert_eq!(target, "abc"); - - record.undo(&mut target); - record.undo(&mut target); - assert_eq!(target, "a"); - - record.redo(&mut target); - record.redo(&mut target); - assert_eq!(target, "abc"); - - println!("{}", record.display().set_st_fmt(&custom_st_fmt)); -} diff --git a/src/entry.rs b/src/entry.rs index b21ed5ea..0e877233 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -58,11 +58,11 @@ impl Entry { impl From for Entry { #[cfg(feature = "std")] fn from(edit: E) -> Self { - let at = SystemTime::now(); + let now = SystemTime::now(); Entry { edit, - created_at: at, - updated_at: at, + created_at: now, + updated_at: now, } } diff --git a/src/format.rs b/src/format.rs index 42019a7d..7b5848fb 100644 --- a/src/format.rs +++ b/src/format.rs @@ -61,32 +61,16 @@ impl Format { } pub fn mark(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result { - #[cfg(feature = "colored")] - if self.colored { - return write!(f, "{} ", "*".color(color_of_level(level))); - } - f.write_str("* ") + self.text(f, "* ", level) } pub fn edge(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result { - #[cfg(feature = "colored")] - if self.colored { - return write!(f, "{}", "|".color(color_of_level(level))); - } - f.write_char('|') + self.text(f, "|", level) } pub fn split(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result { - #[cfg(feature = "colored")] - if self.colored { - return write!( - f, - "{}{}", - "|".color(color_of_level(level)), - "/".color(color_of_level(level + 1)) - ); - } - f.write_str("|/") + self.text(f, "|", level)?; + self.text(f, "/", level + 1) } pub fn position(self, f: &mut fmt::Formatter, at: At, use_branch: bool) -> fmt::Result { @@ -97,7 +81,7 @@ impl Format { } else { alloc::format!("{}", at.index) }; - return write!(f, "{}", position.yellow().bold()); + return write!(f, "{}", position.yellow()); } if use_branch { write!(f, "{}:{}", at.root, at.index) @@ -124,9 +108,9 @@ impl Format { f, " {}{}{} {}{}", "[".yellow(), - "HEAD".cyan().bold(), + "HEAD".cyan(), ",".yellow(), - "SAVED".green().bold(), + "SAVED".green(), "]".yellow() ); } @@ -135,26 +119,14 @@ impl Format { (true, false) => { #[cfg(feature = "colored")] if self.colored { - return write!( - f, - " {}{}{}", - "[".yellow(), - "HEAD".cyan().bold(), - "]".yellow() - ); + return write!(f, " {}{}{}", "[".yellow(), "HEAD".cyan(), "]".yellow()); } f.write_str(" [HEAD]") } (false, true) => { #[cfg(feature = "colored")] if self.colored { - return write!( - f, - " {}{}{}", - "[".yellow(), - "SAVED".green().bold(), - "]".yellow() - ); + return write!(f, " {}{}{}", "[".yellow(), "SAVED".green(), "]".yellow()); } f.write_str(" [SAVED]") } @@ -171,7 +143,6 @@ impl Format { write!(f, " {string}") } - #[cfg(feature = "std")] pub fn text(self, f: &mut fmt::Formatter, text: &str, level: usize) -> fmt::Result { #[cfg(feature = "colored")] if self.colored { diff --git a/src/history/display.rs b/src/history/display.rs index c6956f12..3d99cda9 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -69,9 +69,8 @@ impl Display<'_, E, S> { if let Some(entry) = entry { if self.format.detailed { let st_fmt = self.st_fmt; - self.format.elapsed(f, st_fmt(now, entry.created_at))?; - self.format.text(f, ",", 3)?; - self.format.elapsed(f, st_fmt(now, entry.updated_at))?; + let updated_at = st_fmt(now, entry.updated_at); + self.format.elapsed(f, updated_at)?; } } diff --git a/src/record/display.rs b/src/record/display.rs index f907ebaf..b03f8d4f 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -68,9 +68,8 @@ impl Display<'_, E, S> { if let Some(entry) = entry { if self.format.detailed { let st_fmt = self.st_fmt; - self.format.elapsed(f, st_fmt(now, entry.created_at))?; - self.format.text(f, ",", 3)?; - self.format.elapsed(f, st_fmt(now, entry.updated_at))?; + let updated_at = st_fmt(now, entry.updated_at); + self.format.elapsed(f, updated_at)?; } } From 29178187ab308d448228c6539143cbff8d686e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Wed, 20 Sep 2023 21:19:39 +0200 Subject: [PATCH 28/33] Use if instead of match for labels method --- src/format.rs | 59 ++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/format.rs b/src/format.rs index 7b5848fb..8b16bf46 100644 --- a/src/format.rs +++ b/src/format.rs @@ -97,40 +97,37 @@ impl Format { head: At, saved: Option, ) -> fmt::Result { - match ( - self.head && at == head, - self.saved && matches!(saved, Some(saved) if saved == at), - ) { - (true, true) => { - #[cfg(feature = "colored")] - if self.colored { - return write!( - f, - " {}{}{} {}{}", - "[".yellow(), - "HEAD".cyan(), - ",".yellow(), - "SAVED".green(), - "]".yellow() - ); - } - f.write_str(" [HEAD, SAVED]") + let at_head = self.head && at == head; + let at_saved = self.saved && matches!(saved, Some(saved) if saved == at); + + if at_head && at_saved { + #[cfg(feature = "colored")] + if self.colored { + return write!( + f, + " {}{}{} {}{}", + "[".yellow(), + "HEAD".cyan(), + ",".yellow(), + "SAVED".green(), + "]".yellow() + ); } - (true, false) => { - #[cfg(feature = "colored")] - if self.colored { - return write!(f, " {}{}{}", "[".yellow(), "HEAD".cyan(), "]".yellow()); - } - f.write_str(" [HEAD]") + f.write_str(" [HEAD, SAVED]") + } else if at_head { + #[cfg(feature = "colored")] + if self.colored { + return write!(f, " {}{}{}", "[".yellow(), "HEAD".cyan(), "]".yellow()); } - (false, true) => { - #[cfg(feature = "colored")] - if self.colored { - return write!(f, " {}{}{}", "[".yellow(), "SAVED".green(), "]".yellow()); - } - f.write_str(" [SAVED]") + f.write_str(" [HEAD]") + } else if at_saved { + #[cfg(feature = "colored")] + if self.colored { + return write!(f, " {}{}{}", "[".yellow(), "SAVED".green(), "]".yellow()); } - (false, false) => Ok(()), + f.write_str(" [SAVED]") + } else { + Ok(()) } } From e5ebc90f1478affbd505f7af67c7b315c3f726fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Wed, 20 Sep 2023 21:39:44 +0200 Subject: [PATCH 29/33] Change format of at from 1:1 to 1-1 --- src/format.rs | 30 +++++++++++++++--------------- src/history/display.rs | 2 +- src/record/display.rs | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/format.rs b/src/format.rs index 8b16bf46..4eb9bc01 100644 --- a/src/format.rs +++ b/src/format.rs @@ -36,6 +36,14 @@ impl Default for Format { } impl Format { + pub fn level_text(self, f: &mut fmt::Formatter, text: &str, level: usize) -> fmt::Result { + #[cfg(feature = "colored")] + if self.colored { + return write!(f, "{}", text.color(color_of_level(level))); + } + f.write_str(text) + } + pub fn message( self, f: &mut fmt::Formatter, @@ -61,30 +69,30 @@ impl Format { } pub fn mark(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result { - self.text(f, "* ", level) + self.level_text(f, "* ", level) } pub fn edge(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result { - self.text(f, "|", level) + self.level_text(f, "|", level) } pub fn split(self, f: &mut fmt::Formatter, level: usize) -> fmt::Result { - self.text(f, "|", level)?; - self.text(f, "/", level + 1) + self.level_text(f, "|", level)?; + self.level_text(f, "/", level + 1) } - pub fn position(self, f: &mut fmt::Formatter, at: At, use_branch: bool) -> fmt::Result { + pub fn at(self, f: &mut fmt::Formatter, at: At, use_branch: bool) -> fmt::Result { #[cfg(feature = "colored")] if self.colored { let position = if use_branch { - alloc::format!("{}:{}", at.root, at.index) + alloc::format!("{}-{}", at.root, at.index) } else { alloc::format!("{}", at.index) }; return write!(f, "{}", position.yellow()); } if use_branch { - write!(f, "{}:{}", at.root, at.index) + write!(f, "{}-{}", at.root, at.index) } else { write!(f, "{}", at.index) } @@ -139,14 +147,6 @@ impl Format { } write!(f, " {string}") } - - pub fn text(self, f: &mut fmt::Formatter, text: &str, level: usize) -> fmt::Result { - #[cfg(feature = "colored")] - if self.colored { - return write!(f, "{}", text.color(color_of_level(level))); - } - f.write_str(text) - } } #[cfg(feature = "colored")] diff --git a/src/history/display.rs b/src/history/display.rs index 3d99cda9..ddf04f3b 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -63,7 +63,7 @@ impl Display<'_, E, S> { #[cfg(feature = "std")] now: SystemTime, ) -> fmt::Result { self.format.mark(f, level)?; - self.format.position(f, at, true)?; + self.format.at(f, at, true)?; #[cfg(feature = "std")] if let Some(entry) = entry { diff --git a/src/record/display.rs b/src/record/display.rs index b03f8d4f..309ef605 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -62,7 +62,7 @@ impl Display<'_, E, S> { #[cfg(feature = "std")] now: SystemTime, ) -> fmt::Result { let at = At::rootless(index); - self.format.position(f, at, false)?; + self.format.at(f, at, false)?; #[cfg(feature = "std")] if let Some(entry) = entry { From 0281392678c9efe1aa5ec73de6899c552c8484a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Wed, 20 Sep 2023 22:14:44 +0200 Subject: [PATCH 30/33] Add index format method --- examples/record.rs | 22 ++++++++++++++++++++++ src/format.rs | 23 ++++++++++++----------- src/history/display.rs | 8 ++++---- src/lib.rs | 2 +- src/record/display.rs | 9 ++++----- 5 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 examples/record.rs diff --git a/examples/record.rs b/examples/record.rs new file mode 100644 index 00000000..d13d97f2 --- /dev/null +++ b/examples/record.rs @@ -0,0 +1,22 @@ +use undo::{Add, Record}; + +fn main() { + let mut target = String::new(); + let mut record = Record::new(); + + record.edit(&mut target, Add('a')); + record.edit(&mut target, Add('b')); + record.edit(&mut target, Add('c')); + record.edit(&mut target, Add('d')); + record.edit(&mut target, Add('e')); + record.edit(&mut target, Add('f')); + assert_eq!(target, "abcdef"); + + record.set_saved(true); + + record.undo(&mut target); + record.undo(&mut target); + assert_eq!(target, "abcd"); + + println!("{}", record.display()); +} diff --git a/src/format.rs b/src/format.rs index 4eb9bc01..9c611779 100644 --- a/src/format.rs +++ b/src/format.rs @@ -81,21 +81,22 @@ impl Format { self.level_text(f, "/", level + 1) } - pub fn at(self, f: &mut fmt::Formatter, at: At, use_branch: bool) -> fmt::Result { + pub fn index(self, f: &mut fmt::Formatter, index: usize) -> fmt::Result { #[cfg(feature = "colored")] if self.colored { - let position = if use_branch { - alloc::format!("{}-{}", at.root, at.index) - } else { - alloc::format!("{}", at.index) - }; - return write!(f, "{}", position.yellow()); + let string = index.to_string(); + return write!(f, "{}", string.yellow()); } - if use_branch { - write!(f, "{}-{}", at.root, at.index) - } else { - write!(f, "{}", at.index) + write!(f, "{index}") + } + + pub fn at(self, f: &mut fmt::Formatter, at: At) -> fmt::Result { + #[cfg(feature = "colored")] + if self.colored { + let string = alloc::format!("{}-{}", at.root, at.index); + return write!(f, "{}", string.yellow()); } + write!(f, "{}-{}", at.root, at.index) } pub fn labels( diff --git a/src/history/display.rs b/src/history/display.rs index ddf04f3b..83fa2eab 100644 --- a/src/history/display.rs +++ b/src/history/display.rs @@ -63,7 +63,7 @@ impl Display<'_, E, S> { #[cfg(feature = "std")] now: SystemTime, ) -> fmt::Result { self.format.mark(f, level)?; - self.format.at(f, at, true)?; + self.format.at(f, at)?; #[cfg(feature = "std")] if let Some(entry) = entry { @@ -164,9 +164,9 @@ impl fmt::Display for Display<'_, E, S> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[cfg(feature = "std")] let now = SystemTime::now(); - let branch = self.history.root; + let root = self.history.root; for (i, entry) in self.history.record.entries.iter().enumerate().rev() { - let at = At::new(branch, i + 1); + let at = At::new(root, i + 1); self.fmt_graph( f, at, @@ -178,7 +178,7 @@ impl fmt::Display for Display<'_, E, S> { } self.fmt_graph( f, - At::new(branch, 0), + At::new(root, 0), None, 0, #[cfg(feature = "std")] diff --git a/src/lib.rs b/src/lib.rs index 76145bda..71df821a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,7 +137,7 @@ impl At { } /// Used for records to create a rootless `At`. - const fn rootless(index: usize) -> At { + const fn no_root(index: usize) -> At { At::new(0, index) } } diff --git a/src/record/display.rs b/src/record/display.rs index 309ef605..9f71fc27 100644 --- a/src/record/display.rs +++ b/src/record/display.rs @@ -61,8 +61,7 @@ impl Display<'_, E, S> { entry: Option<&Entry>, #[cfg(feature = "std")] now: SystemTime, ) -> fmt::Result { - let at = At::rootless(index); - self.format.at(f, at, false)?; + self.format.index(f, index)?; #[cfg(feature = "std")] if let Some(entry) = entry { @@ -75,9 +74,9 @@ impl Display<'_, E, S> { self.format.labels( f, - at, - At::rootless(self.record.index), - self.record.saved.map(At::rootless), + At::no_root(index), + At::no_root(self.record.index), + self.record.saved.map(At::no_root), )?; if let Some(entry) = entry { From d01c3a5c9162b6d06a307f99aa44c04ba081ea20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Thu, 21 Sep 2023 21:20:48 +0200 Subject: [PATCH 31/33] Make history example into an interactive client example --- examples/history.rs | 74 ++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/examples/history.rs b/examples/history.rs index 0577eb51..2aaab7f6 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -1,38 +1,56 @@ use chrono::{DateTime, Local}; +use std::io; +use std::io::BufRead; use std::time::SystemTime; -use undo::{Add, History}; +use undo::{Add, At, History}; fn custom_st_fmt(_: SystemTime, at: SystemTime) -> String { let dt = DateTime::::from(at); dt.format("%H:%M:%S").to_string() } -fn main() { - let mut target = String::new(); - let mut history = History::new(); - - history.edit(&mut target, Add('a')); - history.edit(&mut target, Add('b')); - history.edit(&mut target, Add('c')); - assert_eq!(target, "abc"); - - let abc = history.head(); - - history.undo(&mut target); - assert_eq!(target, "ab"); - - history.edit(&mut target, Add('d')); - history.edit(&mut target, Add('e')); - history.edit(&mut target, Add('f')); - assert_eq!(target, "abdef"); +fn main() -> io::Result<()> { + let stdin = io::stdin(); + let mut stdin = stdin.lock(); - let abdef = history.head(); - - history.go_to(&mut target, abc); - assert_eq!(target, "abc"); - - history.go_to(&mut target, abdef); - assert_eq!(target, "abdef"); - - println!("{}", history.display().set_st_fmt(&custom_st_fmt)); + let mut target = String::new(); + let mut record = History::new(); + loop { + println!("Enter a string to edit. Use '-' to undo, '+' to redo, and '! i-j' for goto: "); + let mut string = String::new(); + let n = stdin.read_line(&mut string)?; + if n == 0 { + return Ok(()); + } + + // Clears the terminal. + print!("{}c", 27 as char); + + let mut chars = string.trim().chars(); + while let Some(c) = chars.next() { + if c == '!' { + let rest = chars.collect::(); + let mut at = rest + .trim() + .split('-') + .filter_map(|n| n.parse::().ok()); + + if let (Some(root), Some(index)) = (at.next(), at.next()) { + record.go_to(&mut target, At::new(root, index)); + } else { + println!("Expected input as '! i-j', e.g. '! 1-5'.\n"); + } + break; + } else if c == '<' { + record.undo(&mut target); + } else if c == '>' { + record.redo(&mut target); + } else { + record.edit(&mut target, Add(c)); + } + } + + println!("{}\n", record.display().set_st_fmt(&custom_st_fmt)); + println!("Target: {target}"); + } } From 7749fa99cd833ab4b9993f75f057eba8ecd9235a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Thu, 21 Sep 2023 22:38:09 +0200 Subject: [PATCH 32/33] Add save functionality to history example --- examples/history.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/examples/history.rs b/examples/history.rs index 2aaab7f6..6a8b8ea3 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -1,6 +1,5 @@ use chrono::{DateTime, Local}; use std::io; -use std::io::BufRead; use std::time::SystemTime; use undo::{Add, At, History}; @@ -11,12 +10,13 @@ fn custom_st_fmt(_: SystemTime, at: SystemTime) -> String { fn main() -> io::Result<()> { let stdin = io::stdin(); - let mut stdin = stdin.lock(); - let mut target = String::new(); - let mut record = History::new(); + let mut history = History::<_>::builder().limit(10).capacity(10).build(); + loop { - println!("Enter a string to edit. Use '-' to undo, '+' to redo, and '! i-j' for goto: "); + println!( + "Enter a string. Use '<' to undo, '>' to redo, '*' to save, and '! i-j' for goto: " + ); let mut string = String::new(); let n = stdin.read_line(&mut string)?; if n == 0 { @@ -36,21 +36,23 @@ fn main() -> io::Result<()> { .filter_map(|n| n.parse::().ok()); if let (Some(root), Some(index)) = (at.next(), at.next()) { - record.go_to(&mut target, At::new(root, index)); + history.go_to(&mut target, At::new(root, index)); } else { - println!("Expected input as '! i-j', e.g. '! 1-5'.\n"); + println!("Expected input as '! i-j', e.g. '! 1-5'."); } break; } else if c == '<' { - record.undo(&mut target); + history.undo(&mut target); } else if c == '>' { - record.redo(&mut target); + history.redo(&mut target); + } else if c == '*' { + history.set_saved(true); } else { - record.edit(&mut target, Add(c)); + history.edit(&mut target, Add(c)); } } - println!("{}\n", record.display().set_st_fmt(&custom_st_fmt)); + println!("{}\n", history.display().set_st_fmt(&custom_st_fmt)); println!("Target: {target}"); } } From c90caa5c12e542a0e54928b41136610d2c9c9898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Even=20Olsson=20Rogstadkj=C3=A6rnet?= Date: Fri, 22 Sep 2023 14:01:32 +0200 Subject: [PATCH 33/33] Add default values for '! i-j' in history example --- examples/history.rs | 18 ++++++++---------- src/history.rs | 3 +++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/examples/history.rs b/examples/history.rs index 6a8b8ea3..3439aee6 100644 --- a/examples/history.rs +++ b/examples/history.rs @@ -17,8 +17,8 @@ fn main() -> io::Result<()> { println!( "Enter a string. Use '<' to undo, '>' to redo, '*' to save, and '! i-j' for goto: " ); - let mut string = String::new(); - let n = stdin.read_line(&mut string)?; + let mut buf = String::new(); + let n = stdin.read_line(&mut buf)?; if n == 0 { return Ok(()); } @@ -26,20 +26,18 @@ fn main() -> io::Result<()> { // Clears the terminal. print!("{}c", 27 as char); - let mut chars = string.trim().chars(); + let mut chars = buf.trim().chars(); while let Some(c) = chars.next() { if c == '!' { - let rest = chars.collect::(); - let mut at = rest + let tail = chars.collect::(); + let mut at = tail .trim() .split('-') .filter_map(|n| n.parse::().ok()); - if let (Some(root), Some(index)) = (at.next(), at.next()) { - history.go_to(&mut target, At::new(root, index)); - } else { - println!("Expected input as '! i-j', e.g. '! 1-5'."); - } + let root = at.next().unwrap_or_default(); + let index = at.next().unwrap_or_default(); + history.go_to(&mut target, At::new(root, index)); break; } else if c == '<' { history.undo(&mut target); diff --git a/src/history.rs b/src/history.rs index c810be7c..fde53cb5 100644 --- a/src/history.rs +++ b/src/history.rs @@ -23,6 +23,9 @@ use serde::{Deserialize, Serialize}; /// Unlike [`Record`] which maintains a linear undo history, /// [`History`] maintains an undo tree containing every edit made to the target. /// +/// See [this](https://github.com/evenorog/undo/blob/master/examples/history.rs) +/// for an interactive example of the history tree. +/// /// # Examples /// ``` /// # fn main() {