@@ -9,6 +9,7 @@ use helix_core::text_annotations::TextAnnotations;
9
9
use helix_core:: Range ;
10
10
use helix_vcs:: { DiffHandle , DiffProviderRegistry } ;
11
11
12
+ use :: parking_lot:: Mutex ;
12
13
use serde:: de:: { self , Deserialize , Deserializer } ;
13
14
use serde:: Serialize ;
14
15
use std:: borrow:: Cow ;
@@ -18,7 +19,7 @@ use std::fmt::Display;
18
19
use std:: future:: Future ;
19
20
use std:: path:: { Path , PathBuf } ;
20
21
use std:: str:: FromStr ;
21
- use std:: sync:: Arc ;
22
+ use std:: sync:: { Arc , Weak } ;
22
23
use std:: time:: SystemTime ;
23
24
24
25
use helix_core:: {
@@ -105,6 +106,13 @@ pub struct DocumentSavedEvent {
105
106
pub type DocumentSavedEventResult = Result < DocumentSavedEvent , anyhow:: Error > ;
106
107
pub type DocumentSavedEventFuture = BoxFuture < ' static , DocumentSavedEventResult > ;
107
108
109
+ #[ derive( Debug ) ]
110
+ pub struct SavePoint {
111
+ /// The view this savepoint is associated here
112
+ pub view : ViewId ,
113
+ revert : Mutex < Transaction > ,
114
+ }
115
+
108
116
pub struct Document {
109
117
pub ( crate ) id : DocumentId ,
110
118
text : Rope ,
@@ -136,7 +144,7 @@ pub struct Document {
136
144
pub history : Cell < History > ,
137
145
pub config : Arc < dyn DynAccess < Config > > ,
138
146
139
- pub savepoint : Option < Transaction > ,
147
+ savepoints : Vec < Weak < SavePoint > > ,
140
148
141
149
// Last time we wrote to the file. This will carry the time the file was last opened if there
142
150
// were no saves.
@@ -389,7 +397,7 @@ impl Document {
389
397
diagnostics : Vec :: new ( ) ,
390
398
version : 0 ,
391
399
history : Cell :: new ( History :: default ( ) ) ,
392
- savepoint : None ,
400
+ savepoints : Vec :: new ( ) ,
393
401
last_saved_time : SystemTime :: now ( ) ,
394
402
last_saved_revision : 0 ,
395
403
modified_since_accessed : false ,
@@ -846,11 +854,18 @@ impl Document {
846
854
}
847
855
848
856
// generate revert to savepoint
849
- if self . savepoint . is_some ( ) {
850
- take_with ( & mut self . savepoint , |prev_revert| {
851
- let revert = transaction. invert ( & old_doc) ;
852
- Some ( revert. compose ( prev_revert. unwrap ( ) ) )
853
- } ) ;
857
+ if !self . savepoints . is_empty ( ) {
858
+ let revert = transaction. invert ( & old_doc) ;
859
+ self . savepoints
860
+ . retain_mut ( |save_point| match save_point. upgrade ( ) {
861
+ Some ( savepoint) => {
862
+ let mut revert_to_savepoint = savepoint. revert . lock ( ) ;
863
+ * revert_to_savepoint =
864
+ revert. clone ( ) . compose ( mem:: take ( & mut revert_to_savepoint) ) ;
865
+ true
866
+ }
867
+ None => false ,
868
+ } )
854
869
}
855
870
856
871
// update tree-sitter syntax tree
@@ -940,15 +955,46 @@ impl Document {
940
955
self . undo_redo_impl ( view, false )
941
956
}
942
957
943
- pub fn savepoint ( & mut self ) {
944
- self . savepoint =
945
- Some ( Transaction :: new ( self . text ( ) ) . with_selection ( self . selection ( view. id ) . clone ( ) ) ) ;
958
+ /// Creates a savepoint. Until `doc.restore` is called,
959
+ ///
960
+ /// `Document::savepoint` is set to `None`, or `savepoint` is called again
961
+ /// a transaction to revert the document back to this savepoint is automatically
962
+ /// maintained. `Document::restore` can be used to reset the document contents
963
+ /// and view selection back to the state when this function was called.
964
+ ///
965
+ /// # Returns
966
+ ///
967
+ /// An `id` that can uniquely identify this savepoint. A document
968
+ /// can only track a single savepoint at a time, to check if your savepoint still
969
+ /// applies simply
970
+ pub fn savepoint ( & mut self , view : & View ) -> Arc < SavePoint > {
971
+ let revert = Transaction :: new ( self . text ( ) ) . with_selection ( self . selection ( view. id ) . clone ( ) ) ;
972
+ let savepoint = Arc :: new ( SavePoint {
973
+ view : view. id ,
974
+ revert : Mutex :: new ( revert) ,
975
+ } ) ;
976
+ self . savepoints . push ( Arc :: downgrade ( & savepoint) ) ;
977
+ savepoint
946
978
}
947
979
948
- pub fn restore ( & mut self , view : & mut View ) {
949
- if let Some ( revert) = self . savepoint . take ( ) {
950
- self . apply ( & revert, view. id ) ;
951
- }
980
+ pub fn restore ( & mut self , view : & mut View , savepoint : & SavePoint ) {
981
+ assert_eq ! (
982
+ savepoint. view, view. id,
983
+ "Savepoint must not be used with a different view!"
984
+ ) ;
985
+ // search and remove savepoint using a ptr comparison
986
+ // this avoids a deadlock as we need to lock the mutex
987
+ let savepoint_idx = self
988
+ . savepoints
989
+ . iter ( )
990
+ . position ( |savepoint_ref| savepoint_ref. as_ptr ( ) == savepoint as * const _ )
991
+ . expect ( "Savepoint must belong to this document" ) ;
992
+
993
+ let savepoint_ref = self . savepoints . remove ( savepoint_idx) ;
994
+ let mut revert = savepoint. revert . lock ( ) ;
995
+ self . apply ( & revert, view. id ) ;
996
+ * revert = Transaction :: new ( self . text ( ) ) . with_selection ( self . selection ( view. id ) . clone ( ) ) ;
997
+ self . savepoints . push ( savepoint_ref)
952
998
}
953
999
954
1000
fn earlier_later_impl ( & mut self , view : & mut View , uk : UndoKind , earlier : bool ) -> bool {
0 commit comments