diff --git a/helix-core/src/history.rs b/helix-core/src/history.rs index b99e969f..1aac38d9 100644 --- a/helix-core/src/history.rs +++ b/helix-core/src/history.rs @@ -131,7 +131,7 @@ impl History { .map(|&n| self.revisions[n].inversion.clone()); let down_txns = down.iter().map(|&n| self.revisions[n].transaction.clone()); - up_txns.chain(down_txns).reduce(|acc, tx| tx.compose(acc)) + down_txns.chain(up_txns).reduce(|acc, tx| tx.compose(acc)) } /// Undo the last edit. diff --git a/helix-term/tests/test/splits.rs b/helix-term/tests/test/splits.rs index a34a24b7..96ced21a 100644 --- a/helix-term/tests/test/splits.rs +++ b/helix-term/tests/test/splits.rs @@ -161,5 +161,30 @@ async fn test_changes_in_splits_apply_to_all_views() -> anyhow::Result<()> { )) .await?; + // See . + // This sequence undoes part of the history and then adds new changes, creating a + // new branch in the history tree. `View::sync_changes` applies transactions down + // and up to the lowest common ancestor in the path between old and new revision + // numbers. If we apply these up/down transactions in the wrong order, this case + // panics. + // The key sequence: + // * 3[ Create three empty lines so we are at the end of the document. + // * v Create a split and save that point at the end of the document + // in the jumplist. + // * w Switch back to the first window. + // * uu Undo twice (not three times which would bring us back to the + // root of the tree). + // * 3[ Create three empty lines. Now the end of the document is past + // where it was on step 1. + // * q Close window 1, focusing window 2 and causing a sync. This step + // panics if we don't apply in the right order. + // * %d Clean up the buffer. + test(( + "#[|]#", + "3[vwuu3[q%d", + "#[|]#", + )) + .await?; + Ok(()) }