@ -9,6 +9,7 @@ use helix_core::text_annotations::TextAnnotations;
use helix_core ::Range ;
use helix_core ::Range ;
use helix_vcs ::{ DiffHandle , DiffProviderRegistry } ;
use helix_vcs ::{ DiffHandle , DiffProviderRegistry } ;
use ::parking_lot ::Mutex ;
use serde ::de ::{ self , Deserialize , Deserializer } ;
use serde ::de ::{ self , Deserialize , Deserializer } ;
use serde ::Serialize ;
use serde ::Serialize ;
use std ::borrow ::Cow ;
use std ::borrow ::Cow ;
@ -18,7 +19,7 @@ use std::fmt::Display;
use std ::future ::Future ;
use std ::future ::Future ;
use std ::path ::{ Path , PathBuf } ;
use std ::path ::{ Path , PathBuf } ;
use std ::str ::FromStr ;
use std ::str ::FromStr ;
use std ::sync ::Arc ;
use std ::sync ::{ Arc , Weak } ;
use std ::time ::SystemTime ;
use std ::time ::SystemTime ;
use helix_core ::{
use helix_core ::{
@ -105,6 +106,13 @@ pub struct DocumentSavedEvent {
pub type DocumentSavedEventResult = Result < DocumentSavedEvent , anyhow ::Error > ;
pub type DocumentSavedEventResult = Result < DocumentSavedEvent , anyhow ::Error > ;
pub type DocumentSavedEventFuture = BoxFuture < ' static , DocumentSavedEventResult > ;
pub type DocumentSavedEventFuture = BoxFuture < ' static , DocumentSavedEventResult > ;
#[ derive(Debug) ]
pub struct SavePoint {
/// The view this savepoint is associated with
pub view : ViewId ,
revert : Mutex < Transaction > ,
}
pub struct Document {
pub struct Document {
pub ( crate ) id : DocumentId ,
pub ( crate ) id : DocumentId ,
text : Rope ,
text : Rope ,
@ -136,7 +144,7 @@ pub struct Document {
pub history : Cell < History > ,
pub history : Cell < History > ,
pub config : Arc < dyn DynAccess < Config > > ,
pub config : Arc < dyn DynAccess < Config > > ,
pub savepoint : Option < Transaction > ,
savepoints : Vec < Weak < SavePoint > > ,
// Last time we wrote to the file. This will carry the time the file was last opened if there
// Last time we wrote to the file. This will carry the time the file was last opened if there
// were no saves.
// were no saves.
@ -389,7 +397,7 @@ impl Document {
diagnostics : Vec ::new ( ) ,
diagnostics : Vec ::new ( ) ,
version : 0 ,
version : 0 ,
history : Cell ::new ( History ::default ( ) ) ,
history : Cell ::new ( History ::default ( ) ) ,
savepoint : None ,
savepoint s: Vec ::new ( ) ,
last_saved_time : SystemTime ::now ( ) ,
last_saved_time : SystemTime ::now ( ) ,
last_saved_revision : 0 ,
last_saved_revision : 0 ,
modified_since_accessed : false ,
modified_since_accessed : false ,
@ -846,11 +854,18 @@ impl Document {
}
}
// generate revert to savepoint
// generate revert to savepoint
if self . savepoint . is_some ( ) {
if ! self . savepoints . is_empty ( ) {
take_with ( & mut self . savepoint , | prev_revert | {
let revert = transaction . invert ( & old_doc ) ;
let revert = transaction . invert ( & old_doc ) ;
self . savepoints
Some ( revert . compose ( prev_revert . unwrap ( ) ) )
. retain_mut ( | save_point | match save_point . upgrade ( ) {
} ) ;
Some ( savepoint ) = > {
let mut revert_to_savepoint = savepoint . revert . lock ( ) ;
* revert_to_savepoint =
revert . clone ( ) . compose ( mem ::take ( & mut revert_to_savepoint ) ) ;
true
}
None = > false ,
} )
}
}
// update tree-sitter syntax tree
// update tree-sitter syntax tree
@ -940,15 +955,39 @@ impl Document {
self . undo_redo_impl ( view , false )
self . undo_redo_impl ( view , false )
}
}
pub fn savepoint ( & mut self ) {
/// Creates a reference counted snapshot (called savpepoint) of the document.
self . savepoint =
///
Some ( Transaction ::new ( self . text ( ) ) . with_selection ( self . selection ( view . id ) . clone ( ) ) ) ;
/// The snapshot will remain valid (and updated) idenfinitly as long as ereferences to it exist.
/// Restoring the snapshot will restore the selection and the contents of the document to
/// the state it had when this function was called.
pub fn savepoint ( & mut self , view : & View ) -> Arc < SavePoint > {
let revert = Transaction ::new ( self . text ( ) ) . with_selection ( self . selection ( view . id ) . clone ( ) ) ;
let savepoint = Arc ::new ( SavePoint {
view : view . id ,
revert : Mutex ::new ( revert ) ,
} ) ;
self . savepoints . push ( Arc ::downgrade ( & savepoint ) ) ;
savepoint
}
}
pub fn restore ( & mut self , view : & mut View ) {
pub fn restore ( & mut self , view : & mut View , savepoint : & SavePoint ) {
if let Some ( revert ) = self . savepoint . take ( ) {
assert_eq! (
self . apply ( & revert , view . id ) ;
savepoint . view , view . id ,
}
"Savepoint must not be used with a different view!"
) ;
// search and remove savepoint using a ptr comparison
// this avoids a deadlock as we need to lock the mutex
let savepoint_idx = self
. savepoints
. iter ( )
. position ( | savepoint_ref | savepoint_ref . as_ptr ( ) = = savepoint as * const _ )
. expect ( "Savepoint must belong to this document" ) ;
let savepoint_ref = self . savepoints . remove ( savepoint_idx ) ;
let mut revert = savepoint . revert . lock ( ) ;
self . apply ( & revert , view . id ) ;
* revert = Transaction ::new ( self . text ( ) ) . with_selection ( self . selection ( view . id ) . clone ( ) ) ;
self . savepoints . push ( savepoint_ref )
}
}
fn earlier_later_impl ( & mut self , view : & mut View , uk : UndoKind , earlier : bool ) -> bool {
fn earlier_later_impl ( & mut self , view : & mut View , uk : UndoKind , earlier : bool ) -> bool {