@@ -5,6 +5,8 @@ use std::{
55 vec,
66} ;
77
8+ use ratatui:: symbols:: line;
9+
810use crate :: {
911 lines:: EditLine ,
1012 memstore:: { Chunk , ChunkIndex , LoadStore , Memstore } ,
@@ -37,12 +39,18 @@ impl LoadStore for FileLoadStore {
3739 }
3840}
3941
42+ #[ derive( Debug , Clone ) ]
43+ pub struct LineIndex {
44+ relative : i64 ,
45+ offset_version : u64 ,
46+ }
47+
4048pub struct VirtualFile {
4149 // configuration
4250 chunk_size : u64 ,
4351
44- /// index into chunk_lines
45- line_index : usize ,
52+ line_offset : i64 ,
53+ offset_version : u64 ,
4654
4755 // indices of chunks loaded in chunk_lines
4856 loaded_chunks : Range < ChunkIndex > ,
@@ -58,7 +66,8 @@ impl VirtualFile {
5866 let chunk_zero = ChunkIndex :: from_offset ( 0 , chunk_size) ;
5967 let mut res = VirtualFile {
6068 chunk_size,
61- line_index : 0 ,
69+ offset_version : 0 ,
70+ line_offset : 0 ,
6271 loaded_chunks : Range {
6372 start : chunk_zero. clone ( ) ,
6473 end : chunk_zero. clone ( ) ,
@@ -99,8 +108,9 @@ impl VirtualFile {
99108 } else if new_index. next ( ) == self . loaded_chunks . start && !self . loaded_chunks . is_empty ( ) {
100109 self . loaded_chunks . start = new_index;
101110 // append existing lines to new lines
102- // line_index is relative to the range start, which was pushed up by the new chunk
103- self . line_index += new_chunk_lines. len ( ) ;
111+ // line indexes are relative to the range start, which was pushed up by the new chunk
112+ let len: i64 = new_chunk_lines. len ( ) . try_into ( ) . unwrap ( ) ;
113+ self . line_offset = self . line_offset + len;
104114 std:: mem:: swap ( & mut self . chunk_lines , & mut new_chunk_lines) ;
105115 self . chunk_lines
106116 . last_mut ( )
@@ -114,64 +124,97 @@ impl VirtualFile {
114124 end : new_index. next ( ) ,
115125 } ;
116126 self . chunk_lines = new_chunk_lines;
117- self . line_index = 0 ;
127+ self . line_offset = 0 ;
128+ self . offset_version += 1 ;
118129 } ;
119130 }
120131
121- pub fn prev_line ( & mut self ) -> u16 {
122- if self . line_index == 0 && self . loaded_chunks . start . index > 0 {
132+ pub fn prev_line ( & mut self , line_index : & LineIndex ) -> Option < LineIndex > {
133+ let index = self . to_abs_index ( & line_index) ;
134+ if index. is_none ( ) {
135+ return None ;
136+ }
137+ let index = index. unwrap ( ) ;
138+ if index == 0 && self . loaded_chunks . start . index > 0 {
123139 // seek to previous chunk
124140 self . seek ( self . chunk_size * ( self . loaded_chunks . start . index - 1 ) ) ;
141+ assert ! ( line_index. offset_version == self . offset_version) ;
125142 }
126- // after possible seek, line_index may still be zero if there was nothing to load
127- if self . line_index > 0 {
128- self . line_index -= 1 ;
129- return 1 ;
143+ // after possible seek, index may still be zero if there was nothing to load
144+ if index > 0 {
145+ return Some ( LineIndex {
146+ relative : line_index. relative - 1 ,
147+ offset_version : line_index. offset_version ,
148+ } ) ;
130149 }
131- return 0 ;
150+ return Some ( line_index . clone ( ) ) ;
132151 }
133152
134- pub fn next_line ( & mut self ) -> u16 {
135- if self . line_index + 2 >= self . chunk_lines . len ( ) {
153+ pub fn next_line ( & mut self , line_index : & LineIndex ) -> Option < LineIndex > {
154+ let index = self . to_abs_index ( & line_index) ;
155+ if index. is_none ( ) {
156+ return None ;
157+ }
158+ let index = index. unwrap ( ) ;
159+ if index + 2 >= self . chunk_lines . len ( ) {
136160 // fetch more lines, after increasing index it will be the last line which may be incomplete
137161 self . seek ( self . loaded_chunks . end . to_offset ( ) ) ;
162+ assert ! ( line_index. offset_version == self . offset_version) ;
138163 }
139- if self . line_index + 1 < self . chunk_lines . len ( ) {
140- self . line_index += 1 ;
141- return 1 ;
164+ if index + 1 < self . chunk_lines . len ( ) {
165+ return Some ( LineIndex {
166+ relative : line_index. relative + 1 ,
167+ offset_version : line_index. offset_version ,
168+ } ) ;
142169 }
143- return 0 ;
170+ return Some ( line_index . clone ( ) ) ;
144171 }
145172
146- pub fn remove ( & mut self ) -> EditLine {
147- if self . line_index + 2 >= self . chunk_lines . len ( ) {
173+ pub fn remove ( & mut self , line_index : & LineIndex ) -> Option < EditLine > {
174+ let index = self . to_abs_index ( & line_index) ;
175+ if index. is_none ( ) {
176+ return None ;
177+ }
178+ let index = index. unwrap ( ) ;
179+ if index + 2 >= self . chunk_lines . len ( ) {
148180 // fetch more lines, after removal it will be the last line which may be incomplete
149181 self . seek ( self . loaded_chunks . end . to_offset ( ) ) ;
182+ assert ! ( line_index. offset_version == self . offset_version) ;
150183 }
151- let removed_line = self . chunk_lines . remove ( self . line_index ) ;
152- if self . line_index > 0 {
153- self . line_index -= 1 ;
154- } else if self . chunk_lines . len ( ) == 0 {
184+ let removed_line = self . chunk_lines . remove ( index) ;
185+ if self . chunk_lines . len ( ) == 0 {
155186 // that was the only line left, add one back to avoid empty
156187 self . chunk_lines . push ( EditLine :: empty ( ) ) ;
157188 }
158- return removed_line;
189+ return Some ( removed_line) ;
159190 }
160191
161- pub fn get_index ( & self ) -> usize {
162- self . line_index
163- }
164-
165- pub fn insert_after ( & mut self , new_line : EditLine ) {
166- self . chunk_lines . insert ( self . line_index + 1 , new_line) ;
192+ pub fn insert_after ( & mut self , line_index : & LineIndex , new_line : EditLine ) -> Option < ( ) > {
193+ match self . to_abs_index ( & line_index) {
194+ None => return None ,
195+ Some ( index) => {
196+ self . chunk_lines . insert ( index + 1 , new_line) ;
197+ return Some ( ( ) ) ;
198+ }
199+ }
167200 }
168201
169- pub fn get ( & self ) -> & EditLine {
170- self . chunk_lines . get ( self . line_index ) . unwrap ( )
202+ pub fn get ( & self , line_index : & LineIndex ) -> Option < & EditLine > {
203+ match self . to_abs_index ( & line_index) {
204+ None => return None ,
205+ Some ( index) => {
206+ return self . chunk_lines . get ( index) ;
207+ }
208+ }
171209 }
172210
173- pub fn get_mut ( & mut self ) -> & mut EditLine {
174- self . chunk_lines . get_mut ( self . line_index ) . unwrap ( )
211+ pub fn get_mut ( & mut self , line_index : & LineIndex ) -> Option < & mut EditLine > {
212+ match self . to_abs_index ( & line_index) {
213+ None => return None ,
214+ Some ( index) => {
215+ return self . chunk_lines . get_mut ( index) ;
216+ }
217+ }
175218 }
176219
177220 fn parse_chunk ( data : & Vec < u8 > ) -> Vec < EditLine > {
@@ -181,34 +224,38 @@ impl VirtualFile {
181224 . collect ( )
182225 }
183226
227+ pub fn get_index ( & self ) -> LineIndex {
228+ LineIndex {
229+ relative : 0 ,
230+ offset_version : self . offset_version ,
231+ }
232+ }
233+
184234 pub fn iter_at (
185235 & mut self ,
186- offset_from_line_index : i64 ,
236+ line_index : & LineIndex ,
187237 count : usize ,
188238 ) -> impl Iterator < Item = & EditLine > {
189- // TODO: This is inefficient and also clobbers the current line_index
190- let mut clobber: i32 = 0 ;
191- if offset_from_line_index < 0 {
192- // need to iterate lines backwards
193- for _ in offset_from_line_index..0 {
194- clobber -= self . prev_line ( ) as i32 ;
239+ match self . to_abs_index ( & line_index) {
240+ None => return [ ] . iter ( ) ,
241+ Some ( index) => {
242+ // materialize 'count' lines
243+ let mut line_index = line_index. clone ( ) ;
244+ for _ in 0 ..count {
245+ line_index = self . next_line ( & line_index) . unwrap ( ) ;
246+ }
247+ self . chunk_lines [ index..( index + count) ] . iter ( )
195248 }
196- } else {
197- // need to iterate lines forwards
198- for _ in 0 ..offset_from_line_index {
199- clobber += self . next_line ( ) as i32 ;
200- }
201- }
202- // line_index is now at the start of the range
203- let start_index = self . line_index ;
204- // materialize 'count' lines
205- for _ in 0 ..count {
206- clobber += self . next_line ( ) as i32 ;
207249 }
208- // ... and now go back to where line_index was before
209- self . line_index = ( self . line_index as i32 - clobber) . try_into ( ) . unwrap ( ) ;
250+ }
210251
211- self . chunk_lines . iter ( ) . skip ( start_index)
252+ fn to_abs_index ( & self , line_index : & LineIndex ) -> Option < usize > {
253+ if self . offset_version != line_index. offset_version {
254+ return None ;
255+ }
256+ let index = ( line_index. relative + self . line_offset ) . try_into ( ) . unwrap ( ) ;
257+ assert ! ( index < self . chunk_lines. len( ) ) ;
258+ Some ( index)
212259 }
213260}
214261
0 commit comments