pub enum Edit {
Property {
path: Vec<usize>,
name: String,
value: Option<Literal>,
},
Insert {
parent: Vec<usize>,
index: usize,
text: String,
},
Argument {
path: Vec<usize>,
value: Literal,
},
Move {
from: Vec<usize>,
to: Vec<usize>,
index: usize,
},
Remove {
path: Vec<usize>,
},
Many(Vec<Edit>),
Replace {
path: Vec<usize>,
text: String,
},
}Expand description
One reversible change to a form.
Applied with Form::apply, which hands back the edit that undoes it. See
there for why an inverse is always knowable.
Variants§
Property
Set a property, or take it away with None.
Fields
Insert
Put a node, written as form-file text, among a parent’s children.
The text is a whole node with its own formatting, which is what makes this the inverse of a removal and what a paste from the clipboard is.
Fields
Argument
Set a node’s positional argument — the "Hello" in label "Hello".
Only ever replaces one. A node written without an argument does not grow one this way: an argument has to come before every property, and there is no shape of edit that puts something at the front of a line without rewriting the line. Setting the matching property is what an editor does instead, and means the same thing to the engine.
Move
Take a node out and put it back under another parent.
Reordering among siblings and reparenting are the same edit: both take a node out and put it back somewhere, and doing it as one keeps it to one step on an undo stack.
A node that changes depth is re-indented — every line of it, so the children come along — because a file whose nesting and whose indentation disagree is a file somebody has to fix by hand. Moving it back re-indents it back, so an undo is still byte-for-byte.
Fields
Remove
Take a node, and everything under it, out.
Many(Vec<Edit>)
Several edits as one.
Applied in order and undone in reverse, which is what makes a drag that moved and resized a single step on an undo stack rather than four. If any of them fails the ones already applied are put back, so a compound edit either happens or does not.
Replace
Swap a node for another, written as form-file text.
The exact inverse of anything that cannot be undone by putting a value back — taking a property away is the case: a property re-added by name lands at the end of the line rather than where it was, so the values would come back right and the line would not. Restoring the node’s own text restores its order too.
Implementations§
Source§impl Edit
impl Edit
Sourcepub fn number(path: &[usize], name: &str, value: Option<i64>) -> Self
pub fn number(path: &[usize], name: &str, value: Option<i64>) -> Self
Sets or clears a whole-number property.
The common one by a long way: every rectangle a drag writes is four of these.
let mut form = Form::parse(r#"form "F" version=1 width=99 height=99 { label "Hi" x=0 y=0 w=9 h=9 }"#)?;
form.apply(Edit::number(&[0], "x", Some(24)))?;
assert_eq!(form.property(&[0], "x").as_deref(), Some("24"));
// `None` takes it out of the file, which is what a default is.
form.apply(Edit::number(&[0], "x", None))?;
assert_eq!(form.property(&[0], "x"), None);Sourcepub fn property(path: &[usize], name: &str, value: Option<Literal>) -> Self
pub fn property(path: &[usize], name: &str, value: Option<Literal>) -> Self
The path is child indices from the form node down, and the empty path
is the form itself — its size, its kind, its theme.
let mut form = Form::parse(r#"form "F" version=1 width=320 height=240 { label "Hi" x=0 y=0 w=9 h=9 }"#)?;
form.apply(Edit::property(&[], "width", Some(Literal::Int(640))))?;
assert_eq!(form.size(), denise::Size::new(640, 240));Sets or clears a property.
Sourcepub fn argument(path: &[usize], text: impl Into<String>) -> Self
pub fn argument(path: &[usize], text: impl Into<String>) -> Self
A label "Heading" keeps its text there rather than in a text=
property, and so does the form’s own title.
let mut form = Form::parse(r#"form "F" version=1 width=99 height=99 { label "Hi" x=0 y=0 w=9 h=9 }"#)?;
form.apply(Edit::argument(&[0], "Hello"))?;
assert_eq!(form.argument(&[0]).as_deref(), Some("Hello"));
// The form's title is its argument too.
form.apply(Edit::argument(&[], "Greeting"))?;
assert_eq!(form.title(), "Greeting");Sets a node’s positional argument to a string.
Sourcepub fn move_to(from: &[usize], to: &[usize], index: usize) -> Self
pub fn move_to(from: &[usize], to: &[usize], index: usize) -> Self
index is the position after the node has been taken out, which is
the part that is easy to get wrong: removing [1] moves [3] to [2].
let mut form = Form::parse(
"form \"F\" version=1 width=99 height=99 {\n label \"a\" x=0 y=0 w=9 h=9\n panel name=box x=0 y=9 w=9 h=9\n}\n",
)?;
// The label into the panel, which grows the braces it did not have.
form.apply(Edit::move_to(&[0], &[1], 0))?;
assert!(form.text().contains("panel name=box x=0 y=9 w=9 h=9 {"), "{}", form.text());Moves a node under another parent, or to another place among its siblings.
Sourcepub fn remove(path: &[usize]) -> Self
pub fn remove(path: &[usize]) -> Self
Its children go with it, and so does the comment written above it — the node’s leading trivia is part of the node, which is what makes undoing a removal put the comment back.
let source = "form \"F\" version=1 width=99 height=99 {\n // why\n label \"a\" x=0 y=0 w=9 h=9\n}\n";
let mut form = Form::parse(source)?;
let undo = form.apply(Edit::remove(&[0]))?;
assert!(!form.text().contains("why"));
form.apply(undo)?;
assert_eq!(form.text(), source, "the comment came back with the node");Removes a node.