Skip to content

Commit 7e25c95

Browse files
authored
Keep edits queued behind AddTransaction in their undo step (#4886)
* Make saving an unchanged document write the same bytes, reopened in between or not * Keep edits queued behind AddTransaction in their undo step
1 parent 129f66a commit 7e25c95

3 files changed

Lines changed: 43 additions & 1 deletion

File tree

‎editor/src/messages/portfolio/document/document_message_handler.rs‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
460460
self.layer_range_selection_reference = None;
461461
self.chain_node_range_selection_reference = None;
462462
}
463-
DocumentMessage::DocumentHistoryBackward => self.undo_with_history(document_id, viewport, preferences.validate_storage_round_trip, responses),
463+
DocumentMessage::DocumentHistoryBackward => {
464+
self.stage_edits_outside_transaction(&resource_storage.resources_mut(), preferences.validate_storage_round_trip);
465+
self.undo_with_history(document_id, viewport, preferences.validate_storage_round_trip, responses)
466+
}
464467
DocumentMessage::DocumentHistoryForward => self.redo_with_history(document_id, viewport, preferences.validate_storage_round_trip, responses),
465468
DocumentMessage::DocumentStructureChanged => {
466469
if layers_panel_open {
@@ -1655,6 +1658,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
16551658
}
16561659
// Note: A transaction should never be started in a scope that mutates the network interface, since it will only be run after that scope ends.
16571660
DocumentMessage::StartTransaction => {
1661+
self.stage_edits_outside_transaction(&resource_storage.resources_mut(), preferences.validate_storage_round_trip);
16581662
self.retire_storage_interaction();
16591663

16601664
self.network_interface.start_transaction();
@@ -2426,6 +2430,14 @@ impl DocumentMessageHandler {
24262430
}
24272431
}
24282432

2433+
/// Stages edits made after a transaction finished, such as those queued behind an `AddTransaction`, whose commit runs
2434+
/// first. They belong to the undo step that just ended, so they must reach storage before it closes.
2435+
fn stage_edits_outside_transaction(&mut self, byte_store: &dyn graph_craft::application_io::resource::ResourceStorage, validate: bool) {
2436+
if self.network_interface.transaction_status() == TransactionStatus::Finished && self.network_interface.has_deltas() {
2437+
self.commit_storage_snapshot(byte_store, validate);
2438+
}
2439+
}
2440+
24292441
/// The per-peer view settings that ride along with either kind of staging.
24302442
fn storage_view_settings(&self) -> std::collections::BTreeMap<String, document_graph_storage::Value> {
24312443
use crate::messages::portfolio::document::utility_types::network_interface::storage_metadata::DocumentSettings;

‎editor/src/messages/portfolio/document/utility_types/network_interface/characterization_tests.rs‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,6 +3235,31 @@ async fn redoing_a_nudge_undone_while_it_glides_leaves_nothing_gliding() {
32353235
assert!(editor.active_document().network_interface.glide_offsets(&[]).is_empty());
32363236
}
32373237

3238+
#[tokio::test]
3239+
async fn undoing_a_nudge_keeps_the_node_it_moved() {
3240+
let mut editor = EditorTestUtils::create();
3241+
let node = gliding_nudged_node(&mut editor).await;
3242+
3243+
editor.handle_message(DocumentMessage::Undo).await;
3244+
assert_eq!(editor.active_document().network_interface.position(&node, &[]), Some(IVec2::ZERO));
3245+
3246+
editor.handle_message(DocumentMessage::Undo).await;
3247+
assert!(editor.active_document().network_interface.document_network().nodes.is_empty());
3248+
}
3249+
3250+
#[tokio::test]
3251+
async fn undoing_right_after_creating_a_node_removes_only_that_node() {
3252+
let mut editor = EditorTestUtils::create();
3253+
editor.new_document().await;
3254+
let kept = editor.create_node_by_name_at(rectangle_definition(), 0, 0).await;
3255+
editor.create_node_by_name_at(rectangle_definition(), 5, 0).await;
3256+
3257+
editor.handle_message(DocumentMessage::Undo).await;
3258+
3259+
let nodes = &editor.active_document().network_interface.document_network().nodes;
3260+
assert_eq!(nodes.keys().copied().collect::<Vec<_>>(), vec![kept]);
3261+
}
3262+
32383263
#[tokio::test]
32393264
async fn entering_a_node_while_its_network_glides_settles_it() {
32403265
let mut editor = EditorTestUtils::create();

‎editor/src/messages/portfolio/document/utility_types/network_interface/store/mod.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,11 @@ impl NodeNetworkInterface {
605605
self.network.network().located_network(location).is_some() && self.network_metadata.nested_metadata(location).is_some()
606606
}
607607

608+
/// Whether writes since the last drain are waiting to be staged.
609+
pub(crate) fn has_deltas(&self) -> bool {
610+
!self.deltas.is_empty()
611+
}
612+
608613
/// Takes what the writes since the last drain changed, in write order, leaving the buffer empty
609614
/// after reading it for the nodes an open transaction has written to.
610615
pub(crate) fn take_deltas(&mut self) -> Vec<EditorDelta> {

0 commit comments

Comments
 (0)