1use std::{
20 borrow::Cow,
21 cell::UnsafeCell,
22 cmp::{Ordering, max, min},
23 collections::HashMap,
24 fmt,
25};
26
27const MIN_GAP: usize = 32;
30const MIN_GAP_GROW: usize = 64;
31const MAX_GAP_GROW: usize = 1024 * 8;
32
33#[inline]
36fn clamp_gap_size(len: usize, min_gap: usize) -> usize {
37 min(max(len / 20, min_gap), MAX_GAP_GROW)
38}
39
40#[inline]
41fn count_chars(bytes: &[u8]) -> usize {
42 if bytes.is_empty() {
43 return 0;
44 }
45
46 let mut n_chars = 0;
47 let mut cur = 0;
48
49 while cur < bytes.len() {
50 let ch = unsafe { decode_char_at(cur, bytes) };
52 cur += ch.len_utf8();
53 n_chars += 1;
54 }
55
56 n_chars
57}
58
59type ByteOffset = usize;
60type CharOffset = usize;
61
62#[derive(Debug)]
66struct CharToByteCache(UnsafeCell<HashMap<CharOffset, ByteOffset>>);
67
68impl CharToByteCache {
69 fn clear(&self) {
70 unsafe { (&mut *self.0.get()).clear() }
72 }
73
74 fn get(&self, char_idx: CharOffset) -> Option<ByteOffset> {
75 unsafe { (&*self.0.get()).get(&char_idx).copied() }
77 }
78
79 fn insert(&self, char_idx: CharOffset, byte_idx: ByteOffset) {
80 unsafe {
82 (&mut *self.0.get()).insert(char_idx, byte_idx);
83 }
84 }
85}
86
87impl Clone for CharToByteCache {
88 fn clone(&self) -> Self {
89 unsafe { Self(UnsafeCell::new((&*self.0.get()).clone())) }
91 }
92}
93impl PartialEq for CharToByteCache {
94 fn eq(&self, _: &Self) -> bool {
95 true }
97}
98impl Eq for CharToByteCache {}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct GapBuffer {
104 data: Box<[u8]>,
106 cap: usize,
108 gap_start: usize,
110 gap_end: usize,
112 next_gap: usize,
114 n_chars: usize,
117 line_endings: Vec<(ByteOffset, CharOffset)>,
119 char_to_byte_cache: CharToByteCache,
121}
122
123impl Default for GapBuffer {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129fn compute_line_endings(s: &str) -> (usize, Vec<(ByteOffset, CharOffset)>) {
130 let mut n_chars = 0;
131 let mut line_endings = Vec::new();
132
133 for (line_chars, (idx, ch)) in s.char_indices().enumerate() {
134 n_chars += 1;
135 if ch == '\n' {
136 line_endings.push((idx, line_chars));
137 }
138 }
139
140 (n_chars, line_endings)
141}
142
143impl From<String> for GapBuffer {
144 fn from(s: String) -> Self {
145 let gap_start = s.len();
146 let next_gap = clamp_gap_size(gap_start, MIN_GAP_GROW);
147 let cap = gap_start + next_gap;
148 let (n_chars, line_endings) = compute_line_endings(&s);
149 let mut v = s.into_bytes();
150 v.resize(cap, 0);
151
152 let mut gb = Self {
153 data: v.into_boxed_slice(),
154 cap,
155 gap_start,
156 gap_end: cap,
157 next_gap,
158 n_chars,
159 line_endings,
160 char_to_byte_cache: CharToByteCache(UnsafeCell::new(HashMap::new())),
161 };
162
163 gb.move_gap_to(0);
164 gb
165 }
166}
167
168impl From<&str> for GapBuffer {
169 fn from(s: &str) -> Self {
170 let gap_start = s.len();
171 let next_gap = clamp_gap_size(gap_start, MIN_GAP_GROW);
172 let cap = gap_start + next_gap;
173 let (n_chars, line_endings) = compute_line_endings(s);
174 let mut v = Vec::with_capacity(cap);
175 v.extend_from_slice(s.as_bytes());
176 v.resize(cap, 0);
177
178 let mut gb = Self {
179 data: v.into_boxed_slice(),
180 cap,
181 gap_start,
182 gap_end: cap,
183 next_gap,
184 n_chars,
185 line_endings,
186 char_to_byte_cache: CharToByteCache(UnsafeCell::new(HashMap::new())),
187 };
188
189 gb.move_gap_to(0);
190 gb
191 }
192}
193
194impl fmt::Display for GapBuffer {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 let (s1, s2) = self.as_strs();
197 write!(f, "{s1}{s2}")
198 }
199}
200
201impl<'a> PartialEq<&'a str> for GapBuffer {
202 fn eq(&self, other: &&'a str) -> bool {
203 let b = other.as_bytes();
204 if b.len() != self.len() {
205 return false;
206 }
207
208 b[..self.gap_start] == self.data[..self.gap_start]
209 && b[self.gap_start..] == self.data[self.gap_end..]
210 }
211}
212
213impl PartialEq<String> for GapBuffer {
214 fn eq(&self, other: &String) -> bool {
215 *self == other.as_str()
216 }
217}
218
219macro_rules! assert_line_endings {
226 ($self:expr) => {{
227 let true_endings: Vec<usize> = $self
228 .data
229 .iter()
230 .enumerate()
231 .filter(|&(i, &b)| b == b'\n' && (i < $self.gap_start || i >= $self.gap_end))
232 .map(|(i, _)| i)
233 .collect();
234 let tracked_line_endings: Vec<usize> = $self.line_endings.iter().map(|(n, _)| *n).collect();
235
236 assert_eq!(
237 tracked_line_endings, true_endings,
238 "incorrect byte positions for line endings with gap_start={} gap_end={}",
239 $self.gap_start, $self.gap_end
240 );
241
242 let true_endings: Vec<usize> = $self
243 .to_string()
244 .chars()
245 .enumerate()
246 .filter(|&(_, c)| c == '\n')
247 .map(|(i, _)| i)
248 .collect();
249 let tracked_line_endings: Vec<usize> = $self.line_endings.iter().map(|(_, n)| *n).collect();
250
251 assert_eq!(
252 tracked_line_endings, true_endings,
253 "incorrect character positions for line endings with gap_start={} gap_end={}",
254 $self.gap_start, $self.gap_end
255 );
256 }};
257}
258
259impl GapBuffer {
260 pub fn new() -> Self {
262 Self::from("")
263 }
264
265 #[inline]
267 fn gap(&self) -> usize {
268 self.gap_end - self.gap_start
269 }
270
271 #[inline]
273 pub fn len(&self) -> usize {
274 self.cap - self.gap()
275 }
276
277 #[inline]
280 pub fn is_empty(&self) -> bool {
281 self.cap == self.gap()
282 }
283
284 pub fn make_contiguous(&mut self) -> &[u8] {
287 self.move_gap_to(0);
288 &self.data[self.gap_end..]
289 }
290
291 pub fn is_contiguous(&self) -> bool {
293 self.gap_start == 0 || self.gap_end == self.cap
294 }
295
296 pub fn as_str(&mut self) -> &str {
301 let raw = self.make_contiguous();
302
303 unsafe { std::str::from_utf8_unchecked(raw) }
306 }
307
308 pub unsafe fn substr_from(&self, byte_offset: usize) -> &str {
313 unsafe { std::str::from_utf8_unchecked(&self.data[self.gap_end + byte_offset..]) }
315 }
316
317 pub unsafe fn assume_contiguous_bytes(&self) -> &[u8] {
323 &self.data[self.gap_end..]
324 }
325
326 pub unsafe fn assume_contiguous_str(&self) -> &str {
331 unsafe { std::str::from_utf8_unchecked(&self.data[self.gap_end..]) }
333 }
334
335 pub fn as_strs(&self) -> (&str, &str) {
340 let (left, right) = self.as_byte_slices();
341
342 unsafe {
345 (
346 std::str::from_utf8_unchecked(left),
347 std::str::from_utf8_unchecked(right),
348 )
349 }
350 }
351
352 pub fn as_byte_slices(&self) -> (&[u8], &[u8]) {
357 (&self.data[0..self.gap_start], &self.data[self.gap_end..])
358 }
359
360 pub fn has_trailing_newline(&self) -> bool {
367 let (l, r) = self.as_byte_slices();
368
369 if r.is_empty() {
370 l.ends_with(b"\n")
371 } else {
372 r.ends_with(b"\n")
373 }
374 }
375
376 pub fn chars(&self) -> Chars<'_> {
378 self.as_slice().chars()
379 }
380
381 pub fn iter_lines(&self) -> impl Iterator<Item = Slice<'_>> {
383 let mut line_idx = 0;
384
385 std::iter::from_fn(move || {
386 if line_idx == self.len_lines() {
387 return None;
388 }
389 let slice = self.line(line_idx);
390 line_idx += 1;
391
392 Some(slice)
393 })
394 }
395
396 #[inline]
398 pub fn len_lines(&self) -> usize {
399 match self.line_endings.last() {
400 Some(&(raw_idx, _)) => {
401 let n = self.line_endings.len();
402 let byte_idx = if raw_idx > self.gap_start {
403 raw_idx - self.gap_end
404 } else {
405 raw_idx
406 };
407
408 if byte_idx < self.len() { n + 1 } else { n }
409 }
410
411 None => 1,
412 }
413 }
414
415 #[inline]
417 pub fn len_chars(&self) -> usize {
418 self.n_chars
419 }
420
421 #[inline]
426 pub fn line_end_byte(&self, line_idx: usize) -> usize {
427 if line_idx < self.line_endings.len() {
428 self.raw_byte_to_byte(self.line_endings[line_idx].0)
429 } else {
430 self.len()
431 }
432 }
433
434 pub fn lines_before_byte_offset(&self, byte_offset: usize) -> usize {
435 let raw_offset = self.byte_to_raw_byte(byte_offset);
436 self.line_endings.partition_point(|(b, _)| *b < raw_offset)
437 }
438
439 pub fn clear(&mut self) {
446 self.move_gap_to(0);
447 self.gap_end = self.cap;
448 self.line_endings.clear();
449 self.n_chars = 0;
450
451 #[cfg(test)]
452 assert_line_endings!(self);
453 }
454
455 #[inline]
460 pub fn char(&self, char_idx: usize) -> char {
461 let byte_idx = self.char_to_raw_byte(char_idx);
462
463 unsafe { decode_char_at(byte_idx, &self.data) }
465 }
466
467 #[inline]
469 pub fn get_char(&self, char_idx: usize) -> Option<char> {
470 if char_idx < self.n_chars {
471 Some(self.char(char_idx))
472 } else {
473 None
474 }
475 }
476
477 #[inline]
482 pub fn char_at(&self, byte_idx: usize) -> char {
483 let byte_idx = self.byte_to_raw_byte(byte_idx);
484
485 unsafe { decode_char_at(byte_idx, &self.data) }
487 }
488
489 #[inline]
491 pub fn get_char_at(&self, byte_idx: usize) -> Option<char> {
492 if byte_idx < self.len() {
493 Some(self.char_at(byte_idx))
494 } else {
495 None
496 }
497 }
498
499 #[inline]
500 fn char_len(&self, byte_idx: usize) -> usize {
501 unsafe { decode_char_at(byte_idx, &self.data) }.len_utf8()
503 }
504
505 #[inline]
510 pub fn line(&self, line_idx: usize) -> Slice<'_> {
511 if line_idx >= self.len_lines() {
512 assert_line_endings!(self);
513 panic!(
514 "line index was {line_idx} but buffer has {} lines",
515 self.len_lines()
516 )
517 }
518
519 let to = if line_idx < self.line_endings.len() {
520 let idx = self.line_endings[line_idx].0;
521 idx + self.char_len(idx)
522 } else {
523 self.cap
524 };
525
526 let from = if line_idx == 0 {
527 0
528 } else {
529 let idx = self.line_endings[line_idx - 1].0;
530 idx + 1
531 };
532
533 if from == to {
534 Slice::NULL
535 } else {
536 Slice::from_raw_offsets(from, to, self)
537 }
538 }
539
540 pub fn line_is_blank(&self, line_idx: usize) -> bool {
544 matches!(
545 self.line(line_idx).as_strs(),
546 ("", "") | ("", "\n") | ("\n", "")
547 )
548 }
549
550 #[inline]
555 pub fn line_len_chars(&self, line_idx: usize) -> usize {
556 if line_idx >= self.len_lines() {
557 assert_line_endings!(self);
558 panic!(
559 "line index was {line_idx} but buffer has {} lines",
560 self.len_lines()
561 )
562 }
563
564 let chars_to = if self.line_endings.is_empty() {
565 return self.n_chars;
566 } else if line_idx < self.line_endings.len() {
567 self.line_endings[line_idx].1 + 1
568 } else {
569 self.n_chars
570 };
571
572 let chars_from = if line_idx == 0 {
573 0
574 } else {
575 self.line_endings[line_idx - 1].1 + 1
576 };
577
578 chars_to - chars_from
579 }
580
581 pub fn maximal_slice_from_offset(&self, byte_offset: usize) -> &[u8] {
585 if byte_offset > self.len() {
586 &[]
587 } else {
588 let i = self.byte_to_raw_byte(byte_offset);
589 match i.cmp(&self.gap_start) {
590 Ordering::Less => &self.data[i..self.gap_start],
591 Ordering::Equal => &self.data[self.gap_end..],
592 Ordering::Greater => &self.data[i..],
593 }
594 }
595 }
596
597 pub fn slice_from_byte_offsets(&self, byte_from: usize, byte_to: usize) -> Slice<'_> {
599 if byte_from == byte_to {
600 return Slice::NULL;
601 }
602
603 let from = self.byte_to_raw_byte(byte_from);
604 let to = self.byte_to_raw_byte(byte_to);
605
606 Slice::from_raw_offsets(from, to, self)
607 }
608
609 pub fn slice(&self, char_from: usize, char_to: usize) -> Slice<'_> {
611 if char_from == char_to {
612 return Slice::NULL;
613 }
614
615 let byte_from = self.char_to_raw_byte(char_from);
616 let byte_to = self.offset_char_to_raw_byte(char_to, byte_from, char_from);
617
618 Slice::from_raw_offsets(byte_from, byte_to, self)
619 }
620
621 pub fn as_slice(&self) -> Slice<'_> {
622 self.slice(0, self.len_chars())
623 }
624
625 pub fn chars_in_raw_range(&self, raw_from: usize, raw_to: usize) -> usize {
626 if raw_to <= self.gap_start || raw_from >= self.gap_end {
627 count_chars(&self.data[raw_from..raw_to])
628 } else {
629 count_chars(&self.data[raw_from..self.gap_start])
630 + count_chars(&self.data[self.gap_end..raw_to])
631 }
632 }
633
634 pub fn char_to_line(&self, char_idx: usize) -> usize {
639 match self.try_char_to_line(char_idx) {
640 Some(line_idx) => line_idx,
641 None => {
642 assert_line_endings!(self);
643 panic!(
644 "char index was {char_idx} but the buffer char length is {}",
645 self.len_chars()
646 );
647 }
648 }
649 }
650
651 pub fn try_char_to_line(&self, char_idx: usize) -> Option<usize> {
653 match char_idx.cmp(&self.n_chars) {
654 Ordering::Less => {
655 if char_idx < self.n_chars {
656 Some(self.line_endings.partition_point(|(_, i)| *i < char_idx))
657 } else {
658 Some(self.len_lines() - 1)
659 }
660 }
661
662 Ordering::Equal => Some(self.len_lines() - 1),
664
665 Ordering::Greater => None,
666 }
667 }
668
669 pub fn line_to_char(&self, line_idx: usize) -> usize {
674 match self.try_line_to_char(line_idx) {
675 Some(char_idx) => char_idx,
676 None => {
677 assert_line_endings!(self);
678 panic!(
679 "line index was {line_idx} but the buffer has {} lines",
680 self.len_lines()
681 );
682 }
683 }
684 }
685
686 pub fn try_line_to_char(&self, line_idx: usize) -> Option<usize> {
688 if line_idx > self.len_lines() - 1 {
689 return None;
690 }
691
692 if line_idx == 0 {
693 Some(0)
694 } else {
695 let k = self.line_endings[line_idx - 1].1;
696 Some(k + 1)
697 }
698 }
699
700 pub fn line_to_byte(&self, line_idx: usize) -> usize {
705 let raw = if line_idx == 0 {
706 0
707 } else {
708 self.line_endings[line_idx - 1].0 + 1
709 };
710
711 self.raw_byte_to_byte(raw)
712 }
713
714 pub fn insert_char(&mut self, char_idx: usize, ch: char) {
719 let len = ch.len_utf8();
720 if self.gap().saturating_sub(len) < MIN_GAP {
721 self.grow_gap(len);
722 }
723
724 let idx = self.char_to_byte(char_idx);
725 self.move_gap_to(idx);
726
727 ch.encode_utf8(&mut self.data[self.gap_start..]);
728 self.gap_start += len;
729 self.n_chars += 1;
730
731 let endings: &[(ByteOffset, CharOffset)] =
732 if ch == '\n' { &[(idx, char_idx)] } else { &[] };
733
734 self.add_line_endings(idx, 1, endings);
735 self.char_to_byte_cache.clear();
736
737 #[cfg(test)]
738 assert_line_endings!(self);
739 }
740
741 pub fn insert_str(&mut self, char_idx: usize, s: &str) {
746 let len = s.len();
747 let len_chars = s.chars().count();
748 if self.gap().saturating_sub(len) < MIN_GAP {
749 self.grow_gap(len);
750 }
751
752 let idx = self.char_to_byte(char_idx);
753 self.move_gap_to(idx);
754
755 self.data[self.gap_start..self.gap_start + len].copy_from_slice(s.as_bytes());
756 self.gap_start += len;
757 self.n_chars += len_chars;
758
759 let endings: Vec<_> = s
760 .char_indices()
761 .enumerate()
762 .filter(|(_, (_, ch))| *ch == '\n')
763 .map(|(i, (offset, _))| (idx + offset, char_idx + i))
764 .collect();
765
766 self.add_line_endings(idx, len_chars, &endings);
767 self.char_to_byte_cache.clear();
768
769 #[cfg(test)]
770 assert_line_endings!(self);
771 }
772
773 pub fn remove_char(&mut self, char_idx: usize) {
775 let raw_idx = self.char_to_raw_byte(char_idx);
776 let idx = self.raw_byte_to_byte(raw_idx);
777 let len = self.char_len(raw_idx);
778
779 if idx != self.gap_start {
780 self.move_gap_to(idx);
781 } else {
782 self.char_to_byte_cache.clear();
783 }
784
785 self.gap_end += len;
786 self.n_chars -= 1;
787
788 self.remove_line_endings(idx, idx + len, 1);
789 self.char_to_byte_cache.clear();
790
791 #[cfg(test)]
792 assert_line_endings!(self);
793 }
794
795 pub fn remove_range(&mut self, char_from: usize, char_to: usize) {
800 if char_from == char_to {
801 return;
802 }
803
804 assert!(
805 char_from < char_to,
806 "invalid range: from={char_from} > to={char_to}"
807 );
808
809 let raw_from = self.char_to_raw_byte(char_from);
810 let from = self.raw_byte_to_byte(raw_from);
811 let to = self.offset_char_to_byte(char_to, raw_from, char_from);
812 debug_assert!(from < to, "invalid byte range: from={from} > to={to}");
813 self.move_gap_to(from);
814
815 let n_bytes = to - from;
816 let n_chars = char_to - char_from;
817
818 self.remove_line_endings(from, self.byte_to_raw_byte(to), n_chars);
819
820 self.gap_end += n_bytes;
821 self.n_chars -= n_chars;
822 self.char_to_byte_cache.clear();
823
824 #[cfg(test)]
825 assert_line_endings!(self);
826 }
827
828 fn add_line_endings(
829 &mut self,
830 byte_from: usize,
831 n_chars: usize,
832 new_endings: &[(ByteOffset, CharOffset)],
833 ) {
834 let idx = self.line_endings.partition_point(|(b, _)| *b < byte_from);
835 for (_, c) in &mut self.line_endings[idx..] {
836 *c += n_chars;
837 }
838
839 self.line_endings
840 .splice(idx..idx, new_endings.iter().copied());
841 }
842
843 fn remove_line_endings(&mut self, byte_from: usize, byte_to: usize, n_chars: usize) {
844 self.line_endings
845 .retain(|(b, _)| *b < self.gap_start || *b >= self.gap_end);
846
847 if self.line_endings.is_empty()
852 || self
853 .line_endings
854 .last()
855 .map(|(b, _)| *b < byte_from)
856 .unwrap_or(false)
857 {
858 return;
859 }
860
861 let line_from = self.line_endings.partition_point(|(b, _)| *b < byte_from);
862 let line_to = self.line_endings.partition_point(|(b, _)| *b < byte_to);
863
864 if line_from == line_to {
865 let (b, _) = self.line_endings[line_from];
866 if b >= byte_from && b < byte_to {
867 self.line_endings.remove(line_from);
868 }
869 } else {
870 self.line_endings.drain(line_from..line_to);
871 }
872
873 for (_, c) in &mut self.line_endings[line_from..] {
874 *c -= n_chars;
875 }
876 }
877
878 fn grow_gap(&mut self, n: usize) {
879 if n >= self.next_gap {
880 self.next_gap = clamp_gap_size(self.len() + n, n.next_power_of_two());
881 }
882
883 let gap_increase = self.next_gap + n;
884 let cap = self.cap + self.next_gap + n;
885 let mut buf = Vec::with_capacity(cap);
886
887 buf.extend_from_slice(&self.data[..self.gap_start]); buf.resize(buf.len() + self.next_gap + self.gap() + n, 0); buf.extend_from_slice(&self.data[self.gap_end..]); let start = self.gap_start;
892 let from = self.line_endings.partition_point(|(b, _)| *b <= start);
893 for (b, _) in &mut self.line_endings[from..] {
894 *b += gap_increase;
895 }
896
897 self.next_gap = clamp_gap_size(self.len(), self.next_gap * 2);
898 self.data = buf.into_boxed_slice();
899 self.gap_end += gap_increase;
900 self.cap = cap;
901 self.char_to_byte_cache.clear();
902
903 #[cfg(test)]
904 assert_line_endings!(self);
905 }
906
907 fn move_gap_to(&mut self, byte_idx: usize) {
917 assert!(
919 byte_idx <= self.len(),
920 "index out of bounds: {byte_idx} > {}",
921 self.len()
922 );
923
924 let gap = self.gap();
925
926 let (src, dest) = match byte_idx.cmp(&self.gap_start) {
927 Ordering::Equal => return,
928
929 Ordering::Less => {
931 let start = self.gap_start;
932 let from = self.line_endings.partition_point(|(b, _)| *b < byte_idx);
933 let to = self.line_endings.partition_point(|(b, _)| *b <= start);
934 for (b, _) in &mut self.line_endings[from..to] {
935 *b += gap;
936 }
937
938 (byte_idx..self.gap_start, byte_idx + gap)
939 }
940
941 Ordering::Greater => {
943 let end = self.gap_end;
944 let from = self.line_endings.partition_point(|(b, _)| *b < end);
945 let to = self
946 .line_endings
947 .partition_point(|(b, _)| *b < byte_idx + gap);
948 for (b, _) in &mut self.line_endings[from..to] {
949 *b -= gap;
950 }
951
952 (self.gap_end..byte_idx + gap, self.gap_start)
953 }
954 };
955
956 self.data.copy_within(src, dest);
957 self.gap_end = byte_idx + gap;
958 self.gap_start = byte_idx;
959 self.char_to_byte_cache.clear();
960
961 #[cfg(test)]
962 assert_line_endings!(self);
963 }
964
965 pub fn byte_to_char(&self, byte_idx: usize) -> usize {
975 let mut to = usize::MAX;
976 let raw_byte_idx = self.byte_to_raw_byte(byte_idx);
977 let (mut raw_byte_offset, mut char_offset) = (0, 0);
978
979 for &(b, c) in self.line_endings.iter() {
982 match b.cmp(&raw_byte_idx) {
983 Ordering::Less => (raw_byte_offset, char_offset) = (b, c),
984 Ordering::Equal => {
985 return c;
986 }
987 Ordering::Greater => {
988 to = b;
989 break;
990 }
991 }
992 }
993
994 let slice = Slice::from_raw_offsets(raw_byte_offset, to, self);
995 let mut byte_cur = self.raw_byte_to_byte(raw_byte_offset);
996 let mut cur = char_offset;
997
998 for ch in slice.chars() {
999 if byte_cur == byte_idx {
1000 break;
1001 }
1002 byte_cur += ch.len_utf8();
1003 cur += 1;
1004 }
1005
1006 cur
1007 }
1008
1009 #[inline]
1014 pub fn char_to_byte(&self, char_idx: usize) -> usize {
1015 self.offset_char_to_byte(char_idx, 0, 0)
1016 }
1017
1018 pub fn char_range_to_byte_range(&self, ch_from: usize, ch_to: usize) -> (usize, usize) {
1020 let raw_byte_from = self.char_to_raw_byte(ch_from);
1021 let byte_from = self.raw_byte_to_byte(raw_byte_from);
1022 let byte_to = self.offset_char_to_byte(ch_to, raw_byte_from, ch_from);
1023
1024 (byte_from, byte_to)
1025 }
1026
1027 #[inline]
1028 fn raw_byte_to_byte(&self, raw: usize) -> usize {
1029 if raw > self.gap_start {
1030 raw - self.gap()
1031 } else {
1032 raw
1033 }
1034 }
1035
1036 #[inline]
1037 pub fn byte_to_raw_byte(&self, byte: usize) -> usize {
1038 if byte > self.gap_start {
1039 byte + self.gap()
1040 } else {
1041 byte
1042 }
1043 }
1044
1045 #[inline]
1046 pub(crate) fn offset_char_to_byte(
1047 &self,
1048 char_idx: usize,
1049 byte_offset: usize,
1050 char_offset: usize,
1051 ) -> usize {
1052 let raw = self.offset_char_to_raw_byte(char_idx, byte_offset, char_offset);
1053
1054 self.raw_byte_to_byte(raw)
1055 }
1056
1057 #[inline]
1061 fn char_to_raw_byte(&self, char_idx: usize) -> usize {
1062 self.offset_char_to_raw_byte(char_idx, 0, 0)
1063 }
1064
1065 fn offset_char_to_raw_byte(
1068 &self,
1069 char_idx: usize,
1070 mut byte_offset: usize,
1071 mut char_offset: usize,
1072 ) -> usize {
1073 if let Some(i) = self.char_to_byte_cache.get(char_idx) {
1074 return i;
1075 }
1076
1077 if self.n_chars == 0 {
1079 assert!(
1080 char_idx == 0 || char_idx == 1,
1081 "char index should be 0 or 1 for an empty buffer"
1082 );
1083 return self.gap_start;
1084 }
1085
1086 if self.line_endings.is_empty() && char_idx == self.len_chars().saturating_sub(1) {
1090 if self.gap_end == self.cap && self.gap_start > 0 {
1091 let ch_end = self.gap_start - 1;
1092 let ch = unsafe { decode_char_ending_at(ch_end, &self.data) };
1094
1095 let i = self.gap_start.saturating_sub(ch.len_utf8());
1096 self.char_to_byte_cache.insert(char_idx, i);
1097 return i;
1098 } else {
1099 let ch = unsafe { decode_char_ending_at(self.cap - 1, &self.data) };
1102
1103 let i = self.cap.saturating_sub(ch.len_utf8());
1104 self.char_to_byte_cache.insert(char_idx, i);
1105 return i;
1106 };
1107 }
1108
1109 let mut to = usize::MAX;
1110
1111 let line_from = self.line_endings.partition_point(|(b, _)| *b < byte_offset);
1114 let line_endings = &self.line_endings[line_from..];
1115
1116 match line_endings.binary_search_by_key(&char_idx, |&(_, c)| c) {
1117 Ok(i) => {
1118 let b = line_endings[i].0;
1119 self.char_to_byte_cache.insert(char_idx, b);
1120 return b;
1121 }
1122 Err(i) => {
1123 if i > 0 {
1124 let (b, c) = line_endings[i - 1];
1125 byte_offset = b;
1126 char_offset = c;
1127 }
1128 if i < line_endings.len() {
1129 to = line_endings[i].0;
1130 }
1131 }
1132 }
1133
1134 let slice = Slice::from_raw_offsets(byte_offset, to, self);
1135 let mut chars = slice.chars();
1136 let mut cur = byte_offset;
1137 for _ in 0..(char_idx - char_offset) {
1138 chars.next();
1139 cur = chars.cur + byte_offset;
1140 }
1141
1142 let slice_enclosed_gap = self.gap_start >= byte_offset && self.gap_end <= to;
1143 if cur >= self.gap_start && (cur < self.gap_end || slice_enclosed_gap) {
1145 cur += self.gap();
1146 }
1147
1148 self.char_to_byte_cache.insert(char_idx, cur);
1149 cur
1150 }
1151
1152 #[cfg(test)]
1156 fn shred_gap(&mut self) {
1157 for b in self.data[self.gap_start..self.gap_end].iter_mut() {
1158 *b = b'\0';
1159 }
1160 }
1161}
1162
1163#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
1167pub struct Slice<'a> {
1168 from: usize,
1170 left: &'a [u8],
1171 right: &'a [u8],
1172}
1173
1174impl<'a> From<Slice<'a>> for Cow<'a, str> {
1175 fn from(s: Slice<'a>) -> Self {
1176 if s.left.is_empty() {
1177 Cow::Borrowed(unsafe { std::str::from_utf8_unchecked(s.right) })
1179 } else if s.right.is_empty() {
1180 Cow::Borrowed(unsafe { std::str::from_utf8_unchecked(s.left) })
1182 } else {
1183 Cow::Owned(s.to_string())
1184 }
1185 }
1186}
1187
1188impl<'a> Slice<'a> {
1189 const NULL: Slice<'a> = Slice {
1190 from: 0,
1191 left: &[],
1192 right: &[],
1193 };
1194
1195 pub fn is_contiguous(&self) -> bool {
1196 self.left.is_empty() || self.right.is_empty()
1197 }
1198
1199 pub fn from(&self) -> usize {
1201 self.from
1202 }
1203
1204 #[inline]
1205 fn from_raw_offsets(from: usize, to: usize, gb: &'a GapBuffer) -> Slice<'a> {
1206 let to = min(to, gb.data.len());
1207 let logical_from = gb.raw_byte_to_byte(from);
1208
1209 if to <= gb.gap_start || from >= gb.gap_end {
1210 return Slice {
1211 from: logical_from,
1212 left: &gb.data[from..to],
1213 right: &[],
1214 };
1215 }
1216
1217 debug_assert!(from <= gb.gap_start, "line offset sits in gap");
1218
1219 Slice {
1220 from: logical_from,
1221 left: &gb.data[from..gb.gap_start],
1222 right: &gb.data[gb.gap_end..to],
1223 }
1224 }
1225
1226 pub fn subslice_from_byte_offsets(&self, from: usize, to: usize) -> Slice<'_> {
1227 if from == to {
1228 return Slice::NULL;
1229 }
1230
1231 let to = min(to, self.len());
1232 let logical_from = self.from + from;
1233
1234 if to <= self.left.len() {
1235 Slice {
1236 from: logical_from,
1237 left: &self.left[from..to],
1238 right: &[],
1239 }
1240 } else if from >= self.left.len() {
1241 Slice {
1242 from: logical_from,
1243 left: &[],
1244 right: &self.right[from..to],
1245 }
1246 } else {
1247 Slice {
1248 from: logical_from,
1249 left: &self.left[from..],
1250 right: &self.right[..to],
1251 }
1252 }
1253 }
1254
1255 pub fn len_utf8(&self) -> usize {
1259 self.chars().count()
1260 }
1261
1262 pub fn len(&self) -> usize {
1263 self.left.len() + self.right.len()
1264 }
1265
1266 pub fn is_empty(&self) -> bool {
1267 self.len() == 0
1268 }
1269
1270 pub fn as_strs(&self) -> (&str, &str) {
1272 unsafe {
1274 (
1275 std::str::from_utf8_unchecked(self.left),
1276 std::str::from_utf8_unchecked(self.right),
1277 )
1278 }
1279 }
1280
1281 pub fn as_slices(&self) -> (&[u8], &[u8]) {
1283 (self.left, self.right)
1284 }
1285
1286 pub fn slice_iter(self) -> SliceIter<'a> {
1288 SliceIter {
1289 inner: self,
1290 pos: Some(false),
1291 }
1292 }
1293
1294 pub fn chars(self) -> Chars<'a> {
1296 Chars { s: self, cur: 0 }
1297 }
1298
1299 pub fn rev_chars(self) -> RevChars<'a> {
1301 RevChars {
1302 s: self,
1303 cur: self.left.len() + self.right.len(),
1304 }
1305 }
1306
1307 pub fn indexed_chars(self, from: usize, rev: bool) -> IdxChars<'a> {
1309 let (cur, idx) = if rev {
1310 (
1311 self.left.len() + self.right.len(),
1312 from + count_chars(self.left) + count_chars(self.right),
1313 )
1314 } else {
1315 (0, from)
1316 };
1317
1318 IdxChars {
1319 s: self,
1320 cur,
1321 idx,
1322 rev,
1323 }
1324 }
1325
1326 fn cur_and_data(&self, cur: usize) -> (usize, &[u8]) {
1327 if cur < self.left.len() {
1328 (cur, self.left)
1329 } else {
1330 (cur - self.left.len(), self.right)
1331 }
1332 }
1333}
1334
1335impl fmt::Display for Slice<'_> {
1336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1337 let mut v = Vec::with_capacity(self.left.len() + self.right.len());
1338 v.extend_from_slice(self.left);
1339 v.extend_from_slice(self.right);
1340
1341 match String::from_utf8(v) {
1342 Ok(s) => write!(f, "{s}"),
1343 Err(_) => Err(fmt::Error),
1344 }
1345 }
1346}
1347
1348impl<'a> PartialEq<&'a str> for Slice<'_> {
1349 fn eq(&self, other: &&'a str) -> bool {
1350 let b = other.as_bytes();
1351 if b.len() != self.left.len() + self.right.len() {
1352 return false;
1353 }
1354
1355 &b[..self.left.len()] == self.left && &b[self.left.len()..] == self.right
1356 }
1357}
1358
1359impl PartialEq<String> for Slice<'_> {
1360 fn eq(&self, other: &String) -> bool {
1361 *self == other.as_str()
1362 }
1363}
1364
1365#[derive(Debug)]
1366pub struct SliceIter<'a> {
1367 inner: Slice<'a>,
1368 pos: Option<bool>,
1369}
1370
1371impl<'a> Iterator for SliceIter<'a> {
1372 type Item = &'a [u8];
1373
1374 fn next(&mut self) -> Option<Self::Item> {
1375 match self.pos? {
1376 false => {
1377 self.pos = Some(true);
1378 Some(self.inner.left)
1379 }
1380
1381 true => {
1382 self.pos = None;
1383 Some(self.inner.right)
1384 }
1385 }
1386 }
1387}
1388
1389#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
1391pub struct Chars<'a> {
1392 s: Slice<'a>,
1393 cur: usize,
1394}
1395
1396impl Iterator for Chars<'_> {
1397 type Item = char;
1398
1399 fn next(&mut self) -> Option<Self::Item> {
1400 if self.cur >= self.s.left.len() + self.s.right.len() {
1401 return None;
1402 }
1403
1404 let (cur, data) = self.s.cur_and_data(self.cur);
1405 let ch = unsafe { decode_char_at(cur, data) };
1407 self.cur += ch.len_utf8();
1408
1409 Some(ch)
1410 }
1411}
1412
1413#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
1415pub struct RevChars<'a> {
1416 s: Slice<'a>,
1417 cur: usize,
1418}
1419
1420impl Iterator for RevChars<'_> {
1421 type Item = char;
1422
1423 fn next(&mut self) -> Option<Self::Item> {
1424 if self.cur == 0 {
1425 return None;
1426 }
1427
1428 let (cur, data) = self.s.cur_and_data(self.cur - 1);
1429 let ch = unsafe { decode_char_ending_at(cur, data) };
1431 self.cur -= ch.len_utf8();
1432
1433 Some(ch)
1434 }
1435}
1436
1437#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
1439pub struct IdxChars<'a> {
1440 s: Slice<'a>,
1441 cur: usize,
1442 idx: usize,
1443 rev: bool,
1444}
1445
1446impl Iterator for IdxChars<'_> {
1447 type Item = (usize, char);
1448
1449 fn next(&mut self) -> Option<Self::Item> {
1450 if (!self.rev && self.cur >= self.s.left.len() + self.s.right.len())
1451 || (self.rev && self.cur == 0)
1452 {
1453 return None;
1454 }
1455
1456 if self.rev {
1457 let (cur, data) = self.s.cur_and_data(self.cur - 1);
1458 let ch = unsafe { decode_char_ending_at(cur, data) };
1460 let len = ch.len_utf8();
1461 self.idx -= 1;
1462 self.cur -= len;
1463 Some((self.idx, ch))
1464 } else {
1465 let (cur, data) = self.s.cur_and_data(self.cur);
1466 let ch = unsafe { decode_char_at(cur, data) };
1468 let len = ch.len_utf8();
1469 let res = Some((self.idx, ch));
1470 self.cur += len;
1471 self.idx += 1;
1472 res
1473 }
1474 }
1475}
1476
1477const CONT_MASK: u8 = 0b0011_1111;
1482
1483#[inline]
1487const fn utf8_first_byte(byte: u8, width: u32) -> u32 {
1488 (byte & (0x7F >> width)) as u32
1489}
1490
1491#[inline]
1493const fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 {
1494 (ch << 6) | (byte & CONT_MASK) as u32
1495}
1496
1497#[inline]
1500const fn utf8_is_cont_byte(byte: u8) -> bool {
1501 (byte as i8) < -64
1502}
1503
1504#[inline]
1507unsafe fn decode_char_at(start: usize, bytes: &[u8]) -> char {
1508 let x = bytes[start];
1511 if x < 128 {
1512 return unsafe { char::from_u32_unchecked(x as u32) };
1514 }
1515
1516 let init = utf8_first_byte(x, 2);
1520 let y = bytes[start + 1];
1522 let mut ch = utf8_acc_cont_byte(init, y);
1523
1524 if x >= 0xE0 {
1525 let z = bytes[start + 2];
1529 let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z);
1530 ch = (init << 12) | y_z;
1531 if x >= 0xF0 {
1532 let w = bytes[start + 3];
1536 ch = ((init & 7) << 18) | utf8_acc_cont_byte(y_z, w);
1537 }
1538 }
1539
1540 unsafe { char::from_u32_unchecked(ch) }
1542}
1543
1544#[inline]
1547unsafe fn decode_char_ending_at(end: usize, bytes: &[u8]) -> char {
1548 let w = match bytes[end] {
1550 b if b < 128 => return unsafe { char::from_u32_unchecked(b as u32) },
1552 b => b,
1553 };
1554
1555 let mut ch;
1558 let z = bytes[end - 1];
1560 ch = utf8_first_byte(z, 2);
1561 if utf8_is_cont_byte(z) {
1562 let y = bytes[end - 2];
1564 ch = utf8_first_byte(y, 3);
1565 if utf8_is_cont_byte(y) {
1566 let x = bytes[end - 3];
1568 ch = utf8_first_byte(x, 4);
1569 ch = utf8_acc_cont_byte(ch, y);
1570 }
1571 ch = utf8_acc_cont_byte(ch, z);
1572 }
1573 ch = utf8_acc_cont_byte(ch, w);
1574
1575 unsafe { char::from_u32_unchecked(ch) }
1577}
1578
1579#[cfg(test)]
1580mod tests {
1581 use super::*;
1582 use simple_test_case::test_case;
1583
1584 fn debug_buffer_content(gb: &GapBuffer) -> String {
1585 let mut v = gb.data.to_vec();
1586 v[gb.gap_start..gb.gap_end].copy_from_slice("_".repeat(gb.gap()).as_bytes());
1587 String::from_utf8(v).expect("valid utf8")
1588 }
1589
1590 fn raw_debug_buffer_content(gb: &GapBuffer) -> String {
1591 let mut v = gb.data.to_vec();
1592 for b in v[gb.gap_start..gb.gap_end].iter_mut() {
1593 if *b == b'\0' {
1594 *b = b'_';
1595 }
1596 }
1597 v.insert(gb.gap_end, b']');
1598 v.insert(gb.gap_start, b'[');
1599
1600 String::from_utf8(v).expect("valid utf8")
1601 }
1602
1603 #[test]
1604 fn from_string_matches_from_str() {
1605 let s = "this is a test";
1606 let gb1 = GapBuffer::from(s.to_string());
1607 let gb2 = GapBuffer::from(s);
1608
1609 assert_eq!(gb1, gb2);
1610 }
1611
1612 #[test]
1613 fn to_string_works() {
1614 let s = "this is a test";
1615 let gb = GapBuffer::from(s.to_string());
1616 assert_eq!(gb.to_string(), s);
1617 }
1618
1619 #[test]
1620 fn insert_into_empty_string_initial_gb_works() {
1621 let mut gb = GapBuffer::from(String::new());
1622 gb.insert_char(0, 'a');
1623 gb.shred_gap();
1624
1625 assert_eq!(gb.to_string(), "a");
1626 }
1627
1628 #[test_case("foo│foo│foo"; "interleaved multibyte and ascii")]
1629 #[test_case("hello, 世界!"; "blocks of multibyte and ascii")]
1630 #[test_case("hello, world!"; "just ascii")]
1631 #[test]
1632 fn len_chars_works(s: &str) {
1633 let mut gb = GapBuffer::from(s);
1634 let len_s = s.chars().count();
1635
1636 println!("initial: {:?}", raw_debug_buffer_content(&gb));
1637 assert_eq!(gb.len_chars(), len_s);
1638 assert_eq!(
1639 gb.len_chars(),
1640 gb.to_string().chars().count(),
1641 "char iter len != len_chars"
1642 );
1643 assert_eq!(
1644 gb.line(0).chars().count(),
1645 gb.line_len_chars(0),
1646 "line_len_chars != len_chars"
1647 );
1648
1649 gb.insert_char(5, 'X');
1650 gb.shred_gap();
1651
1652 println!("after insert X: {:?}", raw_debug_buffer_content(&gb));
1653 assert_eq!(gb.len_chars(), len_s + 1);
1654 assert_eq!(
1655 gb.len_chars(),
1656 gb.to_string().chars().count(),
1657 "char iter len != len_chars"
1658 );
1659
1660 gb.insert_char(3, '界');
1661 gb.shred_gap();
1662 println!("after insert 界: {:?}", raw_debug_buffer_content(&gb));
1663 assert_eq!(gb.len_chars(), len_s + 2);
1664 assert_eq!(
1665 gb.len_chars(),
1666 gb.to_string().chars().count(),
1667 "char iter len != len_chars"
1668 );
1669
1670 assert_eq!(gb.char(3), '界');
1671 gb.remove_char(3);
1672 gb.shred_gap();
1673 println!("after remove 界: {:?}", raw_debug_buffer_content(&gb));
1674 assert_eq!(gb.len_chars(), len_s + 1);
1675 assert_eq!(
1676 gb.len_chars(),
1677 gb.to_string().chars().count(),
1678 "char iter len != len_chars"
1679 );
1680
1681 assert_eq!(gb.char(5), 'X');
1682 gb.remove_char(5);
1683 gb.shred_gap();
1684 println!("after remove X: {:?}", debug_buffer_content(&gb));
1685 assert_eq!(gb.len_chars(), len_s);
1686 assert_eq!(
1687 gb.len_chars(),
1688 gb.to_string().chars().count(),
1689 "char iter len != len_chars"
1690 );
1691 assert_eq!(gb.to_string(), s);
1692 }
1693
1694 #[test_case(0, 14; "first line")]
1695 #[test_case(1, 13; "second line")]
1696 #[test_case(2, 14; "last line no trailing newline")]
1697 #[test]
1698 fn line_len_chars_works(line_idx: usize, expected: usize) {
1699 let gb = GapBuffer::from("hello, world!\nhow are you?\nthis is a test");
1700
1701 assert_eq!(gb.line_len_chars(line_idx), expected);
1702 }
1703
1704 #[test_case("foo│foo│foo"; "interleaved multibyte and ascii")]
1705 #[test_case("hello, 世界!"; "blocks of multibyte and ascii")]
1706 #[test_case("hello, world!"; "just ascii")]
1707 #[test]
1708 fn move_gap_to_maintains_content(s: &str) {
1709 let mut gb = GapBuffer::from(s);
1710
1711 for i in 0..gb.len_chars() {
1712 let idx = gb.char_to_byte(i);
1713 gb.move_gap_to(idx);
1714 gb.shred_gap();
1715
1716 let (s1, s2) = (
1719 std::str::from_utf8(&gb.data[..gb.gap_start]).unwrap(),
1720 std::str::from_utf8(&gb.data[gb.gap_end..]).unwrap(),
1721 );
1722
1723 assert_eq!(format!("{s1}{s2}"), s, "idx={idx}");
1724 }
1725 }
1726
1727 #[test]
1728 fn move_gap_to_maintains_line_content() {
1729 let s = "hello, world!\nhow are you?\nthis is a test";
1730 let mut gb = GapBuffer::from(s);
1731 assert_eq!(gb.len_lines(), 3);
1732
1733 for i in 0..gb.len_chars() {
1734 let idx = gb.char_to_byte(i);
1735 gb.move_gap_to(idx);
1736 gb.shred_gap();
1737 assert_eq!(gb.len_lines(), 3);
1738
1739 assert_eq!(gb.line(0).to_string(), "hello, world!\n", "idx={idx}");
1740 assert_eq!(gb.line(1).to_string(), "how are you?\n", "idx={idx}");
1741 assert_eq!(gb.line(2).to_string(), "this is a test", "idx={idx}");
1742 }
1743 }
1744
1745 #[test_case(0, 0, 0; "BOF cur at BOF")]
1746 #[test_case(27, 0, 0; "BOF cur at EOF")]
1747 #[test_case(27, 5, 5; "in the buffer cur at EOF")]
1748 #[test_case(5, 5, 5; "in the buffer cur at gap")]
1749 #[test_case(5, 3, 3; "in the buffer cur before gap")]
1750 #[test_case(5, 11, 15; "in the buffer cur after gap")]
1751 #[test_case(5, 7, 7; "multi byte 1")]
1752 #[test_case(5, 8, 10; "multi byte 2")]
1753 #[test]
1754 fn char_to_byte_works(cur: usize, char_idx: usize, expected: usize) {
1755 let s = "hello, 世界!\nhow are you?";
1756 let mut gb = GapBuffer::from(s);
1757 assert_eq!(s.len(), 27, "EOF case is not 0..s.len()");
1758 assert_eq!("世".len(), 3);
1759 gb.move_gap_to(cur);
1760 gb.shred_gap();
1761
1762 let byte_idx = gb.char_to_byte(char_idx);
1763 assert_eq!(byte_idx, expected, "{:?}", debug_buffer_content(&gb));
1764 }
1765
1766 #[test_case("⍉"; "one multi-byte char")]
1768 #[test_case("⌠⌖"; "two multi-byte chars")]
1769 #[test_case("؏ foo"; "one multi-byte char followed by ascii")]
1770 #[test_case("世界 abc"; "two multi-byte chars followed by ascii")]
1771 #[test]
1772 fn char_to_byte_works_with_leading_multibyte_char(s: &str) {
1773 let mut gb = GapBuffer::new();
1774 gb.insert_str(0, s);
1775 gb.shred_gap();
1776
1777 let byte_idx = gb.char_to_byte(0);
1778 assert_eq!(byte_idx, 0, "{:?}", debug_buffer_content(&gb));
1779 }
1780
1781 #[test_case("⍉", 0; "one multi-byte char")]
1783 #[test_case("⌠⌖", 3; "two multi-byte chars")]
1784 #[test_case("foo ؏", 4; "ascii followed by one multi-byte char")]
1785 #[test_case("abc 世界", 7; "ascii followed by two multi-byte chars")]
1786 #[test_case("Hello, world!⍉", 13; "error case from issue 140")]
1787 #[test]
1788 fn char_to_byte_works_with_single_line_buffers_ending_in_a_multibyte_char(
1789 s: &str,
1790 expected: usize,
1791 ) {
1792 let mut gb = GapBuffer::new();
1793 gb.insert_str(0, s);
1794 gb.shred_gap();
1795 let last_char = s.chars().count() - 1;
1796
1797 let byte_idx = gb.char_to_byte(last_char);
1798 assert_eq!(byte_idx, expected, "{:?}", debug_buffer_content(&gb));
1799 }
1800
1801 #[test_case(0, 0, 64, 'h'; "BOF cur at BOF")]
1802 #[test_case(27, 0, 0, 'h'; "BOF cur at EOF")]
1803 #[test_case(27, 5, 5, ','; "in the buffer cur at EOF")]
1804 #[test_case(5, 5, 69, ','; "in the buffer cur at gap")]
1805 #[test_case(5, 3, 3, 'l'; "in the buffer cur after gap")]
1806 #[test_case(5, 11, 79, 'h'; "in the buffer cur before gap")]
1807 #[test_case(5, 7, 71, '世'; "multi byte 1")]
1808 #[test_case(5, 8, 74, '界'; "multi byte 2")]
1809 #[test]
1810 fn char_to_raw_byte_works(cur: usize, char_idx: usize, expected: usize, expected_ch: char) {
1811 let s = "hello, 世界!\nhow are you?";
1812 let mut gb = GapBuffer::from(s);
1813 assert_eq!(s.len(), 27, "EOF case is not 0..s.len()");
1814 assert_eq!("世".len(), 3);
1815 gb.move_gap_to(cur);
1816 gb.shred_gap();
1817
1818 let byte_idx = gb.char_to_raw_byte(char_idx);
1819 assert_eq!(byte_idx, expected, "{:?}", debug_buffer_content(&gb));
1820
1821 let ch = unsafe { decode_char_at(byte_idx, &gb.data) };
1823 assert_eq!(ch, expected_ch);
1824 }
1825
1826 #[test_case("hello, world! this is the last line"; "ascii no newline")]
1827 #[test_case("hello, world!\nthis is the last line"; "ascii single newline")]
1828 #[test_case("foo│foo│foo"; "mixed width no newlines")]
1829 #[test_case("hello, 世界!\nhow are you?"; "mixed width single newline")]
1830 #[test]
1831 fn byte_to_char_works(s: &str) {
1832 let mut gb = GapBuffer::from(s);
1833
1834 for pos in 0..gb.len_chars() - 1 {
1835 gb.move_gap_to(gb.char_to_byte(pos));
1836 gb.shred_gap();
1837
1838 for (ch_idx, (byte_idx, _ch)) in s.char_indices().enumerate() {
1839 let idx = gb.byte_to_char(byte_idx);
1840 assert_eq!(idx, ch_idx, "gap@{pos}");
1841 }
1842 }
1843 }
1844
1845 #[test_case(0, 0, "hello, world!\n"; "first line cur at BOF")]
1846 #[test_case(0, 1, "how are you?"; "second line cur at BOF")]
1847 #[test_case(26, 0, "hello, world!\n"; "first line cur at EOF")]
1848 #[test_case(26, 1, "how are you?"; "second line cur at EOF")]
1849 #[test_case(10, 0, "hello, world!\n"; "first line cur in line")]
1850 #[test_case(10, 1, "how are you?"; "second line cur in line")]
1851 #[test]
1852 fn slice_to_string_works(cur: usize, line: usize, expected: &str) {
1853 let mut gb = GapBuffer::from("hello, world!\nhow are you?");
1854 gb.move_gap_to(cur);
1855 gb.shred_gap();
1856
1857 assert_eq!(gb.line(line).to_string(), expected);
1858 }
1859
1860 #[test]
1861 fn line_to_char_works() {
1862 let l1 = "hello, world!\n";
1863 let l2 = "how are you?\n";
1864 let l3 = "this is a test";
1865
1866 let gb = GapBuffer::from(format!("{l1}{l2}{l3}"));
1867
1868 assert_eq!(gb.line_to_char(0), 0);
1869 assert_eq!(gb.line_to_char(1), l1.chars().count());
1870 assert_eq!(gb.line_to_char(2), l1.chars().count() + l2.chars().count());
1871 }
1872
1873 #[test_case(0, 0; "start of first line")]
1874 #[test_case(5, 0; "in first line")]
1875 #[test_case(13, 0; "end of first line")]
1876 #[test_case(14, 1; "start of second line")]
1877 #[test_case(20, 1; "in second line")]
1878 #[test_case(26, 1; "end of second line")]
1879 #[test_case(27, 2; "start of third line")]
1880 #[test_case(30, 2; "in third line")]
1881 #[test_case(40, 2; "end of third line")]
1882 #[test]
1883 fn char_to_line_works(char_idx: usize, line_idx: usize) {
1884 let l1 = "hello, world!\n";
1885 let l2 = "how are you?\n";
1886 let l3 = "this is a test";
1887
1888 let gb = GapBuffer::from(format!("{l1}{l2}{l3}"));
1889
1890 assert_eq!(gb.char_to_line(char_idx), line_idx);
1891 }
1892
1893 #[test_case(&[(0, 'h')], "hello world"; "insert front")]
1894 #[test_case(&[(4, ',')], "ello, world"; "insert inner")]
1895 #[test_case(&[(10, '!')], "ello world!"; "insert back")]
1896 #[test_case(&[(4, ','), (11, '!')], "ello, world!"; "insert inner then back")]
1897 #[test_case(&[(4, ','), (0, 'h')], "hello, world"; "insert inner then front")]
1898 #[test_case(&[(0, 'h'), (5, ','),], "hello, world"; "insert front then inner")]
1899 #[test_case(&[(10, '!'), (0, 'h'), (5, ',')], "hello, world!"; "insert all")]
1900 #[test]
1901 fn insert_char(inserts: &[(usize, char)], expected: &str) {
1902 let mut gb = GapBuffer::from("ello world");
1903
1904 for &(idx, ch) in inserts {
1905 gb.insert_char(idx, ch);
1906 gb.shred_gap();
1907 }
1908
1909 assert_eq!(gb.to_string(), expected, "{:?}", debug_buffer_content(&gb))
1910 }
1911
1912 #[test]
1913 fn insert_char_with_moving_cur() {
1914 let mut gb = GapBuffer::from("hello ");
1915 gb.insert_char(6, 'w');
1916 gb.shred_gap();
1917 gb.insert_char(7, 'o');
1918 gb.shred_gap();
1919 gb.insert_char(8, 'r');
1920 gb.shred_gap();
1921 gb.insert_char(9, 'l');
1922 gb.shred_gap();
1923 gb.insert_char(10, 'd');
1924 gb.shred_gap();
1925 gb.insert_char(11, '!');
1926 gb.shred_gap();
1927 gb.insert_char(5, ',');
1928 gb.shred_gap();
1929
1930 assert_eq!(
1931 gb.to_string(),
1932 "hello, world!",
1933 "{:?}",
1934 debug_buffer_content(&gb)
1935 )
1936 }
1937
1938 #[test]
1939 fn insert_newline_char_is_tracked_correctly() {
1940 let s = "hello, world!\nhow are you?";
1941 let mut gb = GapBuffer::from(s);
1942 assert_eq!(gb.len_lines(), 2);
1943
1944 println!("initial: {:?}", raw_debug_buffer_content(&gb));
1945 gb.insert_char(6, '\n');
1946 gb.shred_gap();
1947 println!("insert: {:?}", raw_debug_buffer_content(&gb));
1948
1949 assert_eq!(gb.len_lines(), 3);
1950 assert_eq!(gb.line(0).to_string(), "hello,\n");
1951 assert_eq!(gb.line(1).to_string(), " world!\n");
1952 assert_eq!(gb.line(2).to_string(), "how are you?");
1953
1954 for idx in 0..=gb.len_chars() {
1955 gb.move_gap_to(idx);
1956 gb.shred_gap();
1957 assert_eq!(gb.len_lines(), 3);
1958
1959 assert_eq!(gb.line(0).to_string(), "hello,\n", "idx={idx}");
1960 assert_eq!(gb.line(1).to_string(), " world!\n", "idx={idx}");
1961 assert_eq!(gb.line(2).to_string(), "how are you?", "idx={idx}");
1962 }
1963 }
1964
1965 #[test_case(&[(0, "hell")], "helloworl"; "insert front")]
1966 #[test_case(&[(1, ", ")], "o, worl"; "insert inner")] #[test_case(&[(5, "d!")], "oworld!"; "insert back")]
1968 #[test_case(&[(5, "d!"), (0, "hell"), (5, ", ")], "hello, world!"; "insert all")]
1969 #[test_case(&[(5, "d!"), (0, "hell"), (5, ",\n")], "hello,\nworld!"; "insert all w newline")]
1970 #[test]
1971 fn insert_str(inserts: &[(usize, &str)], expected: &str) {
1972 let mut gb = GapBuffer::from("oworl");
1973 for &(idx, s) in inserts {
1974 gb.insert_str(idx, s);
1975 gb.shred_gap();
1976 }
1977
1978 assert_eq!(gb.to_string(), expected, "{:?}", debug_buffer_content(&gb))
1979 }
1980
1981 #[test]
1982 fn insert_newline_in_str_is_tracked_correctly() {
1983 let s = "hello, world!\nhow are you?";
1984 let mut gb = GapBuffer::from(s);
1985 assert_eq!(gb.len_lines(), 2);
1986
1987 let s2 = " sailor\nisn't this fun?\nwhat a wonderful\n";
1988 gb.insert_str(6, s2);
1989 gb.shred_gap();
1990
1991 for idx in 0..=gb.len_chars() {
1992 gb.move_gap_to(idx);
1993 gb.shred_gap();
1994 assert_eq!(gb.len_lines(), 5);
1995
1996 assert_eq!(gb.line(0).to_string(), "hello, sailor\n", "idx={idx}");
1997 assert_eq!(gb.line(1).to_string(), "isn't this fun?\n", "idx={idx}");
1998 assert_eq!(gb.line(2).to_string(), "what a wonderful\n", "idx={idx}");
1999 assert_eq!(gb.line(3).to_string(), " world!\n", "idx={idx}");
2000 assert_eq!(gb.line(4).to_string(), "how are you?", "idx={idx}");
2001 }
2002 }
2003
2004 #[test_case(6, "hello,world!"; "at gap start")]
2005 #[test_case(7, "hello, orld!"; "at gap end")]
2006 #[test_case(12, "hello, world"; "after gap")]
2007 #[test_case(0, "ello, world!"; "before gap")]
2008 #[test]
2009 fn remove_char(idx: usize, expected: &str) {
2010 let mut gb = GapBuffer::from("hello, world!");
2011 gb.move_gap_to(6); gb.shred_gap();
2013 gb.remove_char(idx);
2014 gb.shred_gap();
2015
2016 assert_eq!(gb.to_string(), expected, "{:?}", debug_buffer_content(&gb))
2017 }
2018
2019 #[test]
2020 fn remove_newline_char_is_tracked_correctly() {
2021 let s = "hello, world!\nhow are you?";
2022 let mut gb = GapBuffer::from(s);
2023 assert_eq!(gb.len_lines(), 2);
2024
2025 gb.remove_char(13);
2026 gb.shred_gap();
2027
2028 assert_eq!(gb.len_lines(), 1);
2029 assert_eq!(gb.line(0).to_string(), "hello, world!how are you?");
2030 }
2031
2032 #[test_case(6, 9, "hello,rld!"; "at gap start")]
2033 #[test_case(7, 10, "hello, ld!"; "at gap end")]
2034 #[test_case(10, 13, "hello, wor"; "after gap")]
2035 #[test_case(0, 5, ", world!"; "before gap")]
2036 #[test_case(0, 13, ""; "remove all")]
2037 #[test]
2038 fn remove_range_works(from: usize, to: usize, expected: &str) {
2039 let s = "hello, world!";
2040 assert_eq!(s.len(), 13, "remove all case is not 0..s.len()");
2041
2042 let mut gb = GapBuffer::from(s);
2043 gb.move_gap_to(6); gb.shred_gap();
2045 gb.remove_range(from, to);
2046 gb.shred_gap();
2047
2048 assert_eq!(gb.to_string(), expected, "{:?}", debug_buffer_content(&gb))
2049 }
2050
2051 #[test]
2052 fn remove_range_w_multibyte_chars_works() {
2053 let s = "foo│foo│foo";
2054 let mut gb = GapBuffer::from(s);
2055
2056 gb.remove_range(0, 3);
2057 gb.shred_gap();
2058 assert_eq!(gb.to_string(), "│foo│foo");
2059 assert_eq!(gb.len_chars(), 8);
2060
2061 gb.remove_range(1, 4);
2062 gb.shred_gap();
2063 assert_eq!(gb.to_string(), "││foo");
2064 assert_eq!(gb.len_chars(), 5);
2065
2066 gb.remove_range(2, 5);
2067 gb.shred_gap();
2068 assert_eq!(gb.to_string(), "││");
2069 assert_eq!(gb.len_chars(), 2);
2070 }
2071
2072 #[test]
2073 fn remove_range_for_last_line_works() {
2074 let s = "hello, world!\nthis is the last line";
2075 let mut gb = GapBuffer::from(s);
2076 gb.remove_range(14, s.len());
2077 gb.shred_gap();
2078 assert_eq!(gb.to_string(), "hello, world!\n");
2079 assert_eq!(gb.len_lines(), 2);
2080 }
2081
2082 #[test_case(10, 15, "hello, worow are you?"; "spanning newline")]
2083 #[test_case(7, 14, "hello, how are you?"; "ending on newline")]
2084 #[test_case(13, 26, "hello, world!"; "starting on newline")]
2085 #[test]
2086 fn remove_newline_in_str_is_tracked_correctly(from: usize, to: usize, expected: &str) {
2087 let s = "hello, world!\nhow are you?";
2088 let mut gb = GapBuffer::from(s);
2089 assert_eq!(gb.len_lines(), 2);
2090
2091 gb.remove_range(from, to);
2092 gb.shred_gap();
2093
2094 assert_eq!(gb.len_lines(), 1);
2095 assert_eq!(gb.to_string(), expected);
2096 assert_eq!(gb.line(0).to_string(), expected);
2097 }
2098
2099 #[test_case('X'; "ascii")]
2100 #[test_case('界'; "multi-byte")]
2101 #[test]
2102 fn insert_remove_char_is_idempotent(ch: char) {
2103 let s = "hello, world!";
2104 let mut gb = GapBuffer::from(s);
2105 gb.insert_char(6, ch);
2106 gb.shred_gap();
2107 gb.remove_char(6);
2108 gb.shred_gap();
2109
2110 assert_eq!(gb.to_string(), s, "{:?}", debug_buffer_content(&gb))
2111 }
2112
2113 #[test_case("TEST", 1; "without trailing newline")]
2114 #[test_case("TEST\n", 2; "with trailing newline")]
2115 #[test_case("TEST\nTEST", 2; "with internal newline")]
2116 #[test]
2117 fn insert_remove_str_is_idempotent(edit: &str, expected_lines: usize) {
2118 let s = "hello, world!";
2119 let mut gb = GapBuffer::from(s);
2120
2121 println!("initial: {:?}", raw_debug_buffer_content(&gb));
2122 for n in 0..gb.len_lines() {
2123 println!("{:?}", gb.line(n).to_string());
2124 }
2125
2126 gb.insert_str(6, edit);
2127 gb.shred_gap();
2128 assert_eq!(gb.len_lines(), expected_lines);
2129 println!("insert: {:?}", raw_debug_buffer_content(&gb));
2130 for n in 0..gb.len_lines() {
2131 println!("{:?}", gb.line(n).to_string());
2132 }
2133
2134 gb.remove_range(6, 6 + edit.len());
2135 gb.shred_gap();
2136 println!("remove: {:?}", raw_debug_buffer_content(&gb));
2137 for n in 0..gb.len_lines() {
2138 println!("{:?}", gb.line(n).to_string());
2139 }
2140
2141 assert_eq!(gb.to_string(), s);
2142 }
2143
2144 #[test]
2145 fn chars_work() {
2146 let s1 = "hello, world!\n";
2147 let s2 = "how are you?";
2148 let gb = GapBuffer::from(format!("{s1}{s2}"));
2149
2150 let l1_chars: String = gb.line(0).chars().collect();
2151 assert_eq!(l1_chars, s1);
2152
2153 let l2_chars: String = gb.line(1).chars().collect();
2154 assert_eq!(l2_chars, s2);
2155 }
2156
2157 #[test_case(
2158 "hello, 世界!", false,
2159 &[(0, 'h'), (1, 'e'), (2, 'l'), (3, 'l'), (4, 'o'),
2160 (5, ','), (6, ' '), (7, '世'), (8, '界'), (9, '!')];
2161 "multi-byte block forward"
2162 )]
2163 #[test_case(
2164 "hello, 世界!", true,
2165 &[(9, '!'), (8, '界'), (7, '世'), (6, ' '), (5, ','),
2166 (4, 'o'), (3, 'l'), (2, 'l'), (1, 'e'), (0, 'h')];
2167 "multi-byte block reversed"
2168 )]
2169 #[test_case(
2170 "foo│foo│foo", false,
2171 &[(0, 'f'), (1, 'o'), (2, 'o'), (3, '│'), (4, 'f'), (5, 'o'),
2172 (6, 'o'), (7, '│'), (8, 'f'), (9, 'o'), (10, 'o')];
2173 "interleaved forward"
2174 )]
2175 #[test_case(
2176 "foo│foo│foo", true,
2177 &[(10, 'o'), (9, 'o'), (8, 'f'), (7, '│'), (6, 'o'), (5, 'o'),
2178 (4, 'f'), (3, '│'), (2, 'o'), (1, 'o'), (0, 'f')];
2179 "interleaved reversed"
2180 )]
2181 #[test]
2182 fn indexed_chars_works(s: &str, rev: bool, expected: &[(usize, char)]) {
2183 let mut gb = GapBuffer::from(s);
2184 let v: Vec<(usize, char)> = gb.line(0).indexed_chars(0, rev).collect();
2185 assert_eq!(&v, expected);
2186
2187 for i in 0..gb.len_chars() {
2188 let idx = gb.char_to_byte(i);
2189 gb.move_gap_to(idx);
2190 gb.shred_gap();
2191
2192 let v: Vec<(usize, char)> = gb.line(0).indexed_chars(0, rev).collect();
2193 assert_eq!(&v, expected, "idx={idx}");
2194 }
2195 }
2196
2197 #[test_case("foo│foo│foo"; "interleaved multibyte and ascii")]
2198 #[test_case("hello, 世界!"; "blocks of multibyte and ascii")]
2199 #[test]
2200 fn chars_works(s: &str) {
2201 let mut gb = GapBuffer::from(s);
2202 let chars: String = gb.line(0).chars().collect();
2203 assert_eq!(chars, s);
2204
2205 for i in 0..gb.len_chars() {
2206 let idx = gb.char_to_byte(i);
2207 gb.move_gap_to(idx);
2208 gb.shred_gap();
2209
2210 let chars: String = gb.line(0).chars().collect();
2211 assert_eq!(chars, s, "idx={idx}");
2212 }
2213 }
2214
2215 #[test]
2216 fn slice_works() {
2217 let mut gb = GapBuffer::from("hello, world!\nhow are you?");
2218 let slice = Slice::from_raw_offsets(0, gb.cap, &gb);
2219 let (s1, s2) = slice.as_strs();
2220 assert_eq!(s1, "");
2221 assert_eq!(s2, "hello, world!\nhow are you?");
2222
2223 let slice = gb.slice(6, 17);
2224 let (s1, s2) = slice.as_strs();
2225 assert_eq!(s1, " world!\nhow");
2226 assert_eq!(s2, "");
2227
2228 gb.move_gap_to(12);
2229 gb.shred_gap();
2230 println!("after move: {:?}", raw_debug_buffer_content(&gb));
2231
2232 let slice = gb.slice(6, 17);
2233 let (s1, s2) = slice.as_strs();
2234 assert_eq!(s1, " world");
2235 assert_eq!(s2, "!\nhow");
2236 }
2237
2238 #[test]
2239 fn null_slice_is_empty() {
2240 let mut gb = GapBuffer::from("hello, world!\nhow are you?");
2241
2242 for i in 0..gb.len_chars() {
2243 let idx = gb.char_to_byte(i);
2244 gb.move_gap_to(idx);
2245 gb.shred_gap();
2246
2247 let slice = gb.slice(0, 0);
2248
2249 assert!(slice.left.is_empty(), "{i} slice left: {:?}", slice.left);
2250 assert!(slice.right.is_empty(), "{i} slice right: {:?}", slice.right);
2251 }
2252 }
2253
2254 #[test]
2255 fn slice_eq_str_works() {
2256 let mut gb = GapBuffer::from("hello, world!\nhow are you?");
2257 gb.move_gap_to(3);
2258 gb.shred_gap();
2259 let slice = gb.slice(0, 5);
2260 assert_eq!(slice, "hello");
2261 }
2262
2263 #[test]
2264 fn chars_in_raw_range_works() {
2265 let mut gb = GapBuffer::from("hello, world!\nhow are you?");
2266 let char_from = 7;
2267 let char_to = 12;
2268
2269 for i in 0..gb.len_chars() {
2270 let idx = gb.char_to_byte(i);
2271 gb.move_gap_to(idx);
2272 gb.shred_gap();
2273
2274 let byte_from = gb.char_to_raw_byte(char_from);
2275 let byte_to = gb.char_to_raw_byte(char_to);
2276 let n_chars = gb.chars_in_raw_range(byte_from, byte_to);
2277 assert_eq!(n_chars, char_to - char_from, "gap at {i}");
2278
2279 let n_chars = gb.chars_in_raw_range(0, gb.char_to_raw_byte(gb.n_chars));
2280 assert_eq!(n_chars, gb.n_chars, "gap at {i}");
2281 }
2282 }
2283
2284 #[test_case(0, "\n", 2; "insert newline at start")]
2285 #[test_case(6, "\n", 2; "insert newline at end of first line")]
2286 #[test_case(3, "\n", 2; "insert newline in middle of line")]
2287 #[test_case(0, "new\n", 2; "insert line at start")]
2288 #[test_case(6, "\nnew", 2; "insert line at end")]
2289 #[test_case(3, "X\nY\n", 3; "insert multiple newlines in middle")]
2290 #[test_case(0, "a\nb\nc\n", 4; "insert multiple lines at start")]
2291 #[test_case(6, "\na\nb\nc", 4; "insert multiple lines at end")]
2292 #[test]
2293 fn insert_str_tracks_line_endings_correctly(at: usize, insert: &str, expected_lines: usize) {
2294 let s = "line 1";
2295 let mut gb = GapBuffer::from(s);
2296 assert_eq!(gb.len_lines(), 1);
2297
2298 gb.insert_str(at, insert);
2299 gb.shred_gap();
2300
2301 assert_eq!(gb.len_lines(), expected_lines);
2302 }
2303
2304 #[test_case(0, 7, "line 2\nline 3", 2; "remove first line from start")]
2305 #[test_case(0, 14, "line 3", 1; "remove first two lines from start")]
2306 #[test_case(7, 14, "line 1\nline 3", 2; "remove middle line")]
2307 #[test_case(14, 20, "line 1\nline 2\n", 3; "remove last line")]
2308 #[test_case(6, 14, "line 1line 3", 1; "remove spanning first newline")]
2309 #[test]
2310 fn remove_range_tracks_line_endings_correctly(
2311 from: usize,
2312 to: usize,
2313 expected: &str,
2314 expected_lines: usize,
2315 ) {
2316 let s = "line 1\nline 2\nline 3";
2317 let mut gb = GapBuffer::from(s);
2318 assert_eq!(gb.len_lines(), 3);
2319
2320 gb.remove_range(from, to);
2321 gb.shred_gap();
2322
2323 assert_eq!(gb.to_string(), expected);
2324 assert_eq!(gb.len_lines(), expected_lines);
2325 }
2326
2327 fn _insert_chars(gb: &mut GapBuffer, s: &str) {
2328 for (idx, ch) in s.chars().enumerate() {
2329 gb.insert_char(idx + 4, ch);
2330 gb.shred_gap();
2331 }
2332 }
2333
2334 fn _insert_str(gb: &mut GapBuffer, s: &str) {
2335 gb.insert_str(4, s);
2336 gb.shred_gap();
2337 }
2338
2339 #[test_case(_insert_chars; "individual chars")]
2340 #[test_case(_insert_str; "whole string")]
2341 #[test]
2342 fn insert_with_multibyte_chars_preserves_line_endings(insert: fn(&mut GapBuffer, &str)) {
2343 let slice_str = |gb: &GapBuffer| gb.slice(0, gb.len_chars()).to_string();
2344
2345 let mut gb = GapBuffer::from("foo\nbar\nbaz\n");
2346 let s = "世\n界 🦊\n ";
2347
2348 insert(&mut gb, s);
2349
2350 assert_eq!(slice_str(&gb), "foo\n世\n界 🦊\n bar\nbaz\n");
2351
2352 assert_eq!(gb.char(8), '🦊');
2353 gb.remove_char(8);
2354 gb.shred_gap();
2355
2356 assert_eq!(slice_str(&gb), "foo\n世\n界 \n bar\nbaz\n");
2357 }
2358
2359 #[test]
2360 fn char_works_with_multibyte_characters() {
2361 let s = "世\n界 🦊\n ";
2362 let gb = GapBuffer::from(s);
2363
2364 for (idx, ch) in s.chars().enumerate() {
2365 assert_eq!(gb.char(idx), ch);
2366 }
2367 }
2368
2369 #[test]
2370 fn char_to_raw_byte_line_end_with_no_newlines() {
2371 let mut gb =
2372 GapBuffer::from("// does it need to be a doc comment? that is a long enough line to");
2373 gb.move_gap_to(0);
2374 gb.shred_gap();
2375 assert_eq!(gb.char_to_raw_byte(65), 129);
2376 gb.move_gap_to(10);
2377 gb.shred_gap();
2378 assert_eq!(gb.char_to_raw_byte(65), 129);
2379 gb.move_gap_to(66);
2380 gb.shred_gap();
2381 assert_eq!(gb.char_to_raw_byte(65), 65);
2382 gb.insert_char(66, '\n');
2383 gb.shred_gap();
2384 assert_eq!(
2385 gb.to_string(),
2386 "// does it need to be a doc comment? that is a long enough line to\n"
2387 );
2388 }
2389
2390 #[test_case(0, "", "foo bar"; "gap at start")]
2391 #[test_case(3, "foo", " bar"; "gap in between")]
2392 #[test]
2393 fn as_strs_works(byte_idx: usize, left: &str, right: &str) {
2394 let mut gb = GapBuffer::from("foo bar");
2395 gb.move_gap_to(byte_idx);
2396 gb.shred_gap();
2397
2398 let (l, r) = gb.as_strs();
2399
2400 assert_eq!((l, r), (left, right));
2401 }
2402
2403 #[test_case(0; "gap at start")]
2404 #[test_case(3; "gap in between")]
2405 #[test]
2406 fn as_str_works(byte_idx: usize) {
2407 let mut gb = GapBuffer::from("foo bar");
2408 gb.move_gap_to(byte_idx);
2409 gb.shred_gap();
2410
2411 let s = gb.as_str();
2412
2413 assert_eq!(s, "foo bar");
2414 assert_eq!(gb.gap_start, 0);
2415 }
2416}