Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change to Edit command #26

Merged
merged 33 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
16146e7
Use edit naming instead of action
evenorog Apr 8, 2023
d4489ca
Rename Push to Add
evenorog Apr 9, 2023
94abbe2
Improve documentation
evenorog Apr 9, 2023
7078c1f
Add more links in documentation
evenorog Apr 9, 2023
01efcd8
Make emit methods lazy
evenorog Apr 11, 2023
4856e90
Hide new method from builder
evenorog Apr 12, 2023
eaed6d9
Add updated_now method to entry
evenorog Apr 12, 2023
f78a4c1
Add more documentation
evenorog Apr 13, 2023
bc50969
Remove Try helper types
evenorog Apr 13, 2023
7f9c912
Rename emit on Slot to on_emit
evenorog Apr 14, 2023
2d79db0
Impl Slot for mpsc senders
evenorog Apr 15, 2023
6fa09fd
Rename current to index
evenorog Apr 16, 2023
1c14144
Replace Nop with ()
evenorog Apr 17, 2023
9b90228
Emit signal when index changes
evenorog Apr 18, 2023
9e2bbb2
Use matches! instead of map_or(false, ..)
evenorog Sep 15, 2023
43069c2
Remove Any, FromFn, and Join
evenorog Sep 17, 2023
5390d56
Fix entry timestamps not working when switching branches
evenorog Sep 17, 2023
27e9635
Rename position methods to head
evenorog Sep 17, 2023
12570c6
Add support for custom system time formatter
evenorog Sep 18, 2023
87afef1
Add missing feature gating
evenorog Sep 18, 2023
da217a4
Split edit and redo functionality from push functionality
evenorog Sep 18, 2023
15aa74e
Rename current field to index
evenorog Sep 18, 2023
5e0d87a
When merging two commands use the updated_at field for the deleted co…
evenorog Sep 19, 2023
c97821c
Rename Signal to Event
evenorog Sep 20, 2023
dc730d1
Make At public and change api to work with it
evenorog Sep 20, 2023
8504708
Improve documentation
evenorog Sep 20, 2023
e7254c5
Show just updated_at timestamp in display
evenorog Sep 20, 2023
2917818
Use if instead of match for labels method
evenorog Sep 20, 2023
e5ebc90
Change format of at from 1:1 to 1-1
evenorog Sep 20, 2023
0281392
Add index format method
evenorog Sep 20, 2023
d01c3a5
Make history example into an interactive client example
evenorog Sep 21, 2023
7749fa9
Add save functionality to history example
evenorog Sep 21, 2023
c90caa5
Add default values for '! i-j' in history example
evenorog Sep 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,40 @@
[![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.
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 apply, undo, and redo changes made to a target.
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

```rust
use undo::{Action, Record};
use undo::{Edit, Record};

struct Push(char);
struct Add(char);

impl Action for Push {
impl Edit for Add {
type Target = String;
type Output = ();

fn apply(&mut self, target: &mut String) {
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");
self.0 = target.pop().unwrap();
}
}

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, Add('a'));
record.edit(&mut target, Add('b'));
record.edit(&mut target, Add('c'));
assert_eq!(target, "abc");

record.undo(&mut target);
Expand Down
103 changes: 52 additions & 51 deletions examples/history.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,56 @@
use core::fmt::{self, Display, Formatter};
use undo::{Action, History};

struct Push(char);

impl Action for Push {
type Target = String;
type Output = ();

fn apply(&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 Push {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Push '{}'", self.0)
}
use chrono::{DateTime, Local};
use std::io;
use std::time::SystemTime;
use undo::{Add, At, History};

fn custom_st_fmt(_: SystemTime, at: SystemTime) -> String {
let dt = DateTime::<Local>::from(at);
dt.format("%H:%M:%S").to_string()
}

fn main() {
let mut history = History::new();
fn main() -> io::Result<()> {
let stdin = io::stdin();
let mut target = String::new();

history.apply(&mut target, Push('a'));
history.apply(&mut target, Push('b'));
history.apply(&mut target, Push('c'));
assert_eq!(target, "abc");

let abc_branch = history.branch();
let abc_current = history.current();

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'));
assert_eq!(target, "abdef");

let abdef_branch = history.branch();
let abdef_current = history.current();

history.go_to(&mut target, abc_branch, abc_current);
assert_eq!(target, "abc");

history.go_to(&mut target, abdef_branch, abdef_current);
assert_eq!(target, "abdef");

println!("{}", history.display());
let mut history = History::<_>::builder().limit(10).capacity(10).build();

loop {
println!(
"Enter a string. Use '<' to undo, '>' to redo, '*' to save, and '! i-j' for goto: "
);
let mut buf = String::new();
let n = stdin.read_line(&mut buf)?;
if n == 0 {
return Ok(());
}

// Clears the terminal.
print!("{}c", 27 as char);

let mut chars = buf.trim().chars();
while let Some(c) = chars.next() {
if c == '!' {
let tail = chars.collect::<String>();
let mut at = tail
.trim()
.split('-')
.filter_map(|n| n.parse::<usize>().ok());

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);
} else if c == '>' {
history.redo(&mut target);
} else if c == '*' {
history.set_saved(true);
} else {
history.edit(&mut target, Add(c));
}
}

println!("{}\n", history.display().set_st_fmt(&custom_st_fmt));
println!("Target: {target}");
}
}
45 changes: 12 additions & 33 deletions examples/record.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,22 @@
use core::fmt::{self, Display, Formatter};
use undo::{Action, Record};

struct Push(char);

impl Action for Push {
type Target = String;
type Output = ();

fn apply(&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 Push {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Push '{}'", self.0)
}
}
use undo::{Add, Record};

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'));
assert_eq!(target, "abc");
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, "a");

record.redo(&mut target);
record.redo(&mut target);
assert_eq!(target, "abc");
assert_eq!(target, "abcd");

println!("{}", record.display());
}
28 changes: 28 additions & 0 deletions src/add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use alloc::string::String;
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.
#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
pub struct Add(pub char);

impl crate::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().unwrap();
}
}

impl Display for Add {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Add '{}'", self.0)
}
}
77 changes: 0 additions & 77 deletions src/any.rs

This file was deleted.

17 changes: 0 additions & 17 deletions src/doctest.rs

This file was deleted.

Loading