mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Undo groups: a cancel drops the edits of every canceled group from the journal
Session::end_undo_group with cancel cancels the open undo group of every document. edit.undoGroup.end with cancel calls it, and so do a field scrub ended with Escape and a library style that fails to apply. The journal was then cut back to where the group of the last document in the document list began, not to where the first of the canceled groups began. When the group that began first belonged to an earlier document, the journal kept the commands of that group's edits, which the cancel had undone. After a scrub ended with Escape, which journals no end command, replaying the journal drew those edits again. Session::end_undo_group now cuts the journal back to the earliest start of the groups it cancels.
This commit is contained in:
@@ -1998,7 +1998,8 @@ impl Session {
|
||||
deselect_guides(&st.doc, &mut st.selection);
|
||||
}
|
||||
st.revision += 1;
|
||||
journal = Some(g.journal);
|
||||
// The journal goes back to where the first of the canceled groups began, whatever the order of the documents.
|
||||
journal = Some(journal.map_or(g.journal, |len: usize| len.min(g.journal)));
|
||||
}
|
||||
}
|
||||
if let Some(len) = journal {
|
||||
|
||||
@@ -343,6 +343,47 @@ fn a_failed_batch_gives_back_the_highlighted_rows_and_the_current_layer() {
|
||||
assert_eq!(documents(&r), documents(&s));
|
||||
}
|
||||
|
||||
/// Canceling undo groups open in two documents undoes the edits of both and cuts the journal back
|
||||
/// to where the first of them began, whichever document comes first in the list.
|
||||
#[test]
|
||||
fn canceling_undo_groups_in_two_documents_drops_the_edits_of_both_from_the_journal() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"created": null})).unwrap();
|
||||
s.execute("file.new", &json!({"created": null})).unwrap();
|
||||
let objects = |s: &Session| s.documents().iter().map(|d| d.doc.node_count()).collect::<Vec<_>>();
|
||||
let before = objects(&s);
|
||||
// The first document's group begins first, and the second document's group begins after it.
|
||||
let journal = s.journal.len();
|
||||
assert!(s.set_active(0));
|
||||
s.begin_undo_group();
|
||||
s.execute("shape.rectangle", &rect_step(10)["params"]).unwrap();
|
||||
assert!(s.set_active(1));
|
||||
s.begin_undo_group();
|
||||
s.execute("shape.rectangle", &rect_step(50)["params"]).unwrap();
|
||||
s.end_undo_group(true);
|
||||
assert_eq!(objects(&s), before, "both documents' edits are undone");
|
||||
assert_eq!(s.journal.len(), journal, "the journal holds neither group's edits");
|
||||
assert_eq!(documents(&replayed(&s)), documents(&s));
|
||||
|
||||
// The commands do the same: neither group's begin nor its edit stays in the journal.
|
||||
let journal = s.journal.len();
|
||||
for (id, p) in [
|
||||
("document.activate", json!({"index": 0})),
|
||||
("edit.undoGroup.begin", json!({})),
|
||||
("shape.rectangle", rect_step(10)["params"].clone()),
|
||||
("document.activate", json!({"index": 1})),
|
||||
("edit.undoGroup.begin", json!({})),
|
||||
("shape.rectangle", rect_step(50)["params"].clone()),
|
||||
("edit.undoGroup.end", json!({"cancel": true})),
|
||||
] {
|
||||
s.execute(id, &p).unwrap();
|
||||
}
|
||||
assert_eq!(objects(&s), before);
|
||||
let ids = s.journal.since(journal).iter().map(|(id, _)| id.as_str()).collect::<Vec<_>>();
|
||||
assert!(!ids.iter().any(|id| matches!(*id, "edit.undoGroup.begin" | "shape.rectangle")), "{ids:?}");
|
||||
assert_eq!(ids.last(), Some(&"edit.undoGroup.end"));
|
||||
}
|
||||
|
||||
/// The edits made in an undo group (a scrubbed numeric field, #400) are one undo step, journaled
|
||||
/// one by one; a cancelled group undoes them and leaves the journal as it was.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user