11use ropey:: Rope ;
2- use std:: fs;
32use std:: fs:: File ;
43use std:: io:: { BufReader , BufWriter } ;
5- use std:: path:: Path ;
6-
74use crate :: config:: { Config } ;
85use crate :: utils:: { self } ;
9- use log2:: * ;
10-
116use serde:: { Deserialize , Serialize } ;
7+ use crate :: history:: History ;
128
13- #[ derive( Debug , Serialize , Deserialize , Clone ) ]
9+ #[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
1410#[ serde( rename_all = "lowercase" ) ]
1511pub enum Operation {
1612 Insert ,
1713 Remove ,
18- Start ,
19- End ,
2014}
2115
22- #[ derive( Debug , Serialize , Deserialize , Clone ) ]
23- pub struct Change {
24- pub start : usize ,
25- pub operation : Operation ,
16+ #[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
17+ pub struct Edit {
18+ pub start : usize , // UTF-16 offset
2619 pub text : String ,
20+ pub operation : Operation ,
21+ }
22+
23+ #[ derive( Clone , Serialize , Deserialize ) ]
24+ pub struct Change {
25+ pub edits : Vec < Edit > ,
26+ pub timestamp : usize ,
27+ }
28+
29+ impl Change {
30+ pub fn new ( ) -> Self {
31+ Self {
32+ edits : Vec :: new ( ) ,
33+ timestamp : 0 ,
34+ }
35+ }
2736}
2837
2938pub struct Code {
@@ -32,8 +41,9 @@ pub struct Code {
3241 pub lang : String ,
3342 pub text : ropey:: Rope ,
3443 pub changed : bool ,
35- pub undo_history : Vec < Change > ,
36- pub redo_history : Vec < Change > ,
44+ pub applying_history : bool ,
45+ pub history : History ,
46+ pub change : Change ,
3747 pub self_updated : bool ,
3848}
3949
@@ -45,15 +55,22 @@ impl Code {
4555 abs_path : String :: new ( ) ,
4656 changed : false ,
4757 lang : String :: new ( ) ,
48- undo_history : Vec :: new ( ) ,
49- redo_history : Vec :: new ( ) ,
58+ applying_history : true ,
59+ history : History :: new ( 1000 ) ,
60+ change : Change :: new ( ) ,
5061 self_updated : false ,
5162 }
5263 }
5364
65+ pub fn get_content ( & self ) -> String {
66+ self . text . to_string ( )
67+ }
68+
5469 pub fn from_str ( text : & str ) -> Self {
5570 let mut code = Self :: new ( ) ;
56- code. insert_text ( text, 0 , 0 ) ;
71+ code. applying_history = false ;
72+ code. insert_text ( text, 0 ) ;
73+ code. applying_history = true ;
5774 code
5875 }
5976
@@ -79,20 +96,13 @@ impl Code {
7996 abs_path,
8097 changed : false ,
8198 lang,
82- undo_history : Vec :: new ( ) ,
83- redo_history : Vec :: new ( ) ,
99+ applying_history : true ,
100+ history : History :: new ( 1000 ) ,
101+ change : Change :: new ( ) ,
84102 self_updated : false ,
85103 } )
86104 }
87105
88-
89- pub fn set_text ( & mut self , text : & str ) {
90- self . text = Rope :: new ( ) ;
91- self . text . insert ( 0 , text) ;
92- self . changed = true ;
93- }
94-
95-
96106 pub fn save_file ( & mut self ) -> std:: io:: Result < ( ) > {
97107 if !self . changed {
98108 return Ok ( ( ) ) ;
@@ -109,17 +119,6 @@ impl Code {
109119 self . file_name = file_name;
110120 }
111121
112- pub fn ensure_file_exists ( & mut self ) -> std:: io:: Result < ( ) > {
113- if !Path :: new ( & self . file_name ) . exists ( ) {
114- fs:: create_dir_all ( Path :: new ( & self . file_name ) . parent ( ) . unwrap ( ) ) ?;
115- fs:: File :: create ( & self . file_name ) ?;
116-
117- self . abs_path = utils:: abs_file ( & self . file_name )
118- . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ?;
119- }
120- Ok ( ( ) )
121- }
122-
123122 pub fn position ( & self , offset : usize ) -> ( usize , usize ) {
124123 let line_idx = self . text . char_to_line ( offset) ;
125124 let line_char_index = self . text . line_to_char ( line_idx) ;
@@ -147,77 +146,52 @@ impl Code {
147146 self . changed = true ;
148147 }
149148
150- pub fn insert_text ( & mut self , text : & str , row : usize , column : usize ) {
151- let from = self . text . line_to_char ( row) + column;
152- self . insert ( text, from) ;
153-
154- self . undo_history . push ( Change {
155- start : from,
156- operation : Operation :: Insert ,
157- text : text. to_string ( ) ,
158- } ) ;
159-
160- self . redo_history . clear ( ) ;
161- }
162-
163- pub fn insert_text_at ( & mut self , text : & str , offset : usize ) {
149+ pub fn insert_text ( & mut self , text : & str , offset : usize ) {
164150 self . insert ( text, offset) ;
165151
166- self . undo_history . push ( Change {
167- start : offset,
168- operation : Operation :: Insert ,
169- text : text. to_string ( ) ,
170- } ) ;
171-
172- self . redo_history . clear ( ) ;
152+ if self . applying_history {
153+ self . change . edits . push ( Edit {
154+ start : offset, text : text. to_string ( ) , operation : Operation :: Insert ,
155+ } ) ;
156+ }
173157 }
174158
175159 fn remove ( & mut self , from : usize , to : usize ) {
176160 self . text . remove ( from..to) ;
177161 self . changed = true ;
178162 }
179163
180- pub fn remove_text ( & mut self , row : usize , col : usize , row1 : usize , col1 : usize ) {
181- let from = self . text . line_to_char ( row) + col;
182- let to = self . text . line_to_char ( row1) + col1;
164+ pub fn remove_text ( & mut self , from : usize , to : usize ) {
183165 let text = self . text . slice ( from..to) . to_string ( ) ;
184166
185167 self . remove ( from, to) ;
186168
187- self . undo_history . push ( Change {
188- start : from,
189- operation : Operation :: Remove ,
190- text : text. to_string ( ) ,
191- } ) ;
192-
193- self . redo_history . clear ( ) ;
169+ if self . applying_history {
170+ self . change . edits . push ( Edit {
171+ start : from, text : text. to_string ( ) , operation : Operation :: Remove ,
172+ } ) ;
173+ }
194174 }
195175
196- pub fn remove_text2 ( & mut self , from : usize , to : usize ) {
197- let text = self . text . slice ( from..to) . to_string ( ) ;
198-
199- self . remove ( from, to) ;
200-
201- self . undo_history . push ( Change {
202- start : from,
203- operation : Operation :: Remove ,
204- text : text. to_string ( ) ,
205- } ) ;
206-
207- self . redo_history . clear ( ) ;
176+ pub fn tx ( & mut self ) {
177+ self . change = Change :: new ( ) ;
178+ self . applying_history = true ;
208179 }
209180
210-
211- pub fn line_len ( & self , idx : usize ) -> usize {
212- let line = self . text . line ( idx) ;
213- let len = line. len_chars ( ) ;
214- if idx == self . text . len_lines ( ) - 1 {
215- len
216- } else {
217- len. saturating_sub ( 1 )
181+ pub fn commit ( & mut self ) {
182+ if !self . change . edits . is_empty ( ) {
183+ self . history . push ( self . change . clone ( ) ) ;
184+ self . change = Change :: new ( ) ;
218185 }
219186 }
220187
188+ pub fn undo_change ( & mut self ) -> Option < Change > {
189+ self . history . undo ( )
190+ }
191+
192+ pub fn redo_change ( & mut self ) -> Option < Change > {
193+ self . history . redo ( )
194+ }
221195}
222196
223197
@@ -240,19 +214,19 @@ mod code_undo_tests {
240214 #[ test]
241215 fn test_code_insert ( ) {
242216 let mut buffer = Code :: new ( ) ;
243- buffer. insert_text ( "hello" , 0 , 0 ) ;
244- buffer. insert_text ( " world" , 0 , 5 ) ;
217+ buffer. insert_text ( "hello" , 0 ) ;
218+ buffer. insert_text ( " world" , 5 ) ;
245219 assert_eq ! ( buffer. text. to_string( ) , "hello world" ) ;
246220 }
247221
248222 #[ test]
249223 fn test_code_remove ( ) {
250224 let mut buffer = Code :: new ( ) ;
251225
252- buffer. insert_text ( "hello world" , 0 , 0 ) ;
226+ buffer. insert_text ( "hello world" , 0 ) ;
253227 assert_eq ! ( buffer. text. to_string( ) , "hello world" ) ;
254228
255- buffer. remove_text ( 0 , 5 , 0 , 11 ) ;
229+ buffer. remove_text ( 5 , 11 ) ;
256230 assert_eq ! ( buffer. text. to_string( ) , "hello" ) ;
257231 }
258232
@@ -263,4 +237,60 @@ mod code_undo_tests {
263237 assert_eq ! ( buffer. char_to_position( 0 ) , ( 0 , 0 ) ) ;
264238 assert_eq ! ( buffer. char_to_position( text. len( ) ) , ( 0 , text. len( ) ) ) ;
265239 }
240+
241+ #[ test]
242+ fn test_undo ( ) {
243+ let mut code = Code :: new ( ) ;
244+
245+ code. tx ( ) ;
246+ code. insert_text ( "Hello " , 0 ) ;
247+ code. commit ( ) ;
248+
249+ code. tx ( ) ;
250+ code. insert_text ( "World" , 6 ) ;
251+ code. commit ( ) ;
252+
253+ assert_eq ! ( code. get_content( ) . to_string( ) , "Hello World" ) ;
254+ assert_eq ! ( code. history. index, 2 ) ;
255+
256+ let batch = code. undo_change ( ) . expect ( "undo should return batch" ) ;
257+ assert_eq ! ( code. history. index, 1 ) ;
258+ assert_eq ! ( batch. edits[ 0 ] , Edit {
259+ start: 6 , text: "World" . to_string( ) , operation: Operation :: Insert
260+ } ) ;
261+
262+ let batch = code. undo_change ( ) . expect ( "undo should return batch" ) ;
263+ assert_eq ! ( code. history. index, 0 ) ;
264+ assert_eq ! ( batch. edits[ 0 ] , Edit {
265+ start: 0 , text: "Hello " . to_string( ) , operation: Operation :: Insert
266+ } ) ;
267+
268+ assert ! ( code. undo_change( ) . is_none( ) ) ;
269+ }
270+
271+ #[ test]
272+ fn test_redo ( ) {
273+ let mut code = Code :: new ( ) ;
274+
275+ code. tx ( ) ;
276+ code. insert_text ( "Hello" , 0 ) ;
277+ code. commit ( ) ;
278+
279+ assert_eq ! ( code. history. index, 1 ) ;
280+
281+ let batch = code. undo_change ( ) . expect ( "undo should return batch" ) ;
282+ assert_eq ! ( code. history. index, 0 ) ;
283+ assert_eq ! ( batch. edits[ 0 ] , Edit {
284+ start: 0 , text: "Hello" . to_string( ) , operation: Operation :: Insert
285+ } ) ;
286+
287+ let batch = code. redo_change ( ) . expect ( "redo should return batch" ) ;
288+ assert_eq ! ( code. history. index, 1 ) ;
289+ assert_eq ! ( batch. edits[ 0 ] , Edit {
290+ start: 0 , text: "Hello" . to_string( ) , operation: Operation :: Insert
291+ } ) ;
292+
293+ let batch = code. redo_change ( ) ;
294+ assert ! ( batch. is_none( ) ) ;
295+ }
266296}
0 commit comments