@@ -30,7 +30,9 @@ impl TextChunk {
3030/// by the edited text and the number of chunks rather than the document tail.
3131#[ derive( Clone , Debug , Default , PartialEq , Eq ) ]
3232pub ( crate ) struct ChunkedText {
33- chunks : Vec < TextChunk > ,
33+ chunks : Arc < Vec < TextChunk > > ,
34+ chunk_ends : Arc < Vec < usize > > ,
35+ line_break_ends : Arc < Vec < usize > > ,
3436 len : usize ,
3537 line_break_count : usize ,
3638}
@@ -43,8 +45,11 @@ impl ChunkedText {
4345 pub ( crate ) fn from_str ( text : & str ) -> Self {
4446 let chunks = split_text_chunks ( text) ;
4547 let line_break_count = chunks. iter ( ) . map ( |chunk| chunk. line_breaks . len ( ) ) . sum ( ) ;
48+ let ( chunk_ends, line_break_ends) = chunk_indexes ( & chunks) ;
4649 Self {
47- chunks,
50+ chunks : Arc :: new ( chunks) ,
51+ chunk_ends : Arc :: new ( chunk_ends) ,
52+ line_break_ends : Arc :: new ( line_break_ends) ,
4853 len : text. len ( ) ,
4954 line_break_count,
5055 }
@@ -85,7 +90,7 @@ impl ChunkedText {
8590
8691 pub ( crate ) fn to_flat_string ( & self ) -> String {
8792 let mut text = String :: with_capacity ( self . len ) ;
88- for chunk in & self . chunks {
93+ for chunk in self . chunks . iter ( ) {
8994 text. push_str ( & chunk. text ) ;
9095 }
9196 text
@@ -125,13 +130,14 @@ impl ChunkedText {
125130 return Some ( String :: new ( ) ) ;
126131 }
127132 let mut result = String :: with_capacity ( end. saturating_sub ( start) ) ;
128- let mut chunk_start = 0usize ;
129- for chunk in & self . chunks {
133+ let ( start_idx, _) = self . locate ( start) ;
134+ let mut chunk_start = start_idx
135+ . checked_sub ( 1 )
136+ . and_then ( |idx| self . chunk_ends . get ( idx) )
137+ . copied ( )
138+ . unwrap_or ( 0 ) ;
139+ for chunk in self . chunks . iter ( ) . skip ( start_idx) {
130140 let chunk_end = chunk_start. saturating_add ( chunk. len ( ) ) ;
131- if chunk_end <= start {
132- chunk_start = chunk_end;
133- continue ;
134- }
135141 if chunk_start >= end {
136142 break ;
137143 }
@@ -190,8 +196,11 @@ impl ChunkedText {
190196 . iter ( )
191197 . map ( |chunk| chunk. line_breaks . len ( ) )
192198 . sum :: < usize > ( ) ;
193- self . chunks
199+ Arc :: make_mut ( & mut self . chunks )
194200 . splice ( drain_start..drain_end. max ( drain_start) , replacement_chunks) ;
201+ let ( chunk_ends, line_break_ends) = chunk_indexes ( & self . chunks ) ;
202+ self . chunk_ends = Arc :: new ( chunk_ends) ;
203+ self . line_break_ends = Arc :: new ( line_break_ends) ;
195204 self . len = self
196205 . len
197206 . saturating_sub ( end. saturating_sub ( start) )
@@ -213,19 +222,18 @@ impl ChunkedText {
213222
214223 pub ( crate ) fn line_index_for_position ( & self , pos : usize ) -> usize {
215224 let pos = pos. min ( self . len ) ;
216- let mut absolute = 0usize ;
217- let mut line_index = 0usize ;
218- for chunk in & self . chunks {
219- let chunk_end = absolute. saturating_add ( chunk. len ( ) ) ;
220- if pos < chunk_end {
221- let relative = pos. saturating_sub ( absolute) ;
222- line_index += chunk. line_breaks . partition_point ( |value| * value < relative) ;
223- return line_index;
224- }
225- line_index = line_index. saturating_add ( chunk. line_breaks . len ( ) ) ;
226- absolute = chunk_end;
227- }
228- line_index
225+ let ( chunk_idx, relative) = self . locate ( pos) ;
226+ let preceding_breaks = chunk_idx
227+ . checked_sub ( 1 )
228+ . and_then ( |idx| self . line_break_ends . get ( idx) )
229+ . copied ( )
230+ . unwrap_or ( 0 ) ;
231+ self . chunks
232+ . get ( chunk_idx)
233+ . map_or ( preceding_breaks, |chunk| {
234+ preceding_breaks
235+ . saturating_add ( chunk. line_breaks . partition_point ( |value| * value < relative) )
236+ } )
229237 }
230238
231239 pub ( crate ) fn line_start ( & self , pos : usize ) -> usize {
@@ -252,35 +260,34 @@ impl ChunkedText {
252260 . unwrap_or ( self . len )
253261 }
254262
255- fn nth_line_break ( & self , mut ordinal : usize ) -> Option < usize > {
256- let mut absolute = 0usize ;
257- for chunk in & self . chunks {
258- if ordinal < chunk. line_breaks . len ( ) {
259- return chunk
260- . line_breaks
261- . get ( ordinal)
262- . map ( |value| absolute. saturating_add ( * value) ) ;
263- }
264- ordinal = ordinal. saturating_sub ( chunk. line_breaks . len ( ) ) ;
265- absolute = absolute. saturating_add ( chunk. len ( ) ) ;
266- }
267- None
263+ fn nth_line_break ( & self , ordinal : usize ) -> Option < usize > {
264+ let chunk_idx = self . line_break_ends . partition_point ( |end| * end <= ordinal) ;
265+ let preceding_breaks = chunk_idx
266+ . checked_sub ( 1 )
267+ . and_then ( |idx| self . line_break_ends . get ( idx) )
268+ . copied ( )
269+ . unwrap_or ( 0 ) ;
270+ let chunk_start = chunk_idx
271+ . checked_sub ( 1 )
272+ . and_then ( |idx| self . chunk_ends . get ( idx) )
273+ . copied ( )
274+ . unwrap_or ( 0 ) ;
275+ self . chunks
276+ . get ( chunk_idx) ?
277+ . line_breaks
278+ . get ( ordinal. saturating_sub ( preceding_breaks) )
279+ . map ( |value| chunk_start. saturating_add ( * value) )
268280 }
269281
270282 fn locate ( & self , pos : usize ) -> ( usize , usize ) {
271283 let pos = pos. min ( self . len ) ;
272- let mut absolute = 0usize ;
273- for ( idx, chunk) in self . chunks . iter ( ) . enumerate ( ) {
274- let chunk_end = absolute. saturating_add ( chunk. len ( ) ) ;
275- if pos < chunk_end {
276- return ( idx, pos. saturating_sub ( absolute) ) ;
277- }
278- if pos == chunk_end {
279- return ( idx. saturating_add ( 1 ) , 0 ) ;
280- }
281- absolute = chunk_end;
282- }
283- ( self . chunks . len ( ) , 0 )
284+ let chunk_idx = self . chunk_ends . partition_point ( |end| * end <= pos) ;
285+ let chunk_start = chunk_idx
286+ . checked_sub ( 1 )
287+ . and_then ( |idx| self . chunk_ends . get ( idx) )
288+ . copied ( )
289+ . unwrap_or ( 0 ) ;
290+ ( chunk_idx, pos. saturating_sub ( chunk_start) )
284291 }
285292}
286293
@@ -495,6 +502,20 @@ fn split_text_chunks(text: &str) -> Vec<TextChunk> {
495502 chunks
496503}
497504
505+ fn chunk_indexes ( chunks : & [ TextChunk ] ) -> ( Vec < usize > , Vec < usize > ) {
506+ let mut byte_end = 0usize ;
507+ let mut line_break_end = 0usize ;
508+ let mut chunk_ends = Vec :: with_capacity ( chunks. len ( ) ) ;
509+ let mut line_break_ends = Vec :: with_capacity ( chunks. len ( ) ) ;
510+ for chunk in chunks {
511+ byte_end = byte_end. saturating_add ( chunk. len ( ) ) ;
512+ line_break_end = line_break_end. saturating_add ( chunk. line_breaks . len ( ) ) ;
513+ chunk_ends. push ( byte_end) ;
514+ line_break_ends. push ( line_break_end) ;
515+ }
516+ ( chunk_ends, line_break_ends)
517+ }
518+
498519fn line_break_positions ( bytes : & [ u8 ] ) -> Vec < usize > {
499520 let mut positions = Vec :: new ( ) ;
500521 let mut idx = 0usize ;
@@ -538,6 +559,24 @@ mod tests {
538559 ) ;
539560 }
540561
562+ #[ test]
563+ fn chunked_text_snapshot_clone_shares_text_and_lookup_indexes ( ) {
564+ let text = ChunkedText :: from_str ( & "SELECT 1;\n " . repeat ( 100_000 ) ) ;
565+ let mut snapshot = text. clone ( ) ;
566+
567+ assert ! ( Arc :: ptr_eq( & text. chunks, & snapshot. chunks) ) ;
568+ assert ! ( Arc :: ptr_eq( & text. chunk_ends, & snapshot. chunk_ends) ) ;
569+ assert ! ( Arc :: ptr_eq(
570+ & text. line_break_ends,
571+ & snapshot. line_break_ends
572+ ) ) ;
573+
574+ let edit_at = snapshot. len ( ) / 2 ;
575+ assert ! ( snapshot. replace_range( edit_at, edit_at, "-- pasted\n " ) ) ;
576+ assert ! ( !Arc :: ptr_eq( & text. chunks, & snapshot. chunks) ) ;
577+ assert_eq ! ( text. len( ) + "-- pasted\n " . len( ) , snapshot. len( ) ) ;
578+ }
579+
541580 #[ test]
542581 fn chunked_text_line_queries_match_flat_text ( ) {
543582 let source = "a\r \n b\n c\r d" . repeat ( 10_000 ) ;
0 commit comments