mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-23 01:46:18 +04:00
Apply inversions to Views on undo/redo
When using undo/redo, the history revision can be decremented. In that case we should apply the inversions since the given revision in History::changes_since. This prevents panics with jumplist operations when a session uses undo/redo to move the jumplist selection outside of the document.
This commit is contained in:
parent
fd00f3a70e
commit
4a103db622
@ -122,17 +122,21 @@ pub const fn at_root(&self) -> bool {
|
|||||||
/// Returns the changes since the given revision composed into a transaction.
|
/// Returns the changes since the given revision composed into a transaction.
|
||||||
/// Returns None if there are no changes between the current and given revisions.
|
/// Returns None if there are no changes between the current and given revisions.
|
||||||
pub fn changes_since(&self, revision: usize) -> Option<Transaction> {
|
pub fn changes_since(&self, revision: usize) -> Option<Transaction> {
|
||||||
if self.at_root() || self.current >= revision {
|
use std::cmp::Ordering::*;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The bounds are checked in the if condition above:
|
match revision.cmp(&self.current) {
|
||||||
// `revision` is known to be `< self.current`.
|
Equal => None,
|
||||||
self.revisions[revision..self.current]
|
Greater => self.revisions[self.current + 1..=revision]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|revision| &revision.transaction)
|
.map(|revision| &revision.inversion)
|
||||||
.cloned()
|
.cloned()
|
||||||
.reduce(|acc, transaction| acc.compose(transaction))
|
.reduce(|acc, inversion| acc.compose(inversion)),
|
||||||
|
Less => self.revisions[revision + 1..=self.current]
|
||||||
|
.iter()
|
||||||
|
.map(|revision| &revision.transaction)
|
||||||
|
.cloned()
|
||||||
|
.reduce(|acc, transaction| acc.compose(transaction)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Undo the last edit.
|
/// Undo the last edit.
|
||||||
|
@ -1424,7 +1424,7 @@ fn handle_event(
|
|||||||
// If the current document has been changed, apply the changes to all views.
|
// If the current document has been changed, apply the changes to all views.
|
||||||
// This ensures that selections in jumplists follow changes.
|
// This ensures that selections in jumplists follow changes.
|
||||||
if doc.id() == original_doc_id
|
if doc.id() == original_doc_id
|
||||||
&& doc.get_current_revision() > original_doc_revision
|
&& doc.get_current_revision() != original_doc_revision
|
||||||
{
|
{
|
||||||
if let Some(transaction) =
|
if let Some(transaction) =
|
||||||
doc.history.get_mut().changes_since(original_doc_revision)
|
doc.history.get_mut().changes_since(original_doc_revision)
|
||||||
|
@ -286,6 +286,15 @@ async fn test_multi_selection_shell_commands() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_undo_redo() -> anyhow::Result<()> {
|
async fn test_undo_redo() -> anyhow::Result<()> {
|
||||||
|
// A jumplist selection is created at a point which is undone.
|
||||||
|
//
|
||||||
|
// * 2[<space> Add two newlines at line start. We're now on line 3.
|
||||||
|
// * <C-s> Save the selection on line 3 in the jumplist.
|
||||||
|
// * u Undo the two newlines. We're now on line 1.
|
||||||
|
// * <C-o><C-i> Jump forward an back again in the jumplist. This would panic
|
||||||
|
// if the jumplist were not being updated correctly.
|
||||||
|
test(("#[|]#", "2[<space><C-s>u<C-o><C-i>", "#[|]#")).await?;
|
||||||
|
|
||||||
// A jumplist selection is passed through an edit and then an undo and then a redo.
|
// A jumplist selection is passed through an edit and then an undo and then a redo.
|
||||||
//
|
//
|
||||||
// * [<space> Add a newline at line start. We're now on line 2.
|
// * [<space> Add a newline at line start. We're now on line 2.
|
||||||
|
Loading…
Reference in New Issue
Block a user