1use indigo_kernel::{
2 display_width::DisplayWidth as _,
3 grapheme::{
4 Graphemes, ceil_grapheme_boundary, floor_grapheme_boundary, is_grapheme_boundary,
5 is_grapheme_start, next_grapheme_boundary, prev_grapheme_boundary,
6 },
7};
8use regex_cursor::{Cursor, IntoCursor};
9use ropey::{LineType, Rope, RopeSlice};
10use std::ops::{Bound, RangeBounds};
11
12pub const LINE_TYPE: LineType = LineType::LF_CR;
13
14pub trait RopeExt {
15 fn as_slice(&self) -> RopeSlice<'_>;
16
17 fn len_lines_unix(&self) -> usize {
20 let rope = self.as_slice();
21 if rope.len() == 0 {
22 return 0;
23 }
24 let last_byte = rope.byte(rope.len() - 1);
25 rope.len_lines(LINE_TYPE) - if last_byte == b'\n' { 1 } else { 0 }
26 }
27
28 fn find_next_byte<R>(&self, byte_range: R, needles: &[u8]) -> Option<usize>
29 where
30 R: RangeBounds<usize> + Clone,
31 {
32 if needles.is_empty() {
33 return None;
34 }
35
36 let rope = self.as_slice().slice(byte_range.clone());
37
38 let start = match byte_range.start_bound() {
39 Bound::Included(n) => *n,
40 Bound::Excluded(_) => unreachable!("wtf"),
41 Bound::Unbounded => 0,
42 };
43
44 let mut needle_index: Option<usize> = None;
45
46 for chunk in needles.chunks(3) {
47 match chunk.len() {
48 3 => {
49 let haystack = rope.chunks().map(|c| c.as_bytes());
50 if let Some(pos) = memchr3(chunk[0], chunk[1], chunk[2], haystack) {
51 needle_index = Some(needle_index.map_or(pos, |current| current.min(pos)));
52 }
53 }
54 _ => {
55 for &needle in chunk {
56 let haystack = rope.chunks().map(|c| c.as_bytes());
57 if let Some(pos) = memchr(needle, haystack) {
58 needle_index =
59 Some(needle_index.map_or(pos, |current| current.min(pos)));
60 }
61 }
62 }
63 }
64 }
65
66 needle_index.map(|byte_index| start + byte_index)
67 }
68
69 fn find_prev_byte<R>(&self, byte_range: R, needles: &[u8]) -> Option<usize>
70 where
71 R: RangeBounds<usize> + Clone,
72 {
73 if needles.is_empty() {
74 return None;
75 }
76
77 let rope = self.as_slice().slice(byte_range.clone());
78
79 let end = match byte_range.end_bound() {
80 Bound::Included(n) => *n,
81 Bound::Excluded(n) => n.saturating_sub(1),
82 Bound::Unbounded => rope.len().saturating_sub(1),
83 };
84
85 let mut needle_index: Option<usize> = None;
86
87 for chunk in needles.chunks(3) {
88 match chunk.len() {
89 3 => {
90 let haystack = rope
91 .chunks_at(rope.len())
92 .0
93 .reversed()
94 .map(|c| c.as_bytes());
95 if let Some(pos) = memrchr3(chunk[0], chunk[1], chunk[2], haystack) {
96 needle_index = Some(needle_index.map_or(pos, |current| current.min(pos)));
97 }
98 }
99 _ => {
100 for &needle in chunk {
101 let haystack = rope
102 .chunks_at(rope.len())
103 .0
104 .reversed()
105 .map(|c| c.as_bytes());
106 if let Some(pos) = memrchr(needle, haystack) {
107 needle_index =
108 Some(needle_index.map_or(pos, |current| current.min(pos)));
109 }
110 }
111 }
112 }
113 }
114
115 needle_index.map(|byte_index| end - byte_index)
116 }
117
118 fn get_grapheme(&self, byte_index: usize) -> Option<RopeSlice<'_>> {
119 let rope = self.as_slice();
120 rope.next_grapheme_boundary(byte_index)
121 .map(|end| rope.slice(byte_index..end))
122 }
123
124 fn grapheme(&self, byte_index: usize) -> RopeSlice<'_> {
125 self.get_grapheme(byte_index).unwrap()
126 }
127
128 fn graphemes(&self) -> Graphemes<'_> {
129 Graphemes::new(&self.as_slice())
130 }
131
132 fn is_grapheme_boundary(&self, byte_offset: usize) -> bool {
133 is_grapheme_boundary(&self.as_slice(), byte_offset)
134 }
135
136 fn is_grapheme_start(&self, byte_index: usize) -> bool {
137 is_grapheme_start(&self.as_slice(), byte_index)
138 }
139
140 fn snap_to_grapheme_start(&self, byte_index: usize) -> Option<usize> {
141 let rope = self.as_slice();
142 let last = self.last_grapheme_start()?;
143 Some(floor_grapheme_boundary(&rope, byte_index.min(last)))
144 }
145
146 fn last_grapheme_start(&self) -> Option<usize> {
147 let rope = self.as_slice();
148 prev_grapheme_boundary(&rope, rope.len())
149 }
150
151 fn prev_grapheme_boundary(&self, byte_offset: usize) -> Option<usize> {
152 prev_grapheme_boundary(&self.as_slice(), byte_offset)
153 }
154
155 fn next_grapheme_boundary(&self, byte_offset: usize) -> Option<usize> {
156 next_grapheme_boundary(&self.as_slice(), byte_offset)
157 }
158
159 fn floor_grapheme_boundary(&self, byte_offset: usize) -> usize {
160 floor_grapheme_boundary(&self.as_slice(), byte_offset)
161 }
162
163 fn ceil_grapheme_boundary(&self, byte_offset: usize) -> usize {
164 ceil_grapheme_boundary(&self.as_slice(), byte_offset)
165 }
166
167 fn display_column(&self, byte_index: usize) -> usize {
168 let rope = self.as_slice();
169 let line_index = rope.byte_to_line_idx(byte_index, LINE_TYPE);
170 let line_byte_index = rope.line_to_byte_idx(line_index, LINE_TYPE);
171 rope.slice(line_byte_index..byte_index).display_width()
172 }
173}
174
175impl RopeExt for RopeSlice<'_> {
176 fn as_slice(&self) -> RopeSlice<'_> {
177 *self
178 }
179}
180
181impl RopeExt for Rope {
182 fn as_slice(&self) -> RopeSlice<'_> {
183 self.slice(0..)
184 }
185}
186
187#[derive(Clone, Copy)]
188enum Pos {
189 ChunkStart,
190 ChunkEnd,
191}
192
193pub struct RegexCursorInput<'a>(RopeSlice<'a>);
194
195impl<'a> From<RopeSlice<'a>> for RegexCursorInput<'a> {
196 fn from(slice: RopeSlice<'a>) -> Self {
197 Self(slice)
198 }
199}
200
201impl<'a> From<&'a Rope> for RegexCursorInput<'a> {
202 fn from(rope: &'a Rope) -> Self {
203 Self(rope.slice(..))
204 }
205}
206
207impl<'a> IntoCursor for RegexCursorInput<'a> {
208 type Cursor = RopeyCursor<'a>;
209
210 fn into_cursor(self) -> Self::Cursor {
211 RopeyCursor::new(self.0)
212 }
213}
214
215#[derive(Clone)]
216pub struct RopeyCursor<'a> {
217 iter: ropey::iter::Chunks<'a>,
218 current: &'a [u8],
219 pos: Pos,
220 len: usize,
221 offset: usize,
222}
223
224impl<'a> RopeyCursor<'a> {
225 #[must_use]
226 pub fn new(slice: RopeSlice<'a>) -> Self {
227 let iter = slice.chunks();
228 let mut res = Self {
229 current: &[],
230 iter,
231 pos: Pos::ChunkEnd,
232 len: slice.len(),
233 offset: 0,
234 };
235 res.advance();
236 res
237 }
238
239 #[must_use]
240 pub fn at(slice: RopeSlice<'a>, at: usize) -> Self {
241 let (iter, offset) = slice.chunks_at(at);
242 if offset == slice.len() {
243 let mut res = Self {
244 current: &[],
245 iter,
246 pos: Pos::ChunkStart,
247 len: slice.len(),
248 offset,
249 };
250 res.backtrack();
251 res
252 } else {
253 let mut res = Self {
254 current: &[],
255 iter,
256 pos: Pos::ChunkEnd,
257 len: slice.len(),
258 offset,
259 };
260 res.advance();
261 res
262 }
263 }
264}
265
266impl Cursor for RopeyCursor<'_> {
267 fn chunk(&self) -> &[u8] {
268 self.current
269 }
270
271 fn advance(&mut self) -> bool {
272 match self.pos {
273 Pos::ChunkStart => {
274 self.iter.next();
275 self.pos = Pos::ChunkEnd;
276 }
277 Pos::ChunkEnd => (),
278 }
279 for next in self.iter.by_ref() {
280 if next.is_empty() {
281 continue;
282 }
283 self.offset += self.current.len();
284 self.current = next.as_bytes();
285 return true;
286 }
287 false
288 }
289
290 fn backtrack(&mut self) -> bool {
291 match self.pos {
292 Pos::ChunkStart => {}
293 Pos::ChunkEnd => {
294 self.iter.prev();
295 self.pos = Pos::ChunkStart;
296 }
297 }
298 while let Some(prev) = self.iter.prev() {
299 if prev.is_empty() {
300 continue;
301 }
302 self.offset -= prev.len();
303 self.current = prev.as_bytes();
304 return true;
305 }
306 false
307 }
308
309 fn utf8_aware(&self) -> bool {
310 true
311 }
312
313 fn total_bytes(&self) -> Option<usize> {
314 Some(self.len)
315 }
316
317 fn offset(&self) -> usize {
318 self.offset
319 }
320}
321
322pub fn memchr<'a>(needle: u8, haystacks: impl IntoIterator<Item = &'a [u8]>) -> Option<usize> {
324 let mut haystacks_byte_index = 0;
325 for haystack in haystacks {
326 if let Some(needle_byte_index) =
327 memchr::memchr(needle, haystack).map(|i| haystacks_byte_index + i)
328 {
329 return Some(needle_byte_index);
330 }
331 haystacks_byte_index += haystack.len();
332 }
333 None
334}
335
336pub fn memrchr<'a>(needle: u8, haystacks: impl IntoIterator<Item = &'a [u8]>) -> Option<usize> {
347 let mut haystacks_byte_index = 0; for haystack in haystacks {
349 if let Some(needle_byte_index) = memchr::memrchr(needle, haystack)
350 .map(|i| haystacks_byte_index + (haystack.len() - (i + 1)))
351 {
352 return Some(needle_byte_index);
353 }
354 haystacks_byte_index += haystack.len();
355 }
356 None
357}
358
359pub fn memchr3<'a>(
361 needle1: u8,
362 needle2: u8,
363 needle3: u8,
364 haystacks: impl IntoIterator<Item = &'a [u8]>,
365) -> Option<usize> {
366 let mut haystacks_byte_index = 0;
367 for haystack in haystacks {
368 if let Some(needle_byte_index) =
369 memchr::memchr3(needle1, needle2, needle3, haystack).map(|i| haystacks_byte_index + i)
370 {
371 return Some(needle_byte_index);
372 }
373 haystacks_byte_index += haystack.len();
374 }
375 None
376}
377
378pub fn memrchr3<'a>(
380 needle1: u8,
381 needle2: u8,
382 needle3: u8,
383 haystacks: impl IntoIterator<Item = &'a [u8]>,
384) -> Option<usize> {
385 let mut haystacks_byte_index = 0; for haystack in haystacks {
387 if let Some(needle_byte_index) = memchr::memrchr3(needle1, needle2, needle3, haystack)
388 .map(|i| haystacks_byte_index + (haystack.len() - (i + 1)))
389 {
390 return Some(needle_byte_index);
391 }
392 haystacks_byte_index += haystack.len();
393 }
394 None
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400
401 #[test]
402 fn rope_length() {
403 let rope = Rope::default();
404 assert_eq!(rope.chars().count(), 0);
405 assert_eq!(rope.len_lines(LINE_TYPE), 1);
406 assert_eq!(rope.len_lines_unix(), 0);
407
408 let rope = Rope::from_str("x");
409 assert_eq!(rope.chars().count(), 1);
410 assert_eq!(rope.len_lines(LINE_TYPE), 1);
411 assert_eq!(rope.len_lines_unix(), 1);
412
413 let rope = Rope::from_str("\n");
414 assert_eq!(rope.chars().count(), 1);
415 assert_eq!(rope.len_lines(LINE_TYPE), 2);
416 assert_eq!(rope.len_lines_unix(), 1);
417
418 let rope = Rope::from_str("x\n");
419 assert_eq!(rope.chars().count(), 2);
420 assert_eq!(rope.len_lines(LINE_TYPE), 2);
421 assert_eq!(rope.len_lines_unix(), 1);
422
423 let rope = Rope::from_str("x\ny\nz");
424 assert_eq!(rope.chars().count(), 5);
425 assert_eq!(rope.len_lines(LINE_TYPE), 3);
426 assert_eq!(rope.len_lines_unix(), 3);
427
428 let rope = Rope::from_str("x\ny\nz\n");
429 assert_eq!(rope.chars().count(), 6);
430 assert_eq!(rope.len_lines(LINE_TYPE), 4);
431 assert_eq!(rope.len_lines_unix(), 3);
432 }
433
434 #[test]
435 fn rope_char() {
436 assert_eq!(Rope::default().get_char(0).ok(), None);
437 assert_eq!(Rope::default().get_char(1).ok(), None);
438 assert_eq!(Rope::from_str("xyz").get_char(0).ok(), Some('x'));
439 assert_eq!(Rope::from_str("xyz").get_char(1).ok(), Some('y'));
440 assert_eq!(Rope::from_str("xyz").get_char(2).ok(), Some('z'));
441 assert_eq!(Rope::from_str("xyz").get_char(3).ok(), None);
442 }
443
444 #[test]
445 fn rope_line() {
446 assert_eq!(Rope::default().get_line(0, LINE_TYPE), Some("".into()));
447 assert_eq!(Rope::from_str("x\ny\nz").get_line(9, LINE_TYPE), None);
448 assert_eq!(Rope::from_str("x").get_line(0, LINE_TYPE), Some("x".into()));
449 assert_eq!(
450 Rope::from_str("x\n").get_line(0, LINE_TYPE),
451 Some("x\n".into())
452 );
453 assert_eq!(
454 Rope::from_str("\nx").get_line(0, LINE_TYPE),
455 Some("\n".into())
456 );
457 assert_eq!(
458 Rope::from_str("\nx").get_line(1, LINE_TYPE),
459 Some("x".into())
460 );
461 assert_eq!(
462 Rope::from_str("x\ny\r\nz").get_line(0, LINE_TYPE),
463 Some("x\n".into())
464 );
465 assert_eq!(
466 Rope::from_str("x\ny\r\nz").get_line(1, LINE_TYPE),
467 Some("y\r\n".into())
468 );
469 assert_eq!(
470 Rope::from_str("x\ny\r\nz").get_line(2, LINE_TYPE),
471 Some("z".into())
472 );
473 }
474
475 #[test]
476 fn test_memchr() {
477 let haystacks = || ["foo", "bar", "baz", "qux"].iter().map(|s| s.as_bytes());
478 assert_eq!(memchr(b'0', haystacks()), None);
479 assert_eq!(memchr(b'f', haystacks()), Some(0));
480 assert_eq!(memchr(b'o', haystacks()), Some(1));
481 assert_eq!(memchr(b'a', haystacks()), Some(4));
482 assert_eq!(memchr(b'x', haystacks()), Some(11));
483 }
484
485 #[test]
486 fn test_memrchr() {
487 let haystacks = || {
488 ["foo", "bar", "baz", "qux"]
489 .iter()
490 .rev()
491 .map(|s| s.as_bytes())
492 };
493 assert_eq!(memrchr(b'0', haystacks()), None);
494 assert_eq!(memrchr(b'f', haystacks()), Some(11));
495 assert_eq!(memrchr(b'o', haystacks()), Some(9));
496 assert_eq!(memrchr(b'a', haystacks()), Some(4));
497 assert_eq!(memrchr(b'x', haystacks()), Some(0));
498 }
499
500 #[test]
501 fn test_find_next_byte() {
502 let mut rope = Rope::new();
503 while rope.chunks().count() < 10 {
504 rope.insert(rope.len(), "hello");
505 }
506 let mut length = rope.len();
507 let byte_index = length / 2;
508 rope.insert(byte_index, "!?");
509 length += "!?".len();
510 assert_eq!(rope.find_next_byte(.., b"!?"), Some(byte_index));
511 assert_eq!(
512 rope.find_next_byte(0..(length / 3) * 2, b"!?"),
513 Some(byte_index)
514 );
515 assert_eq!(rope.find_next_byte(0..length / 3, b"!?"), None);
516 assert_eq!(rope.find_next_byte(0..length, b"!o"), Some(4));
517 }
518
519 #[test]
521 fn test_find_prev_byte() {
522 let mut rope = Rope::new();
523 while rope.chunks().count() < 10 {
524 rope.insert(rope.len(), "hello");
525 }
526 let mut length = rope.len();
527 let byte_index = length / 2;
528 rope.insert(byte_index, "!");
529 length += "!".len();
530 assert_eq!(rope.find_prev_byte(.., b"!"), Some(byte_index));
531 assert_eq!(
532 rope.find_prev_byte(0..(length / 3) * 2, b"!"),
533 Some(byte_index)
534 );
535 assert_eq!(rope.find_prev_byte(0..length / 3, b"!"), None);
536 }
537
538 #[test]
539 fn graphemes() {
540 let a = |text: &str| {
541 Rope::from_str(text)
542 .graphemes()
543 .map(String::from)
544 .collect::<Vec<_>>()
545 };
546 let e = |graphemes: &'static [&str]| {
547 graphemes
548 .iter()
549 .copied()
550 .map(String::from)
551 .collect::<Vec<_>>()
552 };
553 assert_eq!(a(""), e(&[]));
554 assert_eq!(a("hello"), e(&["h", "e", "l", "l", "o"]));
555 assert_eq!(a("x\t\n🇯🇵👨👨👧"), e(&["x", "\t", "\n", "🇯🇵", "👨👨👧"]));
556 }
557
558 #[test]
559 fn test_floor_ceil_grapheme_boundary_with_emoji() {
560 let text = "👩🏻❤️💋👩🏻";
561 let rope = Rope::from_str(text);
562 let slice = rope.slice(..);
563 for byte_index in 0..=rope.len() {
564 let floor = floor_grapheme_boundary(&slice, byte_index);
566 let ceil = ceil_grapheme_boundary(&slice, byte_index);
567 assert!(
569 floor <= byte_index,
570 "floor={floor}, byte_index={byte_index}"
571 );
572 assert!(ceil >= byte_index, "ceil={ceil}, byte_index={byte_index}");
574 assert!(is_grapheme_boundary(&slice, floor));
576 assert!(is_grapheme_boundary(&slice, ceil));
577 }
578 }
579
580 #[test]
581 fn grapheme_boundary_rejects_non_char_boundary() {
582 let rope = Rope::from_str("5\u{e}Չ");
583 assert!(!is_grapheme_boundary(&rope.slice(..), 3));
584 }
585
586 #[test]
587 fn test_next_prev_grapheme_boundary_with_emoji() {
588 let text = "a👩🏻❤️💋👩🏻b";
589 let rope = Rope::from_str(text);
590 let slice = rope.slice(..);
591 let mut pos = 0;
592 let positions = std::iter::from_fn(|| {
593 let current = pos;
594 pos = next_grapheme_boundary(&slice, pos)?;
595 Some(current)
596 })
597 .collect::<Vec<_>>();
598 assert_eq!(positions.len(), 3);
600 for &pos in &positions {
602 assert!(is_grapheme_boundary(&slice, pos));
603 }
604 }
605}