1use std::io;
2use std::path::PathBuf;
3use std::sync::Arc;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct Position {
8 pub line: usize, pub column: usize, }
11
12#[derive(Debug, Clone)]
14pub enum BufferData {
15 Loaded {
17 data: Vec<u8>,
18 line_starts: Option<Vec<usize>>, },
20 Unloaded {
22 file_path: PathBuf,
23 file_offset: usize, bytes: usize, },
26}
27
28#[derive(Debug, Clone)]
31pub struct StringBuffer {
32 pub id: usize,
34 pub data: BufferData,
36 pub stored_file_offset: Option<usize>,
40}
41
42impl StringBuffer {
43 pub fn new(id: usize, data: Vec<u8>) -> Self {
46 let line_starts = Self::compute_line_starts(&data);
47 StringBuffer {
48 id,
49 data: BufferData::Loaded {
50 data,
51 line_starts: Some(line_starts),
52 },
53 stored_file_offset: None,
54 }
55 }
56
57 pub fn new_loaded(id: usize, data: Vec<u8>, compute_lines: bool) -> Self {
59 let line_starts = if compute_lines {
60 Some(Self::compute_line_starts(&data))
61 } else {
62 None
63 };
64 StringBuffer {
65 id,
66 data: BufferData::Loaded { data, line_starts },
67 stored_file_offset: None,
68 }
69 }
70
71 pub fn new_unloaded(id: usize, file_path: PathBuf, file_offset: usize, bytes: usize) -> Self {
73 StringBuffer {
74 id,
75 data: BufferData::Unloaded {
76 file_path,
77 file_offset,
78 bytes,
79 },
80 stored_file_offset: None,
81 }
82 }
83
84 pub fn is_loaded(&self) -> bool {
86 matches!(self.data, BufferData::Loaded { .. })
87 }
88
89 pub fn unloaded_bytes(&self) -> Option<usize> {
91 match &self.data {
92 BufferData::Unloaded { bytes, .. } => Some(*bytes),
93 BufferData::Loaded { .. } => None,
94 }
95 }
96
97 pub(crate) fn get_data(&self) -> Option<&[u8]> {
102 match &self.data {
103 BufferData::Loaded { data, .. } => Some(data),
104 BufferData::Unloaded { .. } => None,
105 }
106 }
107
108 pub fn nth_line_start_in_piece(
113 &self,
114 piece_start: usize,
115 piece_end: usize,
116 line_in_piece: usize,
117 ) -> Option<usize> {
118 if line_in_piece == 0 {
119 return Some(piece_start);
120 }
121 let line_starts = self.get_line_starts()?;
122 let start_idx = line_starts.partition_point(|&ls| ls <= piece_start);
123 let target_idx = start_idx + line_in_piece - 1;
124 let &ls = line_starts.get(target_idx)?;
125 if ls < piece_end {
126 Some(ls)
127 } else {
128 None
129 }
130 }
131
132 pub fn get_line_starts(&self) -> Option<&[usize]> {
134 match &self.data {
135 BufferData::Loaded { line_starts, .. } => line_starts.as_deref(),
136 BufferData::Unloaded { .. } => None,
137 }
138 }
139
140 pub fn load(&mut self, fs: &dyn crate::model::filesystem::FileSystem) -> io::Result<()> {
143 match &self.data {
144 BufferData::Loaded { .. } => Ok(()), BufferData::Unloaded {
146 file_path,
147 file_offset,
148 bytes,
149 } => {
150 let buffer = fs.read_range(file_path, *file_offset as u64, *bytes)?;
152
153 assert_eq!(
158 buffer.len(),
159 *bytes,
160 "FileSystem::read_range violated contract: expected {} bytes at offset {}, got {} (path: {})",
161 bytes,
162 file_offset,
163 buffer.len(),
164 file_path.display()
165 );
166
167 let line_starts = Self::compute_line_starts(&buffer);
171 self.data = BufferData::Loaded {
172 data: buffer,
173 line_starts: Some(line_starts),
174 };
175
176 Ok(())
177 }
178 }
179 }
180
181 pub fn create_chunk_buffer(
193 &self,
194 new_id: usize,
195 chunk_offset: usize,
196 chunk_bytes: usize,
197 ) -> Option<StringBuffer> {
198 match &self.data {
199 BufferData::Unloaded {
200 file_path,
201 file_offset,
202 bytes,
203 } => {
204 if chunk_offset + chunk_bytes > *bytes {
206 return None;
207 }
208
209 let absolute_file_offset = file_offset + chunk_offset;
210 let mut buf = StringBuffer::new_unloaded(
211 new_id,
212 file_path.clone(),
213 absolute_file_offset,
214 chunk_bytes,
215 );
216 buf.stored_file_offset = Some(absolute_file_offset);
217 Some(buf)
218 }
219 BufferData::Loaded { .. } => None, }
221 }
222
223 fn compute_line_starts(data: &[u8]) -> Vec<usize> {
225 let mut line_starts = vec![0];
226 for (i, &byte) in data.iter().enumerate() {
227 if byte == b'\n' {
228 line_starts.push(i + 1);
229 }
230 }
231 line_starts
232 }
233
234 pub fn line_feed_count(&self) -> Option<usize> {
237 match &self.data {
238 BufferData::Loaded { line_starts, .. } => line_starts
239 .as_ref()
240 .map(|starts| starts.len().saturating_sub(1)),
241 BufferData::Unloaded { .. } => None,
242 }
243 }
244
245 pub fn append(&mut self, data_to_append: &[u8]) -> usize {
249 match &mut self.data {
250 BufferData::Loaded { data, line_starts } => {
251 let start_offset = data.len();
252 data.extend_from_slice(data_to_append);
253
254 if let Some(ref mut line_starts) = line_starts {
256 for (i, &byte) in data_to_append.iter().enumerate() {
257 if byte == b'\n' {
258 line_starts.push(start_offset + i + 1);
259 }
260 }
261 }
262
263 start_offset
264 }
265 BufferData::Unloaded { .. } => {
266 0
268 }
269 }
270 }
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275pub enum BufferLocation {
276 Stored(usize), Added(usize), }
281
282impl BufferLocation {
283 pub fn buffer_id(&self) -> usize {
285 match self {
286 Self::Stored(id) | Self::Added(id) => *id,
287 }
288 }
289}
290
291#[derive(Debug, Clone)]
293pub enum PieceTreeNode {
294 Internal {
296 left_bytes: usize, lf_left: Option<usize>, left: Arc<PieceTreeNode>,
299 right: Arc<PieceTreeNode>,
300 },
301 Leaf {
303 location: BufferLocation, offset: usize, bytes: usize, line_feed_cnt: Option<usize>, },
308}
309
310#[derive(Debug, Clone)]
312pub struct PieceInfo {
313 pub location: BufferLocation, pub offset: usize, pub bytes: usize, pub offset_in_piece: Option<usize>, pub line_feeds: Option<usize>, }
319
320#[derive(Debug, Clone)]
322struct OffsetFindResult {
323 info: PieceInfo,
324 bytes_before: usize, }
326
327#[derive(Debug, Clone)]
329pub struct Cursor {
330 pub byte_offset: usize, pub line: usize, pub col: usize, }
334
335#[derive(Debug, Clone, Copy)]
337pub struct LeafData {
338 pub location: BufferLocation,
339 pub offset: usize,
340 pub bytes: usize,
341 pub line_feed_cnt: Option<usize>,
342}
343
344impl LeafData {
345 pub fn new(
346 location: BufferLocation,
347 offset: usize,
348 bytes: usize,
349 line_feed_cnt: Option<usize>,
350 ) -> Self {
351 LeafData {
352 location,
353 offset,
354 bytes,
355 line_feed_cnt,
356 }
357 }
358}
359
360#[derive(Debug, Clone, Copy)]
362pub struct TreeStats {
363 pub total_bytes: usize,
364 pub depth: usize,
365 pub leaf_count: usize,
366 pub line_feed_count: Option<usize>,
367}
368
369impl PieceTreeNode {
378 fn find_by_offset(&self, offset: usize) -> Option<OffsetFindResult> {
380 match self {
381 Self::Internal {
382 left_bytes,
383 left,
384 right,
385 ..
386 } => {
387 if offset < *left_bytes {
388 left.find_by_offset(offset)
389 } else {
390 right.find_by_offset(offset - left_bytes).map(|mut result| {
392 result.bytes_before += left_bytes;
394 result
395 })
396 }
397 }
398 Self::Leaf {
399 location,
400 offset: piece_offset,
401 bytes,
402 line_feed_cnt,
403 } => {
404 if offset < *bytes {
405 Some(OffsetFindResult {
406 info: PieceInfo {
407 location: *location,
408 offset: *piece_offset,
409 bytes: *bytes,
410 offset_in_piece: Some(offset),
411 line_feeds: *line_feed_cnt,
412 },
413 bytes_before: 0,
414 })
415 } else {
416 None
417 }
418 }
419 }
420 }
421
422 fn total_bytes(&self) -> usize {
424 match self {
425 Self::Internal {
426 left_bytes, right, ..
427 } => left_bytes + right.total_bytes(),
428 Self::Leaf { bytes, .. } => *bytes,
429 }
430 }
431
432 fn total_line_feeds(&self) -> Option<usize> {
435 match self {
436 Self::Internal { lf_left, right, .. } => match (lf_left, right.total_line_feeds()) {
437 (Some(left), Some(right)) => Some(left + right),
438 _ => None,
439 },
440 Self::Leaf { line_feed_cnt, .. } => *line_feed_cnt,
441 }
442 }
443
444 fn depth(&self) -> usize {
446 match self {
447 Self::Internal { left, right, .. } => 1 + left.depth().max(right.depth()),
448 Self::Leaf { .. } => 1,
449 }
450 }
451
452 fn count_leaves(&self) -> usize {
454 match self {
455 Self::Internal { left, right, .. } => left.count_leaves() + right.count_leaves(),
456 Self::Leaf { .. } => 1,
457 }
458 }
459
460 fn update_leaf_lf_by_index(
466 self: &Arc<Self>,
467 leaf_index: usize,
468 lf_count: usize,
469 ) -> (Arc<Self>, bool) {
470 match self.as_ref() {
471 Self::Leaf {
472 location,
473 offset,
474 bytes,
475 ..
476 } => {
477 if leaf_index == 0 {
478 (
479 Arc::new(Self::Leaf {
480 location: *location,
481 offset: *offset,
482 bytes: *bytes,
483 line_feed_cnt: Some(lf_count),
484 }),
485 true,
486 )
487 } else {
488 (Arc::clone(self), false)
489 }
490 }
491 Self::Internal {
492 left_bytes,
493 lf_left,
494 left,
495 right,
496 } => {
497 let left_leaf_count = left.count_leaves();
498 if leaf_index < left_leaf_count {
499 let (new_left, found) = left.update_leaf_lf_by_index(leaf_index, lf_count);
500 if found {
501 let new_lf_left = new_left.total_line_feeds();
502 (
503 Arc::new(Self::Internal {
504 left_bytes: *left_bytes,
505 lf_left: new_lf_left,
506 left: new_left,
507 right: Arc::clone(right),
508 }),
509 true,
510 )
511 } else {
512 (Arc::clone(self), false)
513 }
514 } else {
515 let (new_right, found) =
516 right.update_leaf_lf_by_index(leaf_index - left_leaf_count, lf_count);
517 if found {
518 (
519 Arc::new(Self::Internal {
520 left_bytes: *left_bytes,
521 lf_left: *lf_left,
522 left: Arc::clone(left),
523 right: new_right,
524 }),
525 true,
526 )
527 } else {
528 (Arc::clone(self), false)
529 }
530 }
531 }
532 }
533 }
534
535 pub(crate) fn collect_leaves(&self, leaves: &mut Vec<LeafData>) {
537 match self {
538 Self::Internal { left, right, .. } => {
539 left.collect_leaves(leaves);
540 right.collect_leaves(leaves);
541 }
542 Self::Leaf {
543 location,
544 offset,
545 bytes,
546 line_feed_cnt,
547 } => {
548 leaves.push(LeafData::new(*location, *offset, *bytes, *line_feed_cnt));
549 }
550 }
551 }
552
553 fn count_lines_in_byte_range(
557 &self,
558 current_offset: usize,
559 start: usize,
560 end: usize,
561 ) -> Option<usize> {
562 match self {
563 Self::Internal {
564 left_bytes,
565 left,
566 right,
567 ..
568 } => {
569 let left_end = current_offset + left_bytes;
570
571 if end <= current_offset || start >= current_offset + self.total_bytes() {
572 Some(0) } else if start <= current_offset && end >= current_offset + self.total_bytes() {
574 self.total_line_feeds()
576 } else if end <= left_end {
577 left.count_lines_in_byte_range(current_offset, start, end)
579 } else if start >= left_end {
580 right.count_lines_in_byte_range(left_end, start, end)
582 } else {
583 let left_count = left.count_lines_in_byte_range(current_offset, start, end)?;
585 let right_count = right.count_lines_in_byte_range(left_end, start, end)?;
586 Some(left_count + right_count)
587 }
588 }
589 Self::Leaf {
590 line_feed_cnt,
591 bytes,
592 ..
593 } => {
594 let node_end = current_offset + bytes;
595
596 if end <= current_offset || start >= node_end {
597 Some(0) } else if start <= current_offset && end >= node_end {
599 *line_feed_cnt
601 } else {
602 *line_feed_cnt
605 }
606 }
607 }
608 }
609
610 fn find_byte_offset_for_line(
614 &self,
615 current_offset: usize,
616 lines_before: usize,
617 target_line: usize,
618 column: usize,
619 buffers: &[StringBuffer],
620 ) -> Option<usize> {
621 match self {
622 Self::Internal {
623 left_bytes,
624 lf_left,
625 left,
626 right,
627 } => {
628 let lf_left = lf_left.as_ref()?;
630 let lines_after_left = lines_before + lf_left;
631
632 let go_left = if column == 0 {
635 target_line <= lines_after_left
636 } else {
637 target_line < lines_after_left
638 };
639
640 if go_left {
641 let result = left.find_byte_offset_for_line(
643 current_offset,
644 lines_before,
645 target_line,
646 column,
647 buffers,
648 );
649 result.or_else(|| {
651 right.find_byte_offset_for_line(
652 current_offset + left_bytes,
653 lines_after_left,
654 target_line,
655 column,
656 buffers,
657 )
658 })
659 } else {
660 right.find_byte_offset_for_line(
662 current_offset + left_bytes,
663 lines_after_left,
664 target_line,
665 column,
666 buffers,
667 )
668 }
669 }
670 Self::Leaf {
671 location,
672 offset,
673 bytes,
674 line_feed_cnt,
675 } => {
676 let line_feed_cnt = line_feed_cnt.as_ref()?;
678 let lines_in_piece = lines_before + line_feed_cnt;
679
680 if column == 0 && target_line == lines_in_piece && target_line > lines_before {
684 let buffer = buffers.get(location.buffer_id())?;
685 let data = buffer.get_data()?;
686 let last_byte_offset = offset + bytes - 1;
687 let last_byte = data.get(last_byte_offset)?;
688
689 if *last_byte == b'\n' {
690 return None;
692 }
693 }
695
696 if target_line < lines_before || target_line > lines_in_piece {
697 return None;
699 }
700
701 let buffer_id = location.buffer_id();
703 let buffer = buffers.get(buffer_id)?;
704
705 let line_in_piece = target_line - lines_before;
707 let line_start_in_buffer =
708 buffer.nth_line_start_in_piece(*offset, offset + bytes, line_in_piece)?;
709
710 let target_offset_in_buffer = line_start_in_buffer + column;
712
713 let offset_in_piece = target_offset_in_buffer.saturating_sub(*offset);
715 Some(current_offset + offset_in_piece.min(*bytes))
716 }
717 }
718 }
719
720 fn find_piece_for_line(
723 &self,
724 current_offset: usize,
725 lines_before: usize,
726 target_line: usize,
727 ) -> Option<(usize, usize, usize, usize, usize)> {
728 match self {
729 Self::Internal {
730 left_bytes,
731 lf_left,
732 left,
733 right,
734 } => {
735 let lf_left = lf_left.as_ref()?;
736 let lines_after_left = lines_before + lf_left;
737
738 if target_line <= lines_after_left {
739 let result =
740 left.find_piece_for_line(current_offset, lines_before, target_line);
741 result.or_else(|| {
743 right.find_piece_for_line(
744 current_offset + left_bytes,
745 lines_after_left,
746 target_line,
747 )
748 })
749 } else {
750 right.find_piece_for_line(
751 current_offset + left_bytes,
752 lines_after_left,
753 target_line,
754 )
755 }
756 }
757 Self::Leaf {
758 location,
759 offset,
760 bytes,
761 line_feed_cnt,
762 } => {
763 let line_feed_cnt = line_feed_cnt.as_ref()?;
764 let lines_in_piece = lines_before + line_feed_cnt;
765
766 if target_line >= lines_before && target_line <= lines_in_piece {
767 Some((
768 current_offset,
769 location.buffer_id(),
770 *offset,
771 *bytes,
772 lines_before,
773 ))
774 } else {
775 None
776 }
777 }
778 }
779 }
780}
781
782#[derive(Debug, Clone)]
784pub struct PieceTree {
785 root: Arc<PieceTreeNode>,
786 total_bytes: usize,
787}
788
789impl PieceTree {
790 pub fn new(
792 location: BufferLocation,
793 offset: usize,
794 bytes: usize,
795 line_feed_cnt: Option<usize>,
796 ) -> Self {
797 PieceTree {
798 root: Arc::new(PieceTreeNode::Leaf {
799 location,
800 offset,
801 bytes,
802 line_feed_cnt,
803 }),
804 total_bytes: bytes,
805 }
806 }
807
808 pub fn empty() -> Self {
810 PieceTree {
811 root: Arc::new(PieceTreeNode::Leaf {
812 location: BufferLocation::Stored(0),
813 offset: 0,
814 bytes: 0,
815 line_feed_cnt: Some(0), }),
817 total_bytes: 0,
818 }
819 }
820
821 pub fn from_leaves(leaves: &[LeafData]) -> Self {
823 let total_bytes: usize = leaves.iter().map(|l| l.bytes).sum();
824 PieceTree {
825 root: Self::build_balanced(leaves),
826 total_bytes,
827 }
828 }
829
830 fn build_balanced(leaves: &[LeafData]) -> Arc<PieceTreeNode> {
832 if leaves.is_empty() {
833 return Arc::new(PieceTreeNode::Leaf {
834 location: BufferLocation::Stored(0),
835 offset: 0,
836 bytes: 0,
837 line_feed_cnt: Some(0), });
839 }
840
841 if leaves.len() == 1 {
842 let leaf = leaves[0];
843 return Arc::new(PieceTreeNode::Leaf {
844 location: leaf.location,
845 offset: leaf.offset,
846 bytes: leaf.bytes,
847 line_feed_cnt: leaf.line_feed_cnt,
848 });
849 }
850
851 let mid = leaves.len() / 2;
853 let left = Self::build_balanced(&leaves[..mid]);
854 let right = Self::build_balanced(&leaves[mid..]);
855
856 let left_bytes = left.total_bytes();
857 let lf_left = left.total_line_feeds();
858
859 Arc::new(PieceTreeNode::Internal {
860 left_bytes,
861 lf_left,
862 left,
863 right,
864 })
865 }
866
867 fn path_copy_insert(
870 node: &Arc<PieceTreeNode>,
871 current_offset: usize,
872 insert_offset: usize,
873 insert_leaf: LeafData,
874 buffers: &[StringBuffer],
875 ) -> Arc<PieceTreeNode> {
876 match node.as_ref() {
877 PieceTreeNode::Internal {
878 left_bytes,
879 lf_left,
880 left,
881 right,
882 ..
883 } => {
884 let left_end = current_offset + left_bytes;
885 if insert_offset < left_end {
886 let new_left = Self::path_copy_insert(
888 left,
889 current_offset,
890 insert_offset,
891 insert_leaf,
892 buffers,
893 );
894 let new_left_bytes = new_left.total_bytes();
895 let new_lf_left = new_left.total_line_feeds();
896 Arc::new(PieceTreeNode::Internal {
897 left_bytes: new_left_bytes,
898 lf_left: new_lf_left,
899 left: new_left,
900 right: Arc::clone(right),
901 })
902 } else if insert_offset > left_end {
903 let new_right = Self::path_copy_insert(
905 right,
906 left_end,
907 insert_offset,
908 insert_leaf,
909 buffers,
910 );
911 Arc::new(PieceTreeNode::Internal {
912 left_bytes: *left_bytes,
913 lf_left: *lf_left,
914 left: Arc::clone(left),
915 right: new_right,
916 })
917 } else {
918 let new_right = Self::path_copy_insert(
923 right,
924 left_end,
925 insert_offset,
926 insert_leaf,
927 buffers,
928 );
929 Arc::new(PieceTreeNode::Internal {
930 left_bytes: *left_bytes,
931 lf_left: *lf_left,
932 left: Arc::clone(left),
933 right: new_right,
934 })
935 }
936 }
937 PieceTreeNode::Leaf {
938 location,
939 offset,
940 bytes,
941 line_feed_cnt,
942 } => {
943 let piece_end = current_offset + bytes;
944 let offset_in_piece = insert_offset.saturating_sub(current_offset);
945
946 if offset_in_piece > 0 && insert_offset < piece_end {
947 let before_bytes = offset_in_piece;
949 let after_bytes = bytes - offset_in_piece;
950
951 let lf_before =
952 Self::compute_line_feeds_static(buffers, *location, *offset, before_bytes);
953 let lf_after = Self::compute_line_feeds_static(
954 buffers,
955 *location,
956 offset + before_bytes,
957 after_bytes,
958 );
959
960 let leaf_before = LeafData::new(*location, *offset, before_bytes, lf_before);
961 let leaf_after =
962 LeafData::new(*location, offset + before_bytes, after_bytes, lf_after);
963
964 Self::build_balanced(&[leaf_before, insert_leaf, leaf_after])
966 } else if insert_offset <= current_offset {
967 if *bytes == 0 {
969 Arc::new(PieceTreeNode::Leaf {
971 location: insert_leaf.location,
972 offset: insert_leaf.offset,
973 bytes: insert_leaf.bytes,
974 line_feed_cnt: insert_leaf.line_feed_cnt,
975 })
976 } else {
977 let existing = LeafData::new(*location, *offset, *bytes, *line_feed_cnt);
978 Self::build_balanced(&[insert_leaf, existing])
979 }
980 } else {
981 let existing = LeafData::new(*location, *offset, *bytes, *line_feed_cnt);
983 Self::build_balanced(&[existing, insert_leaf])
984 }
985 }
986 }
987 }
988
989 fn path_copy_delete(
993 node: &Arc<PieceTreeNode>,
994 current_offset: usize,
995 delete_start: usize,
996 delete_end: usize,
997 buffers: &[StringBuffer],
998 ) -> Arc<PieceTreeNode> {
999 match node.as_ref() {
1000 PieceTreeNode::Internal {
1001 left_bytes,
1002 lf_left,
1003 left,
1004 right,
1005 ..
1006 } => {
1007 let left_end = current_offset + left_bytes;
1008
1009 if delete_end <= left_end {
1011 let new_left = Self::path_copy_delete(
1012 left,
1013 current_offset,
1014 delete_start,
1015 delete_end,
1016 buffers,
1017 );
1018 let new_left_bytes = new_left.total_bytes();
1019 if new_left_bytes == 0 {
1020 return Arc::clone(right);
1022 }
1023 let new_lf_left = new_left.total_line_feeds();
1024 return Arc::new(PieceTreeNode::Internal {
1025 left_bytes: new_left_bytes,
1026 lf_left: new_lf_left,
1027 left: new_left,
1028 right: Arc::clone(right),
1029 });
1030 }
1031
1032 if delete_start >= left_end {
1034 let new_right =
1035 Self::path_copy_delete(right, left_end, delete_start, delete_end, buffers);
1036 let new_right_bytes = new_right.total_bytes();
1037 if new_right_bytes == 0 {
1038 return Arc::clone(left);
1040 }
1041 return Arc::new(PieceTreeNode::Internal {
1042 left_bytes: *left_bytes,
1043 lf_left: *lf_left,
1044 left: Arc::clone(left),
1045 right: new_right,
1046 });
1047 }
1048
1049 let new_left = Self::path_copy_delete(
1051 left,
1052 current_offset,
1053 delete_start,
1054 left_end.min(delete_end),
1055 buffers,
1056 );
1057 let new_right = Self::path_copy_delete(
1058 right,
1059 left_end,
1060 left_end.max(delete_start),
1061 delete_end,
1062 buffers,
1063 );
1064
1065 let new_left_bytes = new_left.total_bytes();
1066 let new_right_bytes = new_right.total_bytes();
1067
1068 if new_left_bytes == 0 && new_right_bytes == 0 {
1069 return Arc::new(PieceTreeNode::Leaf {
1071 location: BufferLocation::Stored(0),
1072 offset: 0,
1073 bytes: 0,
1074 line_feed_cnt: Some(0),
1075 });
1076 }
1077 if new_left_bytes == 0 {
1078 return new_right;
1079 }
1080 if new_right_bytes == 0 {
1081 return new_left;
1082 }
1083
1084 let new_lf_left = new_left.total_line_feeds();
1085 Arc::new(PieceTreeNode::Internal {
1086 left_bytes: new_left_bytes,
1087 lf_left: new_lf_left,
1088 left: new_left,
1089 right: new_right,
1090 })
1091 }
1092 PieceTreeNode::Leaf {
1093 location,
1094 offset,
1095 bytes,
1096 line_feed_cnt: _,
1097 } => {
1098 let piece_start = current_offset;
1099 let piece_end = current_offset + bytes;
1100
1101 if piece_end <= delete_start || piece_start >= delete_end {
1103 return Arc::clone(node);
1104 }
1105
1106 if delete_start <= piece_start && delete_end >= piece_end {
1108 return Arc::new(PieceTreeNode::Leaf {
1109 location: BufferLocation::Stored(0),
1110 offset: 0,
1111 bytes: 0,
1112 line_feed_cnt: Some(0),
1113 });
1114 }
1115
1116 let before_bytes = delete_start.saturating_sub(piece_start);
1118 let after_bytes = piece_end.saturating_sub(delete_end);
1119
1120 if before_bytes > 0 && after_bytes > 0 {
1121 let lf_before =
1123 Self::compute_line_feeds_static(buffers, *location, *offset, before_bytes);
1124 let lf_after = Self::compute_line_feeds_static(
1125 buffers,
1126 *location,
1127 offset + bytes - after_bytes,
1128 after_bytes,
1129 );
1130 let leaf_before = LeafData::new(*location, *offset, before_bytes, lf_before);
1131 let leaf_after = LeafData::new(
1132 *location,
1133 offset + bytes - after_bytes,
1134 after_bytes,
1135 lf_after,
1136 );
1137 Self::build_balanced(&[leaf_before, leaf_after])
1138 } else if before_bytes > 0 {
1139 let lf_cnt =
1141 Self::compute_line_feeds_static(buffers, *location, *offset, before_bytes);
1142 Arc::new(PieceTreeNode::Leaf {
1143 location: *location,
1144 offset: *offset,
1145 bytes: before_bytes,
1146 line_feed_cnt: lf_cnt,
1147 })
1148 } else {
1149 let skip = delete_end - piece_start;
1151 let lf_cnt = Self::compute_line_feeds_static(
1152 buffers,
1153 *location,
1154 offset + skip,
1155 after_bytes,
1156 );
1157 Arc::new(PieceTreeNode::Leaf {
1158 location: *location,
1159 offset: offset + skip,
1160 bytes: after_bytes,
1161 line_feed_cnt: lf_cnt,
1162 })
1163 }
1164 }
1165 }
1166 }
1167
1168 pub(crate) fn rebalance(&mut self) {
1170 let mut leaves = Vec::new();
1171 self.root.collect_leaves(&mut leaves);
1172 self.root = Self::build_balanced(&leaves);
1173 }
1174
1175 fn check_and_rebalance(&mut self) {
1177 let count = self.root.count_leaves();
1178 if count < 2 {
1179 return;
1180 }
1181
1182 let depth = self.root.depth();
1183 let max_depth = 2 * (count as f64).log2().ceil() as usize;
1184
1185 if depth > max_depth {
1186 self.rebalance();
1187 }
1188 }
1189
1190 pub fn find_by_offset(&self, offset: usize) -> Option<PieceInfo> {
1192 if offset >= self.total_bytes {
1193 return None;
1194 }
1195 self.root.find_by_offset(offset).map(|result| result.info)
1196 }
1197
1198 pub fn cursor_at_offset(&self, offset: usize) -> Cursor {
1201 Cursor {
1202 byte_offset: offset.min(self.total_bytes),
1203 line: 0,
1204 col: 0,
1205 }
1206 }
1207
1208 pub fn insert(
1213 &mut self,
1214 offset: usize,
1215 location: BufferLocation,
1216 buffer_offset: usize,
1217 bytes: usize,
1218 line_feed_cnt: Option<usize>,
1219 buffers: &[StringBuffer],
1220 ) -> Cursor {
1221 if bytes == 0 {
1222 return self.cursor_at_offset(offset);
1223 }
1224
1225 let insert_leaf = LeafData::new(location, buffer_offset, bytes, line_feed_cnt);
1226 self.root = Self::path_copy_insert(&self.root, 0, offset, insert_leaf, buffers);
1227 self.total_bytes += bytes;
1228 self.check_and_rebalance();
1229
1230 self.cursor_at_offset(offset + bytes)
1231 }
1232
1233 pub fn root(&self) -> Arc<PieceTreeNode> {
1235 Arc::clone(&self.root)
1236 }
1237
1238 #[allow(clippy::too_many_arguments)]
1242 pub fn insert_at_position(
1243 &mut self,
1244 line: usize,
1245 column: usize,
1246 location: BufferLocation,
1247 buffer_offset: usize,
1248 bytes: usize,
1249 line_feed_cnt: usize,
1250 buffers: &[StringBuffer],
1251 ) -> Cursor {
1252 let offset = self.position_to_offset(line, column, buffers);
1253 self.insert(
1254 offset,
1255 location,
1256 buffer_offset,
1257 bytes,
1258 Some(line_feed_cnt),
1259 buffers,
1260 )
1261 }
1262
1263 fn collect_leaves_with_split(
1265 &self,
1266 node: &Arc<PieceTreeNode>,
1267 current_offset: usize,
1268 split_offset: usize,
1269 insert: Option<LeafData>,
1270 leaves: &mut Vec<LeafData>,
1271 buffers: &[StringBuffer],
1272 ) {
1273 match node.as_ref() {
1274 PieceTreeNode::Internal {
1275 left_bytes,
1276 left,
1277 right,
1278 ..
1279 } => {
1280 if split_offset < current_offset + left_bytes {
1282 self.collect_leaves_with_split(
1284 left,
1285 current_offset,
1286 split_offset,
1287 insert,
1288 leaves,
1289 buffers,
1290 );
1291 self.collect_leaves_with_split(
1292 right,
1293 current_offset + left_bytes,
1294 split_offset,
1295 None,
1296 leaves,
1297 buffers,
1298 );
1299 } else {
1300 self.collect_leaves_with_split(
1302 left,
1303 current_offset,
1304 split_offset,
1305 None,
1306 leaves,
1307 buffers,
1308 );
1309 self.collect_leaves_with_split(
1310 right,
1311 current_offset + left_bytes,
1312 split_offset,
1313 insert,
1314 leaves,
1315 buffers,
1316 );
1317 }
1318 }
1319 PieceTreeNode::Leaf {
1320 location,
1321 offset,
1322 bytes,
1323 line_feed_cnt,
1324 } => {
1325 let piece_end = current_offset + bytes;
1326
1327 if split_offset > current_offset && split_offset < piece_end {
1328 let offset_in_piece = split_offset - current_offset;
1330
1331 if offset_in_piece > 0 {
1333 let lf_cnt = Self::compute_line_feeds_static(
1334 buffers,
1335 *location,
1336 *offset,
1337 offset_in_piece,
1338 );
1339 leaves.push(LeafData::new(*location, *offset, offset_in_piece, lf_cnt));
1340 }
1341
1342 if let Some(insert_leaf) = insert {
1344 leaves.push(insert_leaf);
1345 }
1346
1347 let remaining = bytes - offset_in_piece;
1349 if remaining > 0 {
1350 let lf_cnt = Self::compute_line_feeds_static(
1351 buffers,
1352 *location,
1353 offset + offset_in_piece,
1354 remaining,
1355 );
1356 leaves.push(LeafData::new(
1357 *location,
1358 offset + offset_in_piece,
1359 remaining,
1360 lf_cnt,
1361 ));
1362 }
1363 } else if split_offset == current_offset {
1364 if let Some(insert_leaf) = insert {
1366 leaves.push(insert_leaf);
1367 }
1368 leaves.push(LeafData::new(*location, *offset, *bytes, *line_feed_cnt));
1369 } else {
1370 leaves.push(LeafData::new(*location, *offset, *bytes, *line_feed_cnt));
1372 }
1373 }
1374 }
1375 }
1376
1377 fn compute_line_feeds_static(
1379 buffers: &[StringBuffer],
1380 location: BufferLocation,
1381 offset: usize,
1382 bytes: usize,
1383 ) -> Option<usize> {
1384 let buffer_id = location.buffer_id();
1385 if let Some(buffer) = buffers.get(buffer_id) {
1386 if let Some(data) = buffer.get_data() {
1387 let end = (offset + bytes).min(data.len());
1388 Some(data[offset..end].iter().filter(|&&b| b == b'\n').count())
1389 } else {
1390 None
1392 }
1393 } else {
1394 None
1396 }
1397 }
1398
1399 pub fn split_at_offset(&mut self, offset: usize, buffers: &[StringBuffer]) {
1406 if offset == 0 || offset >= self.total_bytes {
1407 return;
1408 }
1409
1410 if let Some(_result) = self.root.find_by_offset(offset) {
1412 let mut leaves = Vec::new();
1414 self.collect_leaves_with_split(&self.root, 0, offset, None, &mut leaves, buffers);
1415
1416 self.root = Self::build_balanced(&leaves);
1417 self.check_and_rebalance();
1418 }
1419 }
1420
1421 pub fn replace_buffer_reference(
1427 &mut self,
1428 old_buffer_id: usize,
1429 old_buffer_offset: usize,
1430 old_buffer_bytes: usize,
1431 new_buffer_location: BufferLocation,
1432 ) {
1433 let mut leaves = Vec::new();
1434 self.root.collect_leaves(&mut leaves);
1435
1436 let mut modified = false;
1438 for leaf in &mut leaves {
1439 if leaf.location.buffer_id() == old_buffer_id
1440 && leaf.offset == old_buffer_offset
1441 && leaf.bytes == old_buffer_bytes
1442 {
1443 leaf.location = new_buffer_location;
1444 leaf.offset = 0; modified = true;
1446 }
1447 }
1448
1449 if modified {
1451 self.root = Self::build_balanced(&leaves);
1452 self.check_and_rebalance();
1453 }
1454 }
1455
1456 pub fn delete(&mut self, offset: usize, delete_bytes: usize, buffers: &[StringBuffer]) {
1458 if delete_bytes == 0 || offset >= self.total_bytes {
1459 return;
1460 }
1461
1462 let delete_bytes = delete_bytes.min(self.total_bytes - offset);
1463 let end_offset = offset + delete_bytes;
1464
1465 self.root = Self::path_copy_delete(&self.root, 0, offset, end_offset, buffers);
1466 self.total_bytes -= delete_bytes;
1467
1468 self.check_and_rebalance();
1469 }
1470
1471 pub fn delete_position_range(
1474 &mut self,
1475 start_line: usize,
1476 start_column: usize,
1477 end_line: usize,
1478 end_column: usize,
1479 buffers: &[StringBuffer],
1480 ) {
1481 if start_line == end_line && start_column == end_column {
1483 return;
1484 }
1485
1486 let start_offset = self.position_to_offset(start_line, start_column, buffers);
1487 let end_offset = self.position_to_offset(end_line, end_column, buffers);
1488
1489 if end_offset > start_offset {
1490 self.delete(start_offset, end_offset - start_offset, buffers);
1491 }
1492 }
1493
1494 pub fn total_bytes(&self) -> usize {
1496 self.total_bytes
1497 }
1498
1499 pub fn line_count(&self) -> Option<usize> {
1503 self.root.total_line_feeds().map(|lf| lf + 1)
1504 }
1505
1506 pub fn piece_info_for_line(
1512 &self,
1513 target_line: usize,
1514 ) -> Option<(usize, usize, usize, usize, usize)> {
1515 self.root.find_piece_for_line(0, 0, target_line)
1516 }
1517
1518 pub fn update_leaf_line_feeds(&mut self, updates: &[(usize, usize)]) {
1528 if updates.is_empty() {
1529 return;
1530 }
1531
1532 let mut leaves = Vec::new();
1533 self.root.collect_leaves(&mut leaves);
1534
1535 for &(idx, lf_count) in updates {
1536 if let Some(leaf) = leaves.get_mut(idx) {
1537 leaf.line_feed_cnt = Some(lf_count);
1538 }
1539 }
1540
1541 self.root = Self::build_balanced(&leaves);
1542 }
1543
1544 pub fn update_leaf_line_feeds_path_copy(&mut self, updates: &[(usize, usize)]) {
1548 for &(idx, lf_count) in updates {
1549 let (new_root, _) = self.root.update_leaf_lf_by_index(idx, lf_count);
1550 self.root = new_root;
1551 }
1552 }
1553
1554 pub fn stats(&self) -> TreeStats {
1556 TreeStats {
1557 total_bytes: self.total_bytes,
1558 depth: self.root.depth(),
1559 leaf_count: self.root.count_leaves(),
1560 line_feed_count: self.root.total_line_feeds(),
1561 }
1562 }
1563
1564 pub fn get_leaves(&self) -> Vec<LeafData> {
1566 let mut leaves = Vec::new();
1567 self.root.collect_leaves(&mut leaves);
1568 leaves
1569 }
1570
1571 pub fn split_leaves_to_chunk_size(&mut self, max_bytes: usize) {
1577 let old_leaves = self.get_leaves();
1578 let mut new_leaves = Vec::with_capacity(old_leaves.len());
1579 let mut changed = false;
1580
1581 for leaf in &old_leaves {
1582 if leaf.bytes <= max_bytes {
1583 new_leaves.push(*leaf);
1584 } else {
1585 changed = true;
1586 let mut off = 0;
1587 while off < leaf.bytes {
1588 let len = max_bytes.min(leaf.bytes - off);
1589 new_leaves.push(LeafData::new(leaf.location, leaf.offset + off, len, None));
1590 off += len;
1591 }
1592 }
1593 }
1594
1595 if changed {
1596 self.root = Self::build_balanced(&new_leaves);
1597 }
1598 }
1599
1600 pub fn offset_to_position(
1602 &self,
1603 offset: usize,
1604 buffers: &[StringBuffer],
1605 ) -> Option<(usize, usize)> {
1606 if offset == 0 {
1607 return Some((0, 0));
1608 }
1609
1610 let offset = offset.min(self.total_bytes);
1611
1612 if let Some(result) = self.root.find_by_offset(offset) {
1614 let piece_info = result.info;
1615 let bytes_before = result.bytes_before;
1616
1617 let lines_before = match self.count_lines_before_offset(bytes_before) {
1620 Some(count) => count,
1621 None => {
1622 return None;
1624 }
1625 };
1626
1627 let buffer_id = piece_info.location.buffer_id();
1629 if let Some(buffer) = buffers.get(buffer_id) {
1630 let offset_in_piece = piece_info.offset_in_piece.unwrap_or(0);
1631
1632 if let Some(line_starts) = buffer.get_line_starts() {
1634 let byte_offset_in_buffer = piece_info.offset + offset_in_piece;
1635
1636 let line_in_buffer = line_starts
1638 .binary_search(&byte_offset_in_buffer)
1639 .unwrap_or_else(|i| i.saturating_sub(1));
1640
1641 let piece_start_line = line_starts
1643 .binary_search(&piece_info.offset)
1644 .unwrap_or_else(|i| i.saturating_sub(1));
1645
1646 let line_in_piece = line_in_buffer - piece_start_line;
1648
1649 let doc_line = lines_before + line_in_piece;
1651
1652 let column = if line_in_piece == 0 && bytes_before == 0 {
1654 offset_in_piece
1656 } else if line_in_piece == 0 {
1657 let line_start = self.position_to_offset(doc_line, 0, buffers);
1660 offset.saturating_sub(line_start)
1661 } else {
1662 let line_start_in_buf = buffer
1664 .nth_line_start_in_piece(
1665 piece_info.offset,
1666 piece_info.offset + piece_info.bytes,
1667 line_in_piece,
1668 )
1669 .unwrap_or(piece_info.offset);
1670 let line_start_offset_in_piece = line_start_in_buf - piece_info.offset;
1671 offset_in_piece - line_start_offset_in_piece
1672 };
1673
1674 return Some((doc_line, column));
1675 }
1676
1677 if let Some(data) = buffer.get_data() {
1680 let piece_start = piece_info.offset;
1681 let scan_end = (piece_start + offset_in_piece).min(data.len());
1682 let line_in_piece = data[piece_start..scan_end]
1683 .iter()
1684 .filter(|&&b| b == b'\n')
1685 .count();
1686 let doc_line = lines_before + line_in_piece;
1687
1688 let column = if line_in_piece == 0 {
1690 if bytes_before == 0 {
1691 offset_in_piece
1692 } else {
1693 let line_start = self.position_to_offset(doc_line, 0, buffers);
1695 offset.saturating_sub(line_start)
1696 }
1697 } else {
1698 let last_nl = data[piece_start..scan_end]
1700 .iter()
1701 .rposition(|&b| b == b'\n')
1702 .unwrap_or(0);
1703 offset_in_piece - last_nl - 1
1704 };
1705
1706 return Some((doc_line, column));
1707 }
1708 if let Some(lf_count) = piece_info.line_feeds {
1712 let piece_bytes = piece_info.bytes;
1713 let lines_in_partial = if piece_bytes > 0 {
1714 (offset_in_piece as u64 * lf_count as u64 / piece_bytes as u64) as usize
1715 } else {
1716 0
1717 };
1718 let doc_line = lines_before + lines_in_partial;
1719 let avg_bytes_per_line = if lf_count > 0 {
1721 piece_bytes / lf_count
1722 } else {
1723 80
1724 };
1725 let column = if avg_bytes_per_line > 0 {
1726 offset_in_piece % avg_bytes_per_line
1727 } else {
1728 0
1729 };
1730 return Some((doc_line, column));
1731 }
1732 }
1733 }
1734
1735 if offset >= self.total_bytes {
1738 match self.line_count() {
1739 Some(line_count) => {
1740 let last_line = line_count.saturating_sub(1);
1741 let line_start = self.position_to_offset(last_line, 0, buffers);
1742 let column = self.total_bytes.saturating_sub(line_start);
1743 return Some((last_line, column));
1744 }
1745 None => return None,
1746 }
1747 }
1748 None
1749 }
1750
1751 pub fn position_to_offset(
1753 &self,
1754 line: usize,
1755 column: usize,
1756 buffers: &[StringBuffer],
1757 ) -> usize {
1758 if line == 0 && column == 0 {
1759 return 0;
1760 }
1761
1762 self.find_offset_for_line(line, column, buffers)
1764 .unwrap_or(self.total_bytes)
1765 }
1766
1767 fn count_lines_before_offset(&self, byte_offset: usize) -> Option<usize> {
1770 self.count_lines_in_range(0, byte_offset)
1771 }
1772
1773 fn count_lines_in_range(&self, start: usize, end: usize) -> Option<usize> {
1776 if start >= end {
1777 return Some(0);
1778 }
1779
1780 self.root.count_lines_in_byte_range(0, start, end)
1781 }
1782
1783 fn find_offset_for_line(
1785 &self,
1786 target_line: usize,
1787 column: usize,
1788 buffers: &[StringBuffer],
1789 ) -> Option<usize> {
1790 self.root
1791 .find_byte_offset_for_line(0, 0, target_line, column, buffers)
1792 }
1793
1794 pub fn line_range(
1796 &self,
1797 line: usize,
1798 buffers: &[StringBuffer],
1799 ) -> Option<(usize, Option<usize>)> {
1800 let line_count = self.line_count()?;
1802 if line >= line_count {
1803 return None;
1804 }
1805
1806 let start = self.position_to_offset(line, 0, buffers);
1807 let end = if line + 1 < line_count {
1808 Some(self.position_to_offset(line + 1, 0, buffers))
1809 } else {
1810 None
1811 };
1812 Some((start, end))
1813 }
1814
1815 pub fn iter_pieces_in_range(&self, start: usize, end: usize) -> PieceRangeIter {
1818 PieceRangeIter::new(&self.root, start, end)
1819 }
1820
1821 pub fn apply_bulk_edits<F>(
1834 &mut self,
1835 edits: &[(usize, usize, &str)],
1836 buffers: &[StringBuffer],
1837 mut add_text_fn: F,
1838 ) -> isize
1839 where
1840 F: FnMut(&str) -> (BufferLocation, usize, usize, Option<usize>),
1841 {
1842 if edits.is_empty() {
1843 return 0;
1844 }
1845
1846 let mut split_points: Vec<usize> = Vec::with_capacity(edits.len() * 2);
1848 for (pos, del_len, _) in edits {
1849 split_points.push(*pos);
1850 if *del_len > 0 {
1851 let end = pos.saturating_add(*del_len).min(self.total_bytes);
1852 if end > *pos {
1853 split_points.push(end);
1854 }
1855 }
1856 }
1857 split_points.sort_unstable();
1858 split_points.dedup();
1859
1860 let mut leaves = Vec::new();
1862 self.collect_leaves_with_multi_split(
1863 &self.root.clone(),
1864 0,
1865 &split_points,
1866 &mut leaves,
1867 buffers,
1868 );
1869
1870 let mut edit_ranges: Vec<(usize, usize, Option<LeafData>)> =
1873 Vec::with_capacity(edits.len());
1874 for (pos, del_len, text) in edits {
1875 let del_end = pos.saturating_add(*del_len).min(self.total_bytes);
1876 let insert_leaf = if !text.is_empty() {
1877 let (location, offset, bytes, lf_cnt) = add_text_fn(text);
1878 Some(LeafData::new(location, offset, bytes, lf_cnt))
1879 } else {
1880 None
1881 };
1882 edit_ranges.push((*pos, del_end, insert_leaf));
1883 }
1884
1885 let mut new_leaves: Vec<LeafData> = Vec::with_capacity(leaves.len() + edits.len());
1892 let mut current_offset = 0;
1893 let mut edit_idx = edit_ranges.len(); for leaf in leaves {
1896 let leaf_start = current_offset;
1897 let leaf_end = current_offset + leaf.bytes;
1898
1899 while edit_idx > 0 {
1902 let (edit_start, _edit_end, ref insert_leaf) = edit_ranges[edit_idx - 1];
1903
1904 if edit_start > leaf_start {
1906 break;
1907 }
1908
1909 if let Some(insert) = insert_leaf {
1911 new_leaves.push(*insert);
1912 }
1913 edit_idx -= 1;
1914 }
1915
1916 let mut keep_leaf = true;
1920
1921 for (edit_start, edit_end, _) in &edit_ranges {
1922 if *edit_start >= leaf_end {
1924 continue;
1925 }
1926
1927 if *edit_start == *edit_end {
1929 continue;
1930 }
1931
1932 if *edit_end <= leaf_start {
1934 continue;
1935 }
1936
1937 if leaf_start >= *edit_start && leaf_end <= *edit_end {
1939 keep_leaf = false;
1940 break;
1941 }
1942 }
1943
1944 if keep_leaf {
1945 new_leaves.push(leaf);
1946 }
1947
1948 current_offset = leaf_end;
1949 }
1950
1951 while edit_idx > 0 {
1953 if let Some(insert) = &edit_ranges[edit_idx - 1].2 {
1954 new_leaves.push(*insert);
1955 }
1956 edit_idx -= 1;
1957 }
1958
1959 let old_bytes = self.total_bytes;
1961 let mut new_bytes: usize = 0;
1962 for leaf in &new_leaves {
1963 new_bytes += leaf.bytes;
1964 }
1965 let delta = new_bytes as isize - old_bytes as isize;
1966
1967 self.root = Self::build_balanced(&new_leaves);
1969 self.total_bytes = new_bytes;
1970
1971 delta
1972 }
1973
1974 fn collect_leaves_with_multi_split(
1976 &self,
1977 node: &Arc<PieceTreeNode>,
1978 current_offset: usize,
1979 split_points: &[usize],
1980 leaves: &mut Vec<LeafData>,
1981 buffers: &[StringBuffer],
1982 ) {
1983 match node.as_ref() {
1984 PieceTreeNode::Internal {
1985 left_bytes,
1986 left,
1987 right,
1988 ..
1989 } => {
1990 self.collect_leaves_with_multi_split(
1992 left,
1993 current_offset,
1994 split_points,
1995 leaves,
1996 buffers,
1997 );
1998 self.collect_leaves_with_multi_split(
1999 right,
2000 current_offset + left_bytes,
2001 split_points,
2002 leaves,
2003 buffers,
2004 );
2005 }
2006 PieceTreeNode::Leaf {
2007 location,
2008 offset,
2009 bytes,
2010 line_feed_cnt,
2011 } => {
2012 if *bytes == 0 {
2013 return;
2014 }
2015
2016 let piece_start = current_offset;
2017 let piece_end = current_offset + bytes;
2018
2019 let mut split_offsets: Vec<usize> = Vec::new();
2021 for &sp in split_points {
2022 if sp > piece_start && sp < piece_end {
2023 split_offsets.push(sp - piece_start);
2024 }
2025 }
2026
2027 if split_offsets.is_empty() {
2028 leaves.push(LeafData::new(*location, *offset, *bytes, *line_feed_cnt));
2030 } else {
2031 split_offsets.sort_unstable();
2033 split_offsets.dedup();
2034
2035 let mut prev_offset = 0;
2036 for split_offset in split_offsets {
2037 if split_offset > prev_offset {
2038 let chunk_bytes = split_offset - prev_offset;
2039 let lf_cnt = Self::compute_line_feeds_static(
2040 buffers,
2041 *location,
2042 offset + prev_offset,
2043 chunk_bytes,
2044 );
2045 leaves.push(LeafData::new(
2046 *location,
2047 offset + prev_offset,
2048 chunk_bytes,
2049 lf_cnt,
2050 ));
2051 }
2052 prev_offset = split_offset;
2053 }
2054
2055 if prev_offset < *bytes {
2057 let remaining = bytes - prev_offset;
2058 let lf_cnt = Self::compute_line_feeds_static(
2059 buffers,
2060 *location,
2061 offset + prev_offset,
2062 remaining,
2063 );
2064 leaves.push(LeafData::new(
2065 *location,
2066 offset + prev_offset,
2067 remaining,
2068 lf_cnt,
2069 ));
2070 }
2071 }
2072 }
2073 }
2074 }
2075}
2076
2077#[derive(Debug, Clone)]
2079pub struct PieceView {
2080 pub location: BufferLocation,
2082 pub buffer_offset: usize,
2084 pub bytes: usize,
2086 pub doc_offset: usize,
2088 pub line_feed_cnt: Option<usize>,
2090}
2091
2092pub struct PieceRangeIter {
2095 pieces: Vec<PieceView>,
2096 current_index: usize,
2097}
2098
2099impl PieceRangeIter {
2100 fn new(root: &Arc<PieceTreeNode>, start: usize, end: usize) -> Self {
2101 let mut pieces = Vec::new();
2102 Self::collect_pieces(root, 0, start, end, &mut pieces);
2103 PieceRangeIter {
2104 pieces,
2105 current_index: 0,
2106 }
2107 }
2108
2109 fn collect_pieces(
2111 node: &Arc<PieceTreeNode>,
2112 doc_offset: usize,
2113 range_start: usize,
2114 range_end: usize,
2115 pieces: &mut Vec<PieceView>,
2116 ) {
2117 match node.as_ref() {
2118 PieceTreeNode::Internal {
2119 left_bytes,
2120 left,
2121 right,
2122 ..
2123 } => {
2124 let left_end = doc_offset + left_bytes;
2125
2126 if range_start < left_end {
2128 Self::collect_pieces(left, doc_offset, range_start, range_end, pieces);
2129 }
2130
2131 if range_end > left_end {
2133 Self::collect_pieces(right, left_end, range_start, range_end, pieces);
2134 }
2135 }
2136 PieceTreeNode::Leaf {
2137 location,
2138 offset,
2139 bytes,
2140 line_feed_cnt,
2141 } => {
2142 let piece_end = doc_offset + bytes;
2143
2144 if doc_offset < range_end && piece_end > range_start {
2146 pieces.push(PieceView {
2147 location: *location,
2148 buffer_offset: *offset,
2149 bytes: *bytes,
2150 doc_offset,
2151 line_feed_cnt: *line_feed_cnt,
2152 });
2153 }
2154 }
2155 }
2156 }
2157}
2158
2159impl Iterator for PieceRangeIter {
2160 type Item = PieceView;
2161
2162 fn next(&mut self) -> Option<Self::Item> {
2163 if self.current_index < self.pieces.len() {
2164 let piece = self.pieces[self.current_index].clone();
2165 self.current_index += 1;
2166 Some(piece)
2167 } else {
2168 None
2169 }
2170 }
2171}
2172
2173#[cfg(test)]
2174mod tests {
2175 use super::*;
2176
2177 fn test_buffers() -> Vec<StringBuffer> {
2179 vec![
2180 StringBuffer::new(0, vec![b'a'; 100]), StringBuffer::new(1, vec![b'b'; 50]), StringBuffer::new(2, vec![b'c'; 25]), ]
2184 }
2185
2186 #[test]
2187 fn test_create_empty() {
2188 let tree = PieceTree::empty();
2189 assert_eq!(tree.total_bytes(), 0);
2190 }
2191
2192 #[test]
2193 fn test_create_with_initial_piece() {
2194 let tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2195 assert_eq!(tree.total_bytes(), 100);
2196 }
2197
2198 #[test]
2199 fn test_insert_at_end() {
2200 let buffers = test_buffers();
2201 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2202 tree.insert(100, BufferLocation::Added(1), 0, 50, Some(0), &buffers);
2203 assert_eq!(tree.total_bytes(), 150);
2204 }
2205
2206 #[test]
2207 fn test_insert_in_middle() {
2208 let buffers = test_buffers();
2209 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2210 tree.insert(50, BufferLocation::Added(2), 0, 25, Some(0), &buffers);
2211 assert_eq!(tree.total_bytes(), 125);
2212 let stats = tree.stats();
2213 assert_eq!(stats.leaf_count, 3); }
2215
2216 #[test]
2217 fn test_delete() {
2218 let buffers = test_buffers();
2219 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2220 tree.delete(25, 50, &buffers);
2221 assert_eq!(tree.total_bytes(), 50);
2222 }
2223
2224 #[test]
2225 fn test_delete_at_boundaries() {
2226 let buffers = test_buffers();
2227 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2228
2229 tree.delete(0, 10, &buffers);
2231 assert_eq!(tree.total_bytes(), 90);
2232
2233 tree.delete(80, 10, &buffers);
2235 assert_eq!(tree.total_bytes(), 80);
2236 }
2237
2238 #[test]
2239 fn test_multiple_inserts_and_deletes() {
2240 let buffers = test_buffers();
2241 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2242
2243 tree.insert(50, BufferLocation::Added(1), 0, 20, Some(0), &buffers);
2244 assert_eq!(tree.total_bytes(), 120);
2245
2246 tree.delete(40, 30, &buffers);
2247 assert_eq!(tree.total_bytes(), 90);
2248
2249 tree.insert(0, BufferLocation::Added(1), 20, 10, Some(0), &buffers);
2250 assert_eq!(tree.total_bytes(), 100);
2251 }
2252
2253 #[test]
2254 fn test_rebalancing_many_inserts() {
2255 let buffers = test_buffers();
2256 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2257
2258 for i in 0..20 {
2260 tree.insert(i * 5, BufferLocation::Added(1), i, 1, Some(0), &buffers);
2261 }
2262
2263 let stats = tree.stats();
2264 assert_eq!(stats.total_bytes, 120);
2265 assert!(stats.leaf_count > 20);
2268 assert!(stats.leaf_count < 50); let max_expected_depth = 2 * (stats.leaf_count as f64).log2().ceil() as usize;
2272 assert!(
2273 stats.depth <= max_expected_depth + 2,
2274 "Tree depth {} exceeds max {} for {} leaves",
2275 stats.depth,
2276 max_expected_depth,
2277 stats.leaf_count
2278 );
2279 }
2280
2281 #[test]
2282 fn test_find_by_offset() {
2283 let tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2284
2285 let info = tree.find_by_offset(50).unwrap();
2286 assert_eq!(info.location, BufferLocation::Stored(0));
2287 assert_eq!(info.offset_in_piece, Some(50));
2288
2289 assert!(tree.find_by_offset(100).is_none());
2291 }
2292
2293 #[test]
2294 fn test_find_after_inserts() {
2295 let buffers = test_buffers();
2296 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2297 tree.insert(50, BufferLocation::Added(1), 0, 25, Some(0), &buffers);
2298
2299 let info = tree.find_by_offset(50).unwrap();
2301 assert_eq!(info.location, BufferLocation::Added(1));
2302 }
2303
2304 #[test]
2305 fn test_offset_to_position_column_after_modification() {
2306 let initial = b"fn foo(val: i32) {\n val + 1\n}\n";
2318 let buffer = StringBuffer::new(0, initial.to_vec());
2319 let buffers = vec![buffer.clone()];
2320
2321 let mut tree = PieceTree::new(
2322 BufferLocation::Stored(0),
2323 0,
2324 initial.len(),
2325 Some(initial.iter().filter(|&&b| b == b'\n').count()),
2326 );
2327
2328 let pos = tree.offset_to_position(23, &buffers);
2331 assert_eq!(
2332 pos,
2333 Some((1, 4)),
2334 "Initial: position 23 should be line 1, column 4"
2335 );
2336
2337 tree.delete(23, 3, &buffers);
2345
2346 let value_buf = StringBuffer::new(1, b"value".to_vec());
2348 let buffers = vec![buffer.clone(), value_buf.clone()];
2349 tree.insert(23, BufferLocation::Added(1), 0, 5, Some(0), &buffers);
2350
2351 tree.delete(7, 3, &buffers);
2353
2354 let value_buf2 = StringBuffer::new(2, b"value".to_vec());
2356 let buffers = vec![buffer.clone(), value_buf.clone(), value_buf2];
2357 tree.insert(7, BufferLocation::Added(2), 0, 5, Some(0), &buffers);
2358
2359 let pos = tree.offset_to_position(25, &buffers);
2366 assert_eq!(
2367 pos,
2368 Some((1, 4)),
2369 "After modification: position 25 should be line 1, column 4"
2370 );
2371
2372 let pos = tree.offset_to_position(21, &buffers);
2374 assert_eq!(pos, Some((1, 0)), "Position 21 should be line 1, column 0");
2375 }
2376
2377 fn prepare_bulk_edit_buffers(
2381 buffers: &mut Vec<StringBuffer>,
2382 texts: &[&str],
2383 ) -> Vec<(BufferLocation, usize, usize, Option<usize>)> {
2384 let mut infos = Vec::new();
2385 for (i, text) in texts.iter().enumerate() {
2386 let id = buffers.len();
2387 let bytes = text.as_bytes().to_vec();
2388 let lf = bytes.iter().filter(|&&b| b == b'\n').count();
2389 let len = bytes.len();
2390 buffers.push(StringBuffer::new(id, bytes));
2391 infos.push((BufferLocation::Added(id), 0, len, Some(lf)));
2392 let _ = i; }
2394 infos
2395 }
2396
2397 #[test]
2398 fn test_bulk_edit_single_insert() {
2399 let mut buffers = vec![StringBuffer::new(0, b"hello world".to_vec())];
2400 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 11, Some(0));
2401
2402 let infos = prepare_bulk_edit_buffers(&mut buffers, &["!"]);
2404
2405 let edits: Vec<(usize, usize, &str)> = vec![(11, 0, "!")];
2407 let mut idx = 0;
2408
2409 let delta = tree.apply_bulk_edits(&edits, &buffers, |_text| {
2410 let info = infos[idx];
2411 idx += 1;
2412 info
2413 });
2414
2415 assert_eq!(delta, 1);
2416 assert_eq!(tree.total_bytes(), 12);
2417 }
2418
2419 #[test]
2420 fn test_bulk_edit_single_delete() {
2421 let buffers = vec![StringBuffer::new(0, b"hello world".to_vec())];
2422 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 11, Some(0));
2423
2424 let edits: Vec<(usize, usize, &str)> = vec![(6, 5, "")];
2426
2427 let delta = tree.apply_bulk_edits(&edits, &buffers, |_text| {
2428 (BufferLocation::Added(1), 0, 0, Some(0))
2429 });
2430
2431 assert_eq!(delta, -5);
2432 assert_eq!(tree.total_bytes(), 6);
2433 }
2434
2435 #[test]
2436 fn test_bulk_edit_single_replace() {
2437 let mut buffers = vec![StringBuffer::new(0, b"hello world".to_vec())];
2438 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 11, Some(0));
2439
2440 let infos = prepare_bulk_edit_buffers(&mut buffers, &["rust"]);
2442
2443 let edits: Vec<(usize, usize, &str)> = vec![(6, 5, "rust")];
2445 let mut idx = 0;
2446
2447 let delta = tree.apply_bulk_edits(&edits, &buffers, |_text| {
2448 let info = infos[idx];
2449 idx += 1;
2450 info
2451 });
2452
2453 assert_eq!(delta, -1); assert_eq!(tree.total_bytes(), 10);
2455 }
2456
2457 #[test]
2458 fn test_bulk_edit_multiple_inserts_descending() {
2459 let mut buffers = vec![StringBuffer::new(0, b"abc".to_vec())];
2461 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 3, Some(0));
2462
2463 let infos = prepare_bulk_edit_buffers(&mut buffers, &["X", "X", "X", "X"]);
2465
2466 let edits: Vec<(usize, usize, &str)> = vec![
2468 (3, 0, "X"), (2, 0, "X"), (1, 0, "X"), (0, 0, "X"), ];
2473 let mut idx = 0;
2474
2475 let delta = tree.apply_bulk_edits(&edits, &buffers, |_text| {
2476 let info = infos[idx];
2477 idx += 1;
2478 info
2479 });
2480
2481 assert_eq!(delta, 4);
2482 assert_eq!(tree.total_bytes(), 7); }
2484
2485 #[test]
2486 fn test_bulk_edit_multiple_deletes_descending() {
2487 let buffers = vec![StringBuffer::new(0, b"abcdefgh".to_vec())];
2488 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 8, Some(0));
2489
2490 let edits: Vec<(usize, usize, &str)> = vec![
2492 (6, 1, ""), (4, 1, ""), (2, 1, ""), (0, 1, ""), ];
2497
2498 let delta = tree.apply_bulk_edits(&edits, &buffers, |_| {
2499 (BufferLocation::Added(1), 0, 0, Some(0))
2500 });
2501
2502 assert_eq!(delta, -4);
2503 assert_eq!(tree.total_bytes(), 4); }
2505
2506 #[test]
2507 fn test_bulk_edit_empty_edits() {
2508 let buffers = vec![StringBuffer::new(0, b"hello".to_vec())];
2509 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 5, Some(0));
2510
2511 let edits: Vec<(usize, usize, &str)> = vec![];
2512
2513 let delta = tree.apply_bulk_edits(&edits, &buffers, |_| {
2514 (BufferLocation::Added(1), 0, 0, Some(0))
2515 });
2516
2517 assert_eq!(delta, 0);
2518 assert_eq!(tree.total_bytes(), 5);
2519 }
2520
2521 #[test]
2522 fn test_bulk_edit_consistency_check() {
2523 let mut buffers = vec![StringBuffer::new(0, b"0123456789".to_vec())];
2525 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 10, Some(0));
2526
2527 let infos = prepare_bulk_edit_buffers(&mut buffers, &["XX", "Y", "ZZZ"]);
2529
2530 let edits: Vec<(usize, usize, &str)> = vec![
2532 (8, 1, "XX"), (5, 2, "Y"), (2, 0, "ZZZ"), ];
2536 let mut idx = 0;
2537
2538 tree.apply_bulk_edits(&edits, &buffers, |_text| {
2539 let info = infos[idx];
2540 idx += 1;
2541 info
2542 });
2543
2544 let leaves = tree.get_leaves();
2546 let sum: usize = leaves.iter().map(|l| l.bytes).sum();
2547 assert_eq!(
2548 sum,
2549 tree.total_bytes(),
2550 "Piece sum {} != total_bytes {}",
2551 sum,
2552 tree.total_bytes()
2553 );
2554 }
2555
2556 #[test]
2557 fn test_bulk_edit_vs_sequential_equivalence() {
2558 let original_content = b"The quick brown fox";
2560
2561 let mut buffers1 = vec![StringBuffer::new(0, original_content.to_vec())];
2563 let mut tree1 = PieceTree::new(BufferLocation::Stored(0), 0, 19, Some(0));
2564
2565 let infos = prepare_bulk_edit_buffers(&mut buffers1, &["red", "slow"]);
2567
2568 let mut buffers2 = vec![StringBuffer::new(0, original_content.to_vec())];
2570 let mut tree2 = PieceTree::new(BufferLocation::Stored(0), 0, 19, Some(0));
2571 let mut next_id2 = 1;
2572
2573 let edits: Vec<(usize, usize, &str)> = vec![
2577 (10, 5, "red"), (4, 5, "slow"), ];
2580 let mut idx = 0;
2581
2582 tree1.apply_bulk_edits(&edits, &buffers1, |_text| {
2584 let info = infos[idx];
2585 idx += 1;
2586 info
2587 });
2588
2589 tree2.delete(10, 5, &buffers2);
2592 buffers2.push(StringBuffer::new(next_id2, b"red".to_vec()));
2593 tree2.insert(
2594 10,
2595 BufferLocation::Added(next_id2),
2596 0,
2597 3,
2598 Some(0),
2599 &buffers2,
2600 );
2601 next_id2 += 1;
2602
2603 tree2.delete(4, 5, &buffers2);
2605 buffers2.push(StringBuffer::new(next_id2, b"slow".to_vec()));
2606 tree2.insert(4, BufferLocation::Added(next_id2), 0, 4, Some(0), &buffers2);
2607
2608 assert_eq!(
2609 tree1.total_bytes(),
2610 tree2.total_bytes(),
2611 "Bulk edit total_bytes {} != sequential {}",
2612 tree1.total_bytes(),
2613 tree2.total_bytes()
2614 );
2615 }
2616}
2617
2618#[cfg(test)]
2619mod property_tests {
2620 use super::*;
2621 use proptest::prelude::*;
2622
2623 fn test_buffers_large() -> Vec<StringBuffer> {
2625 vec![
2626 StringBuffer::new(0, vec![b'a'; 10000]), StringBuffer::new(1, vec![b'b'; 10000]),
2628 ]
2629 }
2630
2631 #[derive(Debug, Clone)]
2633 enum Operation {
2634 Insert { offset: usize, bytes: usize },
2635 Delete { offset: usize, bytes: usize },
2636 }
2637
2638 fn operation_strategy() -> impl Strategy<Value = Vec<Operation>> {
2640 prop::collection::vec(
2641 prop_oneof![
2642 (0usize..200, 1usize..50)
2643 .prop_map(|(offset, bytes)| { Operation::Insert { offset, bytes } }),
2644 (0usize..200, 1usize..50)
2645 .prop_map(|(offset, bytes)| { Operation::Delete { offset, bytes } }),
2646 ],
2647 0..50,
2648 )
2649 }
2650
2651 fn aggressive_operation_strategy() -> impl Strategy<Value = Vec<Operation>> {
2653 prop::collection::vec(
2654 prop_oneof![
2655 3 => (0usize..100, 1usize..20).prop_map(|(offset, bytes)| {
2657 Operation::Insert { offset, bytes }
2658 }),
2659 1 => (0usize..100, 1usize..30).prop_map(|(offset, bytes)| {
2661 Operation::Delete { offset, bytes }
2662 }),
2663 ],
2664 10..30, )
2666 }
2667
2668 proptest! {
2669 #[test]
2670 fn prop_total_bytes_consistency(operations in operation_strategy()) {
2671 let buffers = test_buffers_large();
2672 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2673 let mut expected_bytes = 100;
2674
2675 for op in operations {
2676 match op {
2677 Operation::Insert { offset, bytes } => {
2678 let offset = offset.min(tree.total_bytes());
2679 let buffer_len = buffers[1].get_data().map(|d| d.len()).unwrap_or(0);
2680 let bytes = bytes.min(buffer_len);
2681 tree.insert(offset, BufferLocation::Added(1), 0, bytes, Some(0), &buffers);
2682 expected_bytes += bytes;
2683 }
2684 Operation::Delete { offset, bytes } => {
2685 if offset < tree.total_bytes() {
2686 let actual_delete = bytes.min(tree.total_bytes() - offset);
2687 tree.delete(offset, bytes, &buffers);
2688 expected_bytes -= actual_delete;
2689 }
2690 }
2691 }
2692 }
2693
2694 prop_assert_eq!(tree.total_bytes(), expected_bytes);
2695 }
2696
2697 #[test]
2698 fn prop_tree_never_negative_bytes(operations in operation_strategy()) {
2699 let buffers = test_buffers_large();
2700 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2701
2702 for op in operations {
2703 match op {
2704 Operation::Insert { offset, bytes } => {
2705 let offset = offset.min(tree.total_bytes());
2706 let buffer_len = buffers[1].get_data().map(|d| d.len()).unwrap_or(0);
2707 let bytes = bytes.min(buffer_len);
2708 tree.insert(offset, BufferLocation::Added(1), 0, bytes, Some(0), &buffers);
2709 }
2710 Operation::Delete { offset, bytes } => {
2711 tree.delete(offset, bytes, &buffers);
2712 }
2713 }
2714
2715 prop_assert!(tree.total_bytes() < 10_000_000);
2717 }
2718 }
2719
2720 #[test]
2721 fn prop_balanced_after_operations(operations in operation_strategy()) {
2722 let buffers = test_buffers_large();
2723 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2724
2725 for op in operations {
2726 match op {
2727 Operation::Insert { offset, bytes } => {
2728 let offset = offset.min(tree.total_bytes());
2729 let buffer_len = buffers[1].get_data().map(|d| d.len()).unwrap_or(0);
2730 let bytes = bytes.min(buffer_len);
2731 tree.insert(offset, BufferLocation::Added(1), 0, bytes, Some(0), &buffers);
2732 }
2733 Operation::Delete { offset, bytes } => {
2734 tree.delete(offset, bytes, &buffers);
2735 }
2736 }
2737 }
2738
2739 let stats = tree.stats();
2740 if stats.leaf_count > 1 {
2741 let max_depth = 2 * (stats.leaf_count as f64).log2().ceil() as usize;
2742 prop_assert!(stats.depth <= max_depth + 2, "Tree depth {} exceeds expected max {} for {} leaves", stats.depth, max_depth, stats.leaf_count);
2743 }
2744 }
2745
2746 #[test]
2747 fn prop_insert_then_delete_equals_original(
2748 insert_offset in 0usize..100,
2749 insert_bytes in 1usize..50
2750 ) {
2751 let buffers = test_buffers_large();
2752 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2753 let original_bytes = tree.total_bytes();
2754
2755 let insert_offset = insert_offset.min(tree.total_bytes());
2756 let buffer_len = buffers[1].get_data().map(|d| d.len()).unwrap_or(0);
2757 let insert_bytes = insert_bytes.min(buffer_len);
2758 tree.insert(insert_offset, BufferLocation::Added(1), 0, insert_bytes, Some(0), &buffers);
2759
2760 tree.delete(insert_offset, insert_bytes, &buffers);
2762
2763 prop_assert_eq!(tree.total_bytes(), original_bytes);
2764 }
2765
2766 #[test]
2767 fn prop_find_offset_in_bounds(
2768 offset in 0usize..100
2769 ) {
2770 let tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2771
2772 let result = tree.find_by_offset(offset);
2773 prop_assert!(result.is_some());
2774 }
2775
2776 #[test]
2777 fn prop_find_offset_out_of_bounds(
2778 offset in 100usize..1000
2779 ) {
2780 let tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2781
2782 let result = tree.find_by_offset(offset);
2783 prop_assert!(result.is_none());
2784 }
2785
2786 #[test]
2787 fn prop_sequential_inserts_maintain_order(
2788 count in 1usize..20,
2789 insert_size in 1usize..10
2790 ) {
2791 let buffers = test_buffers_large();
2792 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 10, Some(0));
2793
2794 for _i in 0..count {
2795 let buffer_len = buffers[1].get_data().map(|d| d.len()).unwrap_or(0);
2796 let insert_size = insert_size.min(buffer_len);
2797 tree.insert(tree.total_bytes(), BufferLocation::Added(1), 0, insert_size, Some(0), &buffers);
2798 }
2799
2800 let expected_bytes = 10 + (count * insert_size);
2801 prop_assert_eq!(tree.total_bytes(), expected_bytes);
2802 }
2803
2804 #[test]
2805 fn prop_delete_all_reaches_zero(
2806 delete_size in 1usize..10
2807 ) {
2808 let buffers = test_buffers_large();
2809 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2810
2811 while tree.total_bytes() > 0 {
2812 let to_delete = delete_size.min(tree.total_bytes());
2813 tree.delete(0, to_delete, &buffers);
2814 }
2815
2816 prop_assert_eq!(tree.total_bytes(), 0);
2817 }
2818 }
2819
2820 #[test]
2821 fn test_empty_delete() {
2822 let buffers = test_buffers_large();
2823 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2824 tree.delete(50, 0, &buffers);
2825 assert_eq!(tree.total_bytes(), 100);
2826 }
2827
2828 #[derive(Debug, Clone)]
2832 struct BulkEditOp {
2833 position: usize,
2834 delete_len: usize,
2835 insert_text: String,
2836 }
2837
2838 fn bulk_edit_strategy() -> impl Strategy<Value = Vec<BulkEditOp>> {
2839 prop::collection::vec(
2840 (0usize..100, 0usize..20, "[a-zA-Z0-9]{0,10}").prop_map(
2841 |(position, delete_len, insert_text)| BulkEditOp {
2842 position,
2843 delete_len,
2844 insert_text,
2845 },
2846 ),
2847 1..20,
2848 )
2849 }
2850
2851 fn preallocate_buffers(
2853 buffers: &mut Vec<StringBuffer>,
2854 texts: &[String],
2855 ) -> Vec<(BufferLocation, usize, usize, Option<usize>)> {
2856 let mut infos = Vec::new();
2857 for text in texts {
2858 let id = buffers.len();
2859 let bytes = text.as_bytes().to_vec();
2860 let lf = bytes.iter().filter(|&&b| b == b'\n').count();
2861 let len = bytes.len();
2862 buffers.push(StringBuffer::new(id, bytes));
2863 infos.push((BufferLocation::Added(id), 0, len, Some(lf)));
2864 }
2865 infos
2866 }
2867
2868 proptest! {
2869 #[test]
2872 fn prop_bulk_edit_tree_consistency(ops in bulk_edit_strategy()) {
2873 let mut buffers = vec![StringBuffer::new(0, b"x".repeat(100).to_vec())];
2874 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2875
2876 let mut ops = ops;
2878 ops.sort_by(|a, b| b.position.cmp(&a.position));
2879
2880 let texts: Vec<String> = ops.iter().map(|op| op.insert_text.clone()).collect();
2882 let infos = preallocate_buffers(&mut buffers, &texts);
2883
2884 let edits: Vec<(usize, usize, &str)> = ops.iter()
2886 .map(|op| {
2887 let pos = op.position.min(tree.total_bytes());
2888 let del = op.delete_len.min(tree.total_bytes().saturating_sub(pos));
2889 (pos, del, op.insert_text.as_str())
2890 })
2891 .collect();
2892
2893 let mut idx = 0;
2894 tree.apply_bulk_edits(&edits, &buffers, |_text| {
2895 let info = infos[idx];
2896 idx += 1;
2897 info
2898 });
2899
2900 let leaves = tree.get_leaves();
2902 let sum_of_pieces: usize = leaves.iter().map(|leaf| leaf.bytes).sum();
2903 prop_assert_eq!(
2904 sum_of_pieces,
2905 tree.total_bytes(),
2906 "After bulk edit: sum of pieces ({}) != total_bytes ({})",
2907 sum_of_pieces,
2908 tree.total_bytes()
2909 );
2910 }
2911
2912 #[test]
2914 fn prop_bulk_edit_correct_delta(ops in bulk_edit_strategy()) {
2915 let mut buffers = vec![StringBuffer::new(0, b"x".repeat(100).to_vec())];
2916 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
2917
2918 let original_bytes = tree.total_bytes();
2919
2920 let mut ops = ops;
2922 ops.sort_by(|a, b| b.position.cmp(&a.position));
2923
2924 let texts: Vec<String> = ops.iter().map(|op| op.insert_text.clone()).collect();
2926 let infos = preallocate_buffers(&mut buffers, &texts);
2927
2928 let edits: Vec<(usize, usize, &str)> = ops.iter()
2929 .map(|op| {
2930 let pos = op.position.min(tree.total_bytes());
2931 let del = op.delete_len.min(tree.total_bytes().saturating_sub(pos));
2932 (pos, del, op.insert_text.as_str())
2933 })
2934 .collect();
2935
2936 let mut idx = 0;
2937 let delta = tree.apply_bulk_edits(&edits, &buffers, |_text| {
2938 let info = infos[idx];
2939 idx += 1;
2940 info
2941 });
2942
2943 let actual_change = tree.total_bytes() as isize - original_bytes as isize;
2944 prop_assert_eq!(
2945 delta,
2946 actual_change,
2947 "Returned delta ({}) != actual change ({})",
2948 delta,
2949 actual_change
2950 );
2951 }
2952
2953 #[test]
2955 fn prop_bulk_edit_inserts_only(
2956 positions in prop::collection::vec(0usize..50, 1..10),
2957 insert_len in 1usize..10
2958 ) {
2959 let mut buffers = vec![StringBuffer::new(0, b"x".repeat(50).to_vec())];
2960 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 50, Some(0));
2961
2962 let insert_text = "a".repeat(insert_len);
2963 let original_bytes = tree.total_bytes();
2964
2965 let mut positions = positions;
2967 positions.sort_by(|a, b| b.cmp(a));
2968 positions.dedup();
2969
2970 let texts: Vec<String> = positions.iter().map(|_| insert_text.clone()).collect();
2972 let infos = preallocate_buffers(&mut buffers, &texts);
2973
2974 let edits: Vec<(usize, usize, &str)> = positions
2975 .iter()
2976 .map(|&pos| (pos.min(tree.total_bytes()), 0, insert_text.as_str()))
2977 .collect();
2978
2979 let mut idx = 0;
2980 tree.apply_bulk_edits(&edits, &buffers, |_text| {
2981 let info = infos[idx];
2982 idx += 1;
2983 info
2984 });
2985
2986 let expected_bytes = original_bytes + edits.len() * insert_len;
2987 prop_assert_eq!(
2988 tree.total_bytes(),
2989 expected_bytes,
2990 "After {} inserts of {} bytes each: expected {} bytes, got {}",
2991 edits.len(),
2992 insert_len,
2993 expected_bytes,
2994 tree.total_bytes()
2995 );
2996 }
2997
2998 #[test]
3000 fn prop_bulk_edit_deletes_only(
3001 ops in prop::collection::vec((0usize..80, 1usize..5), 1..10)
3002 ) {
3003 let buffers = vec![StringBuffer::new(0, b"x".repeat(100).to_vec())];
3004 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3005
3006 let mut ops = ops;
3008 ops.sort_by(|a, b| b.0.cmp(&a.0));
3009
3010 let mut edits: Vec<(usize, usize, &str)> = Vec::new();
3012 let mut last_affected_pos = tree.total_bytes();
3013 for (pos, del_len) in ops {
3014 if pos < last_affected_pos {
3015 let actual_del = del_len.min(last_affected_pos - pos);
3016 if actual_del > 0 {
3017 edits.push((pos, actual_del, ""));
3018 last_affected_pos = pos;
3019 }
3020 }
3021 }
3022
3023 let expected_delete: usize = edits.iter().map(|(_, d, _)| d).sum();
3024
3025 tree.apply_bulk_edits(&edits, &buffers, |_| {
3026 (BufferLocation::Added(1), 0, 0, Some(0))
3027 });
3028
3029 let expected_bytes = 100 - expected_delete;
3030 prop_assert_eq!(
3031 tree.total_bytes(),
3032 expected_bytes,
3033 "After deleting {} bytes: expected {} bytes, got {}",
3034 expected_delete,
3035 expected_bytes,
3036 tree.total_bytes()
3037 );
3038 }
3039 }
3040
3041 proptest! {
3042 #[test]
3045 fn prop_tree_consistency_piece_sum(operations in operation_strategy()) {
3046 let buffers = test_buffers_large();
3047 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3048
3049 for op in operations {
3050 match op {
3051 Operation::Insert { offset, bytes } => {
3052 let offset = offset.min(tree.total_bytes());
3053 let buffer_len = buffers[1].get_data().map(|d| d.len()).unwrap_or(0);
3054 let bytes = bytes.min(buffer_len);
3055 tree.insert(offset, BufferLocation::Added(1), 0, bytes, Some(0), &buffers);
3056 }
3057 Operation::Delete { offset, bytes } => {
3058 tree.delete(offset, bytes, &buffers);
3059 }
3060 }
3061
3062 let leaves = tree.get_leaves();
3064 let sum_of_pieces: usize = leaves.iter().map(|leaf| leaf.bytes).sum();
3065 prop_assert_eq!(
3066 sum_of_pieces,
3067 tree.total_bytes(),
3068 "Tree inconsistency: sum of piece lengths ({}) != total_bytes ({})",
3069 sum_of_pieces,
3070 tree.total_bytes()
3071 );
3072 }
3073 }
3074
3075 #[test]
3078 fn prop_tree_consistency_line_feeds(operations in operation_strategy()) {
3079 let buffers = test_buffers_large();
3080 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3081
3082 for op in operations {
3083 match op {
3084 Operation::Insert { offset, bytes } => {
3085 let offset = offset.min(tree.total_bytes());
3086 let buffer_len = buffers[1].get_data().map(|d| d.len()).unwrap_or(0);
3087 let bytes = bytes.min(buffer_len);
3088 tree.insert(offset, BufferLocation::Added(1), 0, bytes, Some(0), &buffers);
3089 }
3090 Operation::Delete { offset, bytes } => {
3091 tree.delete(offset, bytes, &buffers);
3092 }
3093 }
3094
3095 let leaves = tree.get_leaves();
3097 let sum_of_line_feeds: Option<usize> = leaves.iter()
3098 .try_fold(0, |acc, leaf| leaf.line_feed_cnt.map(|cnt| acc + cnt));
3099 let stats = tree.stats();
3100 prop_assert_eq!(
3101 sum_of_line_feeds,
3102 stats.line_feed_count,
3103 "Line feed inconsistency: sum of piece line feeds ({:?}) != tree total ({:?})",
3104 sum_of_line_feeds,
3105 stats.line_feed_count
3106 );
3107 }
3108 }
3109
3110 #[test]
3113 fn prop_tree_consistency_aggressive(operations in aggressive_operation_strategy()) {
3114 let buffers = test_buffers_large();
3115 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3116
3117 for i in 0..5 {
3120 let offset = (i * 17) % (tree.total_bytes().max(1));
3121 tree.insert(offset, BufferLocation::Added(1), i * 100, 10, Some(0), &buffers);
3122 }
3123
3124 prop_assert!(tree.stats().depth > 1, "Priming should create internal nodes");
3126
3127 for (i, op) in operations.iter().enumerate() {
3128 match *op {
3129 Operation::Insert { offset, bytes } => {
3130 let offset = offset.min(tree.total_bytes());
3131 let buffer_len = buffers[1].get_data().map(|d| d.len()).unwrap_or(0);
3132 let bytes = bytes.min(buffer_len);
3133 tree.insert(offset, BufferLocation::Added(1), 0, bytes, Some(0), &buffers);
3134 }
3135 Operation::Delete { offset, bytes } => {
3136 tree.delete(offset, bytes, &buffers);
3137 }
3138 }
3139
3140 let leaves = tree.get_leaves();
3143 let sum_of_pieces: usize = leaves.iter().map(|leaf| leaf.bytes).sum();
3144 prop_assert_eq!(
3145 sum_of_pieces,
3146 tree.total_bytes(),
3147 "Operation {}: Tree inconsistency after {:?}.\n\
3148 Sum of piece lengths ({}) != total_bytes ({}).\n\
3149 Tree depth: {}, leaves: {}.\n\
3150 Pieces: {:?}",
3151 i, op, sum_of_pieces, tree.total_bytes(),
3152 tree.stats().depth, tree.stats().leaf_count,
3153 leaves
3154 );
3155 }
3156 }
3157 }
3158
3159 #[test]
3160 fn test_delete_beyond_end() {
3161 let buffers = test_buffers_large();
3162 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3163 tree.delete(50, 100, &buffers); assert_eq!(tree.total_bytes(), 50); }
3166
3167 #[test]
3168 fn test_insert_zero_bytes() {
3169 let buffers = test_buffers_large();
3170 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3171 tree.insert(50, BufferLocation::Added(1), 0, 0, Some(0), &buffers);
3172 assert_eq!(tree.total_bytes(), 100);
3173 }
3174
3175 #[test]
3176 fn test_tree_consistency_after_insert() {
3177 let buffers = test_buffers_large();
3180 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3181
3182 for i in 0..10 {
3184 let offset = (i * 13) % (tree.total_bytes().max(1)); tree.insert(
3186 offset,
3187 BufferLocation::Added(1),
3188 i * 10,
3189 5,
3190 Some(0),
3191 &buffers,
3192 );
3193
3194 let leaves = tree.get_leaves();
3196 let sum: usize = leaves.iter().map(|l| l.bytes).sum();
3197 assert_eq!(
3198 sum,
3199 tree.total_bytes(),
3200 "After insert {}: sum of pieces ({}) != total_bytes ({}).\nLeaves: {:?}",
3201 i,
3202 sum,
3203 tree.total_bytes(),
3204 leaves
3205 );
3206 }
3207
3208 let stats = tree.stats();
3210 assert!(
3211 stats.depth > 1,
3212 "Test should create internal nodes, but depth is {}",
3213 stats.depth
3214 );
3215 }
3216
3217 #[test]
3218 fn test_duplicate_piece_bug_exact_scenario() {
3219 let mut buffers = vec![StringBuffer::new(0, b"initial\ntext".to_vec())];
3221 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 12, Some(1));
3222
3223 tree.delete(0, 12, &buffers);
3225
3226 let leaves = tree.get_leaves();
3228 let sum: usize = leaves.iter().map(|l| l.bytes).sum();
3229 assert_eq!(
3230 sum,
3231 tree.total_bytes(),
3232 "After delete: sum={}, total={}",
3233 sum,
3234 tree.total_bytes()
3235 );
3236
3237 buffers.push(StringBuffer::new(1, b"a".to_vec()));
3239 tree.insert(0, BufferLocation::Added(1), 0, 1, Some(0), &buffers);
3240
3241 let leaves = tree.get_leaves();
3243 let sum: usize = leaves.iter().map(|l| l.bytes).sum();
3244 assert_eq!(
3245 sum,
3246 tree.total_bytes(),
3247 "After first insert: sum={}, total={}. Leaves: {:?}",
3248 sum,
3249 tree.total_bytes(),
3250 leaves
3251 );
3252
3253 buffers.push(StringBuffer::new(2, b"b".to_vec()));
3255 tree.insert(0, BufferLocation::Added(2), 0, 1, Some(0), &buffers);
3256
3257 let leaves = tree.get_leaves();
3259 let sum: usize = leaves.iter().map(|l| l.bytes).sum();
3260 assert_eq!(
3261 sum,
3262 tree.total_bytes(),
3263 "After second insert: sum={}, total={}. Leaves: {:?}",
3264 sum,
3265 tree.total_bytes(),
3266 leaves
3267 );
3268 }
3269
3270 proptest! {
3272 #[test]
3273 fn test_piece_iter_covers_exact_range(
3274 ops in aggressive_operation_strategy(),
3275 start in 0usize..100,
3276 len in 1usize..50
3277 ) {
3278 let mut buffers = vec![StringBuffer::new(0, b"x".repeat(100).to_vec())];
3279 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3280
3281 for op in ops.iter() {
3283 match op {
3284 Operation::Insert { offset, bytes } => {
3285 let offset = (*offset).min(tree.total_bytes());
3286 buffers.push(StringBuffer::new(buffers.len(), b"a".repeat(*bytes).to_vec()));
3287 tree.insert(offset, BufferLocation::Added(buffers.len() - 1), 0, *bytes, Some(0), &buffers);
3288 }
3289 Operation::Delete { offset, bytes } => {
3290 let offset = (*offset).min(tree.total_bytes());
3291 let bytes = (*bytes).min(tree.total_bytes().saturating_sub(offset));
3292 if bytes > 0 {
3293 tree.delete(offset, bytes, &buffers);
3294 }
3295 }
3296 }
3297 }
3298
3299 let total_bytes = tree.total_bytes();
3300 if total_bytes == 0 {
3301 return Ok(());
3302 }
3303
3304 let start = start.min(total_bytes.saturating_sub(1));
3305 let end = (start + len).min(total_bytes);
3306
3307 let pieces: Vec<_> = tree.iter_pieces_in_range(start, end).collect();
3309
3310 if !pieces.is_empty() {
3312 let first_piece_start = pieces[0].doc_offset;
3313 let last_piece = &pieces[pieces.len() - 1];
3314 let last_piece_end = last_piece.doc_offset + last_piece.bytes;
3315
3316 prop_assert!(first_piece_start <= start,
3318 "First piece starts at {}, but requested start is {}", first_piece_start, start);
3319
3320 prop_assert!(last_piece_end >= end,
3322 "Last piece ends at {}, but requested end is {}", last_piece_end, end);
3323 }
3324 }
3325
3326 #[test]
3327 fn test_piece_iter_no_gaps(ops in aggressive_operation_strategy()) {
3328 let mut buffers = vec![StringBuffer::new(0, b"x".repeat(100).to_vec())];
3329 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3330
3331 for op in ops {
3332 match op {
3333 Operation::Insert { offset, bytes } => {
3334 let offset = offset.min(tree.total_bytes());
3335 buffers.push(StringBuffer::new(buffers.len(), b"a".repeat(bytes).to_vec()));
3336 tree.insert(offset, BufferLocation::Added(buffers.len() - 1), 0, bytes, Some(0), &buffers);
3337 }
3338 Operation::Delete { offset, bytes } => {
3339 let offset = offset.min(tree.total_bytes());
3340 let bytes = bytes.min(tree.total_bytes().saturating_sub(offset));
3341 if bytes > 0 {
3342 tree.delete(offset, bytes, &buffers);
3343 }
3344 }
3345 }
3346 }
3347
3348 let total_bytes = tree.total_bytes();
3349 if total_bytes == 0 {
3350 return Ok(());
3351 }
3352
3353 let pieces: Vec<_> = tree.iter_pieces_in_range(0, total_bytes).collect();
3355
3356 for i in 1..pieces.len() {
3358 let prev_end = pieces[i - 1].doc_offset + pieces[i - 1].bytes;
3359 let curr_start = pieces[i].doc_offset;
3360 prop_assert_eq!(prev_end, curr_start,
3361 "Gap between piece {} (ends at {}) and piece {} (starts at {})",
3362 i - 1, prev_end, i, curr_start);
3363 }
3364 }
3365
3366 #[test]
3367 fn test_piece_iter_total_bytes_matches(ops in aggressive_operation_strategy()) {
3368 let mut buffers = vec![StringBuffer::new(0, b"x".repeat(100).to_vec())];
3369 let mut tree = PieceTree::new(BufferLocation::Stored(0), 0, 100, Some(0));
3370
3371 for op in ops {
3372 match op {
3373 Operation::Insert { offset, bytes } => {
3374 let offset = offset.min(tree.total_bytes());
3375 buffers.push(StringBuffer::new(buffers.len(), b"a".repeat(bytes).to_vec()));
3376 tree.insert(offset, BufferLocation::Added(buffers.len() - 1), 0, bytes, Some(0), &buffers);
3377 }
3378 Operation::Delete { offset, bytes } => {
3379 let offset = offset.min(tree.total_bytes());
3380 let bytes = bytes.min(tree.total_bytes().saturating_sub(offset));
3381 if bytes > 0 {
3382 tree.delete(offset, bytes, &buffers);
3383 }
3384 }
3385 }
3386 }
3387
3388 let total_bytes = tree.total_bytes();
3389 if total_bytes == 0 {
3390 return Ok(());
3391 }
3392
3393 let pieces: Vec<_> = tree.iter_pieces_in_range(0, total_bytes).collect();
3395 let sum_bytes: usize = pieces.iter().map(|p| p.bytes).sum();
3396 prop_assert_eq!(sum_bytes, total_bytes,
3397 "Sum of piece bytes ({}) doesn't match total_bytes ({})", sum_bytes, total_bytes);
3398 }
3399
3400 #[test]
3404 fn prop_offset_to_position_correct_after_modifications(
3405 ops in prop::collection::vec(
3406 prop_oneof![
3407 (0usize..50, prop::collection::vec(
3409 prop_oneof![
3410 Just(b'a'),
3411 Just(b'\n'),
3412 ],
3413 1..20
3414 )).prop_map(|(offset, bytes)| (offset, bytes, true)),
3415 (0usize..50, 1usize..10).prop_map(|(offset, _bytes)| (offset, vec![], false)),
3417 ],
3418 5..20
3419 ),
3420 test_offsets in prop::collection::vec(0usize..100, 3..10)
3421 ) {
3422 let initial = b"Hello\nWorld\nTest\n";
3424 let mut content = initial.to_vec();
3425
3426 let mut buffers = vec![StringBuffer::new(0, initial.to_vec())];
3427 let newline_count = initial.iter().filter(|&&b| b == b'\n').count();
3428 let mut tree = PieceTree::new(
3429 BufferLocation::Stored(0),
3430 0,
3431 initial.len(),
3432 Some(newline_count),
3433 );
3434
3435 for (offset, bytes, is_insert) in ops {
3437 if is_insert && !bytes.is_empty() {
3438 let offset = offset.min(content.len());
3439 let newlines = bytes.iter().filter(|&&b| b == b'\n').count();
3440
3441 buffers.push(StringBuffer::new(buffers.len(), bytes.clone()));
3443 tree.insert(
3444 offset,
3445 BufferLocation::Added(buffers.len() - 1),
3446 0,
3447 bytes.len(),
3448 Some(newlines),
3449 &buffers,
3450 );
3451
3452 content.splice(offset..offset, bytes);
3454 } else if !is_insert {
3455 let offset = offset.min(content.len());
3457 let delete_len = 5.min(content.len().saturating_sub(offset)); if delete_len > 0 {
3459 tree.delete(offset, delete_len, &buffers);
3460 content.drain(offset..offset + delete_len);
3461 }
3462 }
3463 }
3464
3465 let compute_position = |content: &[u8], offset: usize| -> (usize, usize) {
3467 let offset = offset.min(content.len());
3468 let mut line = 0;
3469 let mut col = 0;
3470 for (i, &byte) in content.iter().enumerate() {
3471 if i == offset {
3472 break;
3473 }
3474 if byte == b'\n' {
3475 line += 1;
3476 col = 0;
3477 } else {
3478 col += 1;
3479 }
3480 }
3481 (line, col)
3482 };
3483
3484 for offset in test_offsets {
3486 let offset = offset.min(content.len());
3487 if offset == 0 {
3488 continue; }
3490
3491 let expected = compute_position(&content, offset);
3492 let actual = tree.offset_to_position(offset, &buffers);
3493
3494 prop_assert_eq!(
3495 actual,
3496 Some(expected),
3497 "offset_to_position({}) returned {:?}, expected {:?}. Content len: {}",
3498 offset,
3499 actual,
3500 expected,
3501 content.len()
3502 );
3503 }
3504 }
3505 }
3506}