rat_text/
grapheme.rs

1use crate::{Cursor, TextError, TextPosition};
2use ropey::iter::Chunks;
3use ropey::RopeSlice;
4use std::borrow::Cow;
5use std::cmp;
6use std::ops::Range;
7use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete};
8
9/// One grapheme.
10#[derive(Debug, PartialEq)]
11pub struct Grapheme<'a> {
12    /// grapheme
13    grapheme: Cow<'a, str>,
14    /// byte-range of the grapheme in the given slice.
15    text_bytes: Range<usize>,
16}
17
18impl<R: AsRef<str>> PartialEq<R> for Grapheme<'_> {
19    fn eq(&self, other: &R) -> bool {
20        self.grapheme.as_ref() == other.as_ref()
21    }
22}
23
24impl<'a> Grapheme<'a> {
25    pub fn new(grapheme: Cow<'a, str>, text_bytes: Range<usize>) -> Self {
26        Self {
27            grapheme,
28            text_bytes,
29        }
30    }
31
32    /// First (only) char of the grapheme is a whitespace.
33    pub fn is_whitespace(&self) -> bool {
34        self.grapheme
35            .chars()
36            .next()
37            .map(|v| v.is_whitespace())
38            .unwrap_or(false)
39    }
40
41    /// Is a linebreak.
42    pub fn is_line_break(&self) -> bool {
43        self.grapheme == "\n" || self.grapheme == "\r\n"
44    }
45
46    /// Get the grapheme.
47    pub fn grapheme(&'a self) -> &'a str {
48        self.grapheme.as_ref()
49    }
50
51    /// Get the byte-range as absolute range into the complete text.
52    pub fn text_bytes(&self) -> Range<usize> {
53        self.text_bytes.clone()
54    }
55}
56
57/// Data for rendering/mapping graphemes to screen coordinates.
58#[derive(Debug)]
59pub struct Glyph<'a> {
60    /// Display glyph.
61    glyph: Cow<'a, str>,
62    /// byte-range of the glyph in the given slice.
63    text_bytes: Range<usize>,
64    /// screen-position corrected by text_offset.
65    /// first visible column is at 0.
66    screen_pos: (u16, u16),
67    /// Display length for the glyph.
68    screen_width: u16,
69    /// text-position
70    pos: TextPosition,
71}
72
73impl<'a> Glyph<'a> {
74    pub fn new(
75        glyph: Cow<'a, str>,
76        text_bytes: Range<usize>,
77        screen_pos: (u16, u16),
78        screen_width: u16,
79        pos: TextPosition,
80    ) -> Self {
81        Self {
82            glyph,
83            text_bytes,
84            screen_pos,
85            screen_width,
86            pos,
87        }
88    }
89
90    /// Get the glyph.
91    pub fn glyph(&'a self) -> &'a str {
92        self.glyph.as_ref()
93    }
94
95    /// Get the byte-range as absolute range into the complete text.
96    pub fn text_bytes(&self) -> Range<usize> {
97        self.text_bytes.clone()
98    }
99
100    /// Get the position of the glyph
101    pub fn pos(&self) -> TextPosition {
102        self.pos
103    }
104
105    /// Get the screen position of the glyph.
106    pub fn screen_pos(&self) -> (u16, u16) {
107        self.screen_pos
108    }
109
110    /// Display width of the glyph.
111    pub fn screen_width(&self) -> u16 {
112        self.screen_width
113    }
114}
115
116/// A cursor over graphemes of a string.
117#[derive(Debug)]
118pub(crate) struct StrGraphemes<'a> {
119    text_offset: usize,
120    text: &'a str,
121    cursor: GraphemeCursor,
122}
123
124impl<'a> StrGraphemes<'a> {
125    /// Iterate the graphemes of a str-slice.
126    ///
127    /// * slice_offset - offset of the slice in the complete text.
128    /// * slice - slice
129    ///
130    pub(crate) fn new(slice_offset: usize, slice: &'a str) -> Self {
131        Self {
132            text_offset: slice_offset,
133            text: slice,
134            cursor: GraphemeCursor::new(0, slice.len(), true),
135        }
136    }
137
138    /// Iterate the graphemes of a str-slice.
139    ///
140    /// * slice_offset - offset of the slice in the complete text.
141    /// * slice - slice
142    /// * offset - relative offset into the slice
143    ///
144    pub(crate) fn new_offset(slice_offset: usize, slice: &'a str, offset: usize) -> Self {
145        Self {
146            text_offset: slice_offset,
147            text: slice,
148            cursor: GraphemeCursor::new(offset, slice.len(), true),
149        }
150    }
151}
152
153impl Cursor for StrGraphemes<'_> {
154    fn prev(&mut self) -> Option<Self::Item> {
155        let start = self.cursor.cur_cursor();
156        let prev = self.cursor.prev_boundary(self.text, 0).unwrap()?;
157        Some(Grapheme {
158            grapheme: Cow::Borrowed(&self.text[prev..start]),
159            text_bytes: self.text_offset + prev..self.text_offset + start,
160        })
161    }
162
163    fn rev_cursor(self) -> impl Cursor<Item = Self::Item> {
164        RevStrGraphemes { it: self }
165    }
166
167    fn text_offset(&self) -> usize {
168        self.text_offset + self.cursor.cur_cursor()
169    }
170}
171
172impl<'a> Iterator for StrGraphemes<'a> {
173    type Item = Grapheme<'a>;
174
175    #[inline]
176    fn next(&mut self) -> Option<Grapheme<'a>> {
177        let start = self.cursor.cur_cursor();
178        let next = self.cursor.next_boundary(self.text, 0).unwrap()?;
179        Some(Grapheme {
180            grapheme: Cow::Borrowed(&self.text[start..next]),
181            text_bytes: self.text_offset + start..self.text_offset + next,
182        })
183    }
184
185    #[inline]
186    fn size_hint(&self) -> (usize, Option<usize>) {
187        let slen = self.text.len() - self.cursor.cur_cursor();
188        (cmp::min(slen, 1), Some(slen))
189    }
190}
191
192#[derive(Debug)]
193pub(crate) struct RevStrGraphemes<'a> {
194    it: StrGraphemes<'a>,
195}
196
197impl<'a> Iterator for RevStrGraphemes<'a> {
198    type Item = Grapheme<'a>;
199
200    #[inline]
201    fn next(&mut self) -> Option<Self::Item> {
202        self.it.prev()
203    }
204}
205
206impl Cursor for RevStrGraphemes<'_> {
207    #[inline]
208    fn prev(&mut self) -> Option<Self::Item> {
209        self.it.next()
210    }
211
212    #[inline]
213    fn rev_cursor(self) -> impl Cursor<Item = Self::Item> {
214        self.it
215    }
216
217    fn text_offset(&self) -> usize {
218        self.it.text_offset()
219    }
220}
221
222/// An implementation of a graphemes iterator, for iterating over
223/// the graphemes of a RopeSlice.
224#[derive(Debug)]
225pub(crate) struct RopeGraphemes<'a> {
226    text_offset: usize,
227    text: RopeSlice<'a>,
228    chunks: Chunks<'a>,
229    was_next: Option<bool>,
230    cur_chunk: &'a str,
231    cur_chunk_start: usize,
232    cursor: GraphemeCursor,
233}
234
235impl<'a> RopeGraphemes<'a> {
236    /// New grapheme iterator.
237    ///
238    /// * slice_offset - offset of the slice in the complete text.
239    /// * slice - slice of the complete text
240    pub(crate) fn new(slice_offset: usize, slice: RopeSlice<'a>) -> RopeGraphemes<'a> {
241        let mut chunks = slice.chunks();
242
243        // was_next is only useful, if there was a true next().
244        // otherwise it confuses the algorithm.
245        let (first_chunk, was_next) = match chunks.next() {
246            Some(v) => (v, Some(true)),
247            None => ("", None),
248        };
249
250        RopeGraphemes {
251            text_offset: slice_offset,
252            text: slice,
253            chunks,
254            was_next,
255            cur_chunk: first_chunk,
256            cur_chunk_start: 0,
257            cursor: GraphemeCursor::new(0, slice.len_bytes(), true),
258        }
259    }
260
261    /// New grapheme iterator.
262    ///
263    /// * slice_offset - offset of the slice in the complete text.
264    /// * slice - slice of the complete text
265    /// * offset - relative offset into the slice
266    ///
267    /// Offset must be a valid char boundary.
268    pub(crate) fn new_offset(
269        slice_offset: usize,
270        slice: RopeSlice<'a>,
271        offset: usize,
272    ) -> Result<RopeGraphemes<'a>, TextError> {
273        let Some((mut chunks, chunk_start, _, _)) = slice.get_chunks_at_byte(offset) else {
274            return Err(TextError::ByteIndexOutOfBounds(offset, slice.len_bytes()));
275        };
276
277        // was_next is only useful, if there was a true next().
278        // otherwise it confuses the algorithm.
279        let (first_chunk, was_next) = match chunks.next() {
280            Some(v) => (v, Some(true)),
281            None => ("", None),
282        };
283
284        Ok(RopeGraphemes {
285            text_offset: slice_offset,
286            text: slice,
287            chunks,
288            was_next,
289            cur_chunk: first_chunk,
290            cur_chunk_start: chunk_start,
291            cursor: GraphemeCursor::new(offset, slice.len_bytes(), true),
292        })
293    }
294}
295
296impl<'a> Cursor for RopeGraphemes<'a> {
297    fn prev(&mut self) -> Option<Grapheme<'a>> {
298        let a = self.cursor.cur_cursor();
299        let b;
300        loop {
301            match self
302                .cursor
303                .prev_boundary(self.cur_chunk, self.cur_chunk_start)
304            {
305                Ok(None) => {
306                    return None;
307                }
308                Ok(Some(n)) => {
309                    b = n;
310                    break;
311                }
312                Err(GraphemeIncomplete::PrevChunk) => {
313                    if self.was_next == Some(true) {
314                        // skip current
315                        self.chunks.prev();
316                    }
317                    (self.cur_chunk, self.was_next) = match self.chunks.prev() {
318                        Some(v) => (v, Some(false)),
319                        None => ("", None),
320                    };
321                    self.cur_chunk_start -= self.cur_chunk.len();
322                }
323                Err(GraphemeIncomplete::PreContext(idx)) => {
324                    let (chunk, byte_idx, _, _) = self.text.chunk_at_byte(idx.saturating_sub(1));
325                    self.cursor.provide_context(chunk, byte_idx);
326                }
327                _ => unreachable!(),
328            }
329        }
330
331        if a >= self.cur_chunk_start + self.cur_chunk.len() {
332            let a_char = self.text.byte_to_char(a);
333            let b_char = self.text.byte_to_char(b);
334
335            Some(Grapheme {
336                grapheme: Cow::Owned(self.text.slice(b_char..a_char).to_string()),
337                text_bytes: self.text_offset + b..self.text_offset + a,
338            })
339        } else {
340            let a2 = a - self.cur_chunk_start;
341            let b2 = b - self.cur_chunk_start;
342            Some(Grapheme {
343                grapheme: Cow::Borrowed(&self.cur_chunk[b2..a2]),
344                text_bytes: self.text_offset + b..self.text_offset + a,
345            })
346        }
347    }
348
349    #[inline]
350    fn rev_cursor(self) -> impl Cursor<Item = Self::Item> {
351        RevRopeGraphemes { it: self }
352    }
353
354    fn text_offset(&self) -> usize {
355        self.text_offset + self.cursor.cur_cursor()
356    }
357}
358
359impl<'a> Iterator for RopeGraphemes<'a> {
360    type Item = Grapheme<'a>;
361
362    fn next(&mut self) -> Option<Grapheme<'a>> {
363        let a = self.cursor.cur_cursor();
364        let b;
365        loop {
366            match self
367                .cursor
368                .next_boundary(self.cur_chunk, self.cur_chunk_start)
369            {
370                Ok(None) => {
371                    return None;
372                }
373                Ok(Some(n)) => {
374                    b = n;
375                    break;
376                }
377                Err(GraphemeIncomplete::NextChunk) => {
378                    self.cur_chunk_start += self.cur_chunk.len();
379                    if self.was_next == Some(false) {
380                        // skip current
381                        self.chunks.next();
382                    }
383                    (self.cur_chunk, self.was_next) = match self.chunks.next() {
384                        Some(v) => (v, Some(true)),
385                        None => ("", None),
386                    };
387                }
388                Err(GraphemeIncomplete::PreContext(idx)) => {
389                    let (chunk, byte_idx, _, _) = self.text.chunk_at_byte(idx.saturating_sub(1));
390                    self.cursor.provide_context(chunk, byte_idx);
391                }
392                _ => unreachable!(),
393            }
394        }
395
396        if a < self.cur_chunk_start {
397            let a_char = self.text.byte_to_char(a);
398            let b_char = self.text.byte_to_char(b);
399
400            Some(Grapheme {
401                grapheme: Cow::Owned(self.text.slice(a_char..b_char).to_string()),
402                text_bytes: self.text_offset + a..self.text_offset + b,
403            })
404        } else {
405            let a2 = a - self.cur_chunk_start;
406            let b2 = b - self.cur_chunk_start;
407            Some(Grapheme {
408                grapheme: Cow::Borrowed(&self.cur_chunk[a2..b2]),
409                text_bytes: self.text_offset + a..self.text_offset + b,
410            })
411        }
412    }
413}
414
415#[derive(Debug)]
416pub(crate) struct RevRopeGraphemes<'a> {
417    it: RopeGraphemes<'a>,
418}
419
420impl<'a> Iterator for RevRopeGraphemes<'a> {
421    type Item = Grapheme<'a>;
422
423    #[inline]
424    fn next(&mut self) -> Option<Self::Item> {
425        self.it.prev()
426    }
427}
428
429impl Cursor for RevRopeGraphemes<'_> {
430    #[inline]
431    fn prev(&mut self) -> Option<Self::Item> {
432        self.it.next()
433    }
434
435    #[inline]
436    fn rev_cursor(self) -> impl Cursor<Item = Self::Item> {
437        self.it
438    }
439
440    fn text_offset(&self) -> usize {
441        self.it.text_offset()
442    }
443}
444
445/// Iterates over the glyphs of a row-range.
446///
447/// Keeps track of the text-position and the display-position on screen.
448/// Does a conversion from graphemes to glyph-text and glyph-width.
449///
450/// This is used for rendering text, and for mapping text-positions
451/// to screen-positions and vice versa.
452#[derive(Debug)]
453pub(crate) struct GlyphIter<Iter> {
454    iter: Iter,
455
456    pos: TextPosition,
457
458    screen_offset: u16,
459    screen_width: u16,
460    screen_pos: (u16, u16),
461
462    tabs: u16,
463    show_ctrl: bool,
464    line_break: bool,
465}
466
467impl<'a, Iter> GlyphIter<Iter>
468where
469    Iter: Iterator<Item = Grapheme<'a>>,
470{
471    /// New iterator.
472    pub(crate) fn new(pos: TextPosition, iter: Iter) -> Self {
473        Self {
474            iter,
475            pos,
476            screen_offset: 0,
477            screen_width: u16::MAX,
478            screen_pos: Default::default(),
479            tabs: 8,
480            show_ctrl: false,
481            line_break: true,
482        }
483    }
484
485    /// Screen offset.
486    pub(crate) fn set_screen_offset(&mut self, offset: u16) {
487        self.screen_offset = offset;
488    }
489
490    /// Screen width.
491    pub(crate) fn set_screen_width(&mut self, width: u16) {
492        self.screen_width = width;
493    }
494
495    /// Tab width
496    pub(crate) fn set_tabs(&mut self, tabs: u16) {
497        self.tabs = tabs;
498    }
499
500    /// Handle line-breaks. If false everything is treated as one line.
501    pub(crate) fn set_line_break(&mut self, line_break: bool) {
502        self.line_break = line_break;
503    }
504
505    /// Show ASCII control codes.
506    pub(crate) fn set_show_ctrl(&mut self, show_ctrl: bool) {
507        self.show_ctrl = show_ctrl;
508    }
509}
510
511impl<'a, Iter> Iterator for GlyphIter<Iter>
512where
513    Iter: Iterator<Item = Grapheme<'a>>,
514{
515    type Item = Glyph<'a>;
516
517    fn next(&mut self) -> Option<Self::Item> {
518        for grapheme in self.iter.by_ref() {
519            let glyph;
520            let len: u16;
521            let mut lbrk = false;
522
523            // todo: maybe add some ligature support.
524
525            match grapheme.grapheme.as_ref() {
526                "\n" | "\r\n" if self.line_break => {
527                    lbrk = true;
528                    len = if self.show_ctrl { 1 } else { 0 };
529                    glyph = Cow::Borrowed(if self.show_ctrl { "\u{2424}" } else { "" });
530                }
531                "\n" | "\r\n" if !self.line_break => {
532                    lbrk = false;
533                    len = 1;
534                    glyph = Cow::Borrowed("\u{2424}");
535                }
536                "\t" => {
537                    len = self.tabs - (self.screen_pos.0 % self.tabs);
538                    glyph = Cow::Borrowed(if self.show_ctrl { "\u{2409}" } else { " " });
539                }
540                c if ("\x00".."\x20").contains(&c) => {
541                    static CCHAR: [&str; 32] = [
542                        "\u{2400}", "\u{2401}", "\u{2402}", "\u{2403}", "\u{2404}", "\u{2405}",
543                        "\u{2406}", "\u{2407}", "\u{2408}", "\u{2409}", "\u{240A}", "\u{240B}",
544                        "\u{240C}", "\u{240D}", "\u{240E}", "\u{240F}", "\u{2410}", "\u{2411}",
545                        "\u{2412}", "\u{2413}", "\u{2414}", "\u{2415}", "\u{2416}", "\u{2417}",
546                        "\u{2418}", "\u{2419}", "\u{241A}", "\u{241B}", "\u{241C}", "\u{241D}",
547                        "\u{241E}", "\u{241F}",
548                    ];
549                    let c0 = c.bytes().next().expect("byte");
550                    len = 1;
551                    glyph = Cow::Borrowed(if self.show_ctrl {
552                        CCHAR[c0 as usize]
553                    } else {
554                        "\u{FFFD}"
555                    });
556                }
557                c => {
558                    len = unicode_display_width::width(c) as u16;
559                    glyph = grapheme.grapheme;
560                }
561            }
562
563            let pos = self.pos;
564            let screen_pos = self.screen_pos;
565
566            if lbrk {
567                self.screen_pos.0 = 0;
568                self.screen_pos.1 += 1;
569                self.pos.x = 0;
570                self.pos.y += 1;
571            } else {
572                self.screen_pos.0 += len;
573                self.pos.x += 1;
574            }
575
576            // clip left
577            if screen_pos.0 < self.screen_offset {
578                if screen_pos.0 + len > self.screen_offset {
579                    // don't show partial glyphs, but show the space they need.
580                    // avoids flickering when scrolling left/right.
581                    return Some(Glyph {
582                        glyph: Cow::Borrowed("\u{2203}"),
583                        text_bytes: grapheme.text_bytes,
584                        screen_width: screen_pos.0 + len - self.screen_offset,
585                        pos,
586                        screen_pos: (0, screen_pos.1),
587                    });
588                } else {
589                    // out left
590                }
591            } else if screen_pos.0 + len > self.screen_offset + self.screen_width {
592                if screen_pos.0 < self.screen_offset + self.screen_width {
593                    // don't show partial glyphs, but show the space they need.
594                    // avoids flickering when scrolling left/right.
595                    return Some(Glyph {
596                        glyph: Cow::Borrowed("\u{2203}"),
597                        text_bytes: grapheme.text_bytes,
598                        screen_width: screen_pos.0 + len - (self.screen_offset + self.screen_width),
599                        pos,
600                        screen_pos: (screen_pos.0 - self.screen_offset, screen_pos.1),
601                    });
602                } else {
603                    // out right
604                    if !self.line_break {
605                        break;
606                    }
607                }
608            } else {
609                return Some(Glyph {
610                    glyph,
611                    text_bytes: grapheme.text_bytes,
612                    screen_width: len,
613                    pos,
614                    screen_pos: (screen_pos.0 - self.screen_offset, screen_pos.1),
615                });
616            }
617        }
618
619        None
620    }
621}
622
623#[cfg(test)]
624mod test_str {
625    use crate::grapheme::StrGraphemes;
626    use crate::Cursor;
627
628    #[test]
629    fn test_str_graphemes1() {
630        // basic graphemes
631        let s = String::from("qwertz");
632
633        let mut s0 = StrGraphemes::new(0, &s);
634        assert_eq!(s0.next().unwrap(), "q");
635        assert_eq!(s0.next().unwrap(), "w");
636        assert_eq!(s0.next().unwrap(), "e");
637        assert_eq!(s0.next().unwrap(), "r");
638        assert_eq!(s0.next().unwrap(), "t");
639        assert_eq!(s0.next().unwrap(), "z");
640        assert!(s0.next().is_none());
641        assert_eq!(s0.prev().unwrap(), "z");
642        assert_eq!(s0.prev().unwrap(), "t");
643        assert_eq!(s0.prev().unwrap(), "r");
644        assert_eq!(s0.prev().unwrap(), "e");
645        assert_eq!(s0.prev().unwrap(), "w");
646        assert_eq!(s0.prev().unwrap(), "q");
647
648        let mut s0 = StrGraphemes::new(1, &s[1..s.len() - 1]);
649        assert_eq!(s0.next().unwrap(), "w");
650        assert_eq!(s0.next().unwrap(), "e");
651        assert_eq!(s0.next().unwrap(), "r");
652        assert_eq!(s0.next().unwrap(), "t");
653        assert!(s0.next().is_none());
654        assert_eq!(s0.prev().unwrap(), "t");
655        assert_eq!(s0.prev().unwrap(), "r");
656        assert_eq!(s0.prev().unwrap(), "e");
657        assert_eq!(s0.prev().unwrap(), "w");
658
659        let mut s0 = StrGraphemes::new(3, &s[3..3]);
660        assert!(s0.next().is_none());
661        assert!(s0.prev().is_none());
662    }
663
664    #[test]
665    fn test_str_graphemes2() {
666        // complicated graphemes
667        let s = String::from("w🤷‍♂️xw🤷‍♀️xw🤦‍♂️xw❤️xw🤦‍♀️xw💕🙍🏿‍♀️x");
668
669        let mut s0 = StrGraphemes::new(0, &s);
670        assert_eq!(s0.next().unwrap(), "w");
671        assert_eq!(s0.next().unwrap(), "🤷‍♂️");
672        assert_eq!(s0.next().unwrap(), "x");
673        assert_eq!(s0.next().unwrap(), "w");
674        assert_eq!(s0.next().unwrap(), "🤷‍♀️");
675        assert_eq!(s0.next().unwrap(), "x");
676        assert_eq!(s0.next().unwrap(), "w");
677        assert_eq!(s0.next().unwrap(), "🤦‍♂️");
678        assert_eq!(s0.next().unwrap(), "x");
679        assert_eq!(s0.next().unwrap(), "w");
680        assert_eq!(s0.next().unwrap(), "❤️");
681        assert_eq!(s0.next().unwrap(), "x");
682        assert_eq!(s0.next().unwrap(), "w");
683        assert_eq!(s0.next().unwrap(), "🤦‍♀️");
684        assert_eq!(s0.next().unwrap(), "x");
685        assert_eq!(s0.next().unwrap(), "w");
686        assert_eq!(s0.next().unwrap(), "💕");
687        assert_eq!(s0.next().unwrap(), "🙍🏿‍♀️");
688        assert_eq!(s0.next().unwrap(), "x");
689        assert!(s0.next().is_none());
690        assert_eq!(s0.prev().unwrap(), "x");
691        assert_eq!(s0.prev().unwrap(), "🙍🏿‍♀️");
692        assert_eq!(s0.prev().unwrap(), "💕");
693        assert_eq!(s0.prev().unwrap(), "w");
694        assert_eq!(s0.prev().unwrap(), "x");
695        assert_eq!(s0.prev().unwrap(), "🤦‍♀️");
696        assert_eq!(s0.prev().unwrap(), "w");
697        assert_eq!(s0.prev().unwrap(), "x");
698        assert_eq!(s0.prev().unwrap(), "❤️");
699        assert_eq!(s0.prev().unwrap(), "w");
700        assert_eq!(s0.prev().unwrap(), "x");
701        assert_eq!(s0.prev().unwrap(), "🤦‍♂️");
702        assert_eq!(s0.prev().unwrap(), "w");
703        assert_eq!(s0.prev().unwrap(), "x");
704        assert_eq!(s0.prev().unwrap(), "🤷‍♀️");
705        assert_eq!(s0.prev().unwrap(), "w");
706        assert_eq!(s0.prev().unwrap(), "x");
707        assert_eq!(s0.prev().unwrap(), "🤷‍♂️");
708        assert_eq!(s0.prev().unwrap(), "w");
709    }
710
711    #[test]
712    fn test_str_graphemes3() {
713        // complicated slices
714        let s = String::from("qwertz");
715        let mut s0 = StrGraphemes::new_offset(0, &s, 3);
716        assert_eq!(s0.next().unwrap(), "r");
717        assert_eq!(s0.prev().unwrap(), "r");
718        assert_eq!(s0.prev().unwrap(), "e");
719
720        let mut s0 = StrGraphemes::new_offset(0, &s, 3);
721        assert_eq!(s0.next().unwrap().text_bytes(), 3..4);
722        assert_eq!(s0.prev().unwrap().text_bytes(), 3..4);
723        assert_eq!(s0.prev().unwrap().text_bytes(), 2..3);
724
725        let s = String::from("w🤷‍♂️🤷‍♀️🤦‍♂️❤️🤦‍♀️💕🙍🏿‍♀️x");
726        let mut s0 = StrGraphemes::new_offset(0, &s, 21);
727        assert_eq!(s0.next().unwrap(), "♀\u{fe0f}");
728        assert_eq!(s0.next().unwrap(), "🤦\u{200d}♂\u{fe0f}");
729        assert_eq!(s0.prev().unwrap(), "🤦\u{200d}♂\u{fe0f}");
730        assert_eq!(s0.prev().unwrap(), "🤷\u{200d}♀\u{fe0f}");
731
732        let s = String::from("w🤷‍♂️🤷‍♀️🤦‍♂️❤️🤦‍♀️💕🙍🏿‍♀️x");
733        let mut s0 = StrGraphemes::new_offset(0, &s, 21);
734        assert_eq!(s0.next().unwrap().text_bytes(), 21..27);
735        assert_eq!(s0.next().unwrap().text_bytes(), 27..40);
736        assert_eq!(s0.prev().unwrap().text_bytes(), 27..40);
737        assert_eq!(s0.prev().unwrap().text_bytes(), 14..27);
738    }
739
740    #[test]
741    fn test_str_graphemes4() {
742        // offsets and partial slices
743        let s = String::from("qwertz");
744        let mut s0 = StrGraphemes::new_offset(1, &s[1..5], 2);
745        s0.next();
746        assert_eq!(s0.text_offset(), 4);
747        s0.next();
748        assert_eq!(s0.text_offset(), 5);
749        s0.next();
750        assert_eq!(s0.text_offset(), 5);
751        s0.next();
752        assert_eq!(s0.text_offset(), 5);
753        s0.prev();
754        assert_eq!(s0.text_offset(), 4);
755        s0.prev();
756        assert_eq!(s0.text_offset(), 3);
757        s0.prev();
758        assert_eq!(s0.text_offset(), 2);
759        s0.prev();
760        assert_eq!(s0.text_offset(), 1);
761        s0.prev();
762        assert_eq!(s0.text_offset(), 1);
763    }
764
765    #[test]
766    fn test_str_graphemes5() {
767        // offsets and partial slices
768        let s = String::from("qwertz");
769        let mut s0 = StrGraphemes::new_offset(1, &s[1..5], 2).rev_cursor();
770        assert_eq!(s0.next().unwrap(), "e");
771        assert_eq!(s0.text_offset(), 2);
772
773        assert_eq!(s0.next().unwrap(), "w");
774        assert_eq!(s0.text_offset(), 1);
775
776        assert_eq!(s0.prev().unwrap(), "w");
777        assert_eq!(s0.text_offset(), 2);
778
779        assert_eq!(s0.prev().unwrap(), "e");
780        assert_eq!(s0.text_offset(), 3);
781
782        assert_eq!(s0.prev().unwrap(), "r");
783        assert_eq!(s0.text_offset(), 4);
784
785        assert_eq!(s0.prev().unwrap(), "t");
786        assert_eq!(s0.text_offset(), 5);
787    }
788}
789
790#[cfg(test)]
791mod test_rope {
792    use crate::grapheme::RopeGraphemes;
793    use crate::Cursor;
794    use ropey::Rope;
795
796    #[test]
797    fn test_rope_graphemes1() {
798        // basic graphemes
799        let s = Rope::from("qwertz");
800
801        let mut s0 = RopeGraphemes::new(0, s.byte_slice(..));
802        assert_eq!(s0.next().unwrap(), "q");
803        assert_eq!(s0.next().unwrap(), "w");
804        assert_eq!(s0.next().unwrap(), "e");
805        assert_eq!(s0.next().unwrap(), "r");
806        assert_eq!(s0.next().unwrap(), "t");
807        assert_eq!(s0.next().unwrap(), "z");
808        assert!(s0.next().is_none());
809        assert_eq!(s0.prev().unwrap(), "z");
810        assert_eq!(s0.prev().unwrap(), "t");
811        assert_eq!(s0.prev().unwrap(), "r");
812        assert_eq!(s0.prev().unwrap(), "e");
813        assert_eq!(s0.prev().unwrap(), "w");
814        assert_eq!(s0.prev().unwrap(), "q");
815
816        let mut s0 = RopeGraphemes::new(1, s.byte_slice(1..s.len_bytes() - 1));
817        assert_eq!(s0.next().unwrap(), "w");
818        assert_eq!(s0.next().unwrap(), "e");
819        assert_eq!(s0.next().unwrap(), "r");
820        assert_eq!(s0.next().unwrap(), "t");
821        assert!(s0.next().is_none());
822        assert_eq!(s0.prev().unwrap(), "t");
823        assert_eq!(s0.prev().unwrap(), "r");
824        assert_eq!(s0.prev().unwrap(), "e");
825        assert_eq!(s0.prev().unwrap(), "w");
826
827        let mut s0 = RopeGraphemes::new(3, s.byte_slice(3..3));
828        assert!(s0.next().is_none());
829        assert!(s0.prev().is_none());
830    }
831
832    #[test]
833    fn test_rope_graphemes2() {
834        // complicated graphemes
835        let s = Rope::from("w🤷‍♂️xw🤷‍♀️xw🤦‍♂️xw❤️xw🤦‍♀️xw💕🙍🏿‍♀️x");
836
837        let mut s0 = RopeGraphemes::new(0, s.byte_slice(..));
838        assert_eq!(s0.next().unwrap(), "w");
839        assert_eq!(s0.next().unwrap(), "🤷‍♂️");
840        assert_eq!(s0.next().unwrap(), "x");
841        assert_eq!(s0.next().unwrap(), "w");
842        assert_eq!(s0.next().unwrap(), "🤷‍♀️");
843        assert_eq!(s0.next().unwrap(), "x");
844        assert_eq!(s0.next().unwrap(), "w");
845        assert_eq!(s0.next().unwrap(), "🤦‍♂️");
846        assert_eq!(s0.next().unwrap(), "x");
847        assert_eq!(s0.next().unwrap(), "w");
848        assert_eq!(s0.next().unwrap(), "❤️");
849        assert_eq!(s0.next().unwrap(), "x");
850        assert_eq!(s0.next().unwrap(), "w");
851        assert_eq!(s0.next().unwrap(), "🤦‍♀️");
852        assert_eq!(s0.next().unwrap(), "x");
853        assert_eq!(s0.next().unwrap(), "w");
854        assert_eq!(s0.next().unwrap(), "💕");
855        assert_eq!(s0.next().unwrap(), "🙍🏿‍♀️");
856        assert_eq!(s0.next().unwrap(), "x");
857        assert!(s0.next().is_none());
858        assert_eq!(s0.prev().unwrap(), "x");
859        assert_eq!(s0.prev().unwrap(), "🙍🏿‍♀️");
860        assert_eq!(s0.prev().unwrap(), "💕");
861        assert_eq!(s0.prev().unwrap(), "w");
862        assert_eq!(s0.prev().unwrap(), "x");
863        assert_eq!(s0.prev().unwrap(), "🤦‍♀️");
864        assert_eq!(s0.prev().unwrap(), "w");
865        assert_eq!(s0.prev().unwrap(), "x");
866        assert_eq!(s0.prev().unwrap(), "❤️");
867        assert_eq!(s0.prev().unwrap(), "w");
868        assert_eq!(s0.prev().unwrap(), "x");
869        assert_eq!(s0.prev().unwrap(), "🤦‍♂️");
870        assert_eq!(s0.prev().unwrap(), "w");
871        assert_eq!(s0.prev().unwrap(), "x");
872        assert_eq!(s0.prev().unwrap(), "🤷‍♀️");
873        assert_eq!(s0.prev().unwrap(), "w");
874        assert_eq!(s0.prev().unwrap(), "x");
875        assert_eq!(s0.prev().unwrap(), "🤷‍♂️");
876        assert_eq!(s0.prev().unwrap(), "w");
877    }
878
879    #[test]
880    fn test_rope_graphemes3() {
881        // complicated graphemes
882        let s = Rope::from("qwertz");
883        let mut s0 = RopeGraphemes::new_offset(0, s.byte_slice(..), 3).expect("fine");
884        assert_eq!(s0.next().unwrap(), "r");
885        assert_eq!(s0.prev().unwrap(), "r");
886        assert_eq!(s0.prev().unwrap(), "e");
887
888        let mut s0 = RopeGraphemes::new_offset(0, s.byte_slice(..), 3).expect("fine");
889        assert_eq!(s0.next().unwrap().text_bytes(), 3..4);
890        assert_eq!(s0.prev().unwrap().text_bytes(), 3..4);
891        assert_eq!(s0.prev().unwrap().text_bytes(), 2..3);
892
893        let s = Rope::from("w🤷‍♂️🤷‍♀️🤦‍♂️❤️🤦‍♀️💕🙍🏿‍♀️x");
894        let mut s0 = RopeGraphemes::new_offset(0, s.byte_slice(..), 21).expect("fine");
895        assert_eq!(s0.next().unwrap(), "♀\u{fe0f}");
896        assert_eq!(s0.next().unwrap(), "🤦\u{200d}♂\u{fe0f}");
897        assert_eq!(s0.prev().unwrap(), "🤦\u{200d}♂\u{fe0f}");
898        assert_eq!(s0.prev().unwrap(), "🤷\u{200d}♀\u{fe0f}");
899
900        let s = Rope::from("w🤷‍♂️🤷‍♀️🤦‍♂️❤️🤦‍♀️💕🙍🏿‍♀️x");
901        let mut s0 = RopeGraphemes::new_offset(0, s.byte_slice(..), 21).expect("fine");
902        assert_eq!(s0.next().unwrap().text_bytes(), 21..27);
903        assert_eq!(s0.next().unwrap().text_bytes(), 27..40);
904        assert_eq!(s0.prev().unwrap().text_bytes(), 27..40);
905        assert_eq!(s0.prev().unwrap().text_bytes(), 14..27);
906    }
907
908    #[test]
909    fn test_rope_graphemes4() {
910        // offsets and partial slices
911        let s = Rope::from("qwertz");
912        let mut s0 = RopeGraphemes::new_offset(1, s.byte_slice(1..5), 2).expect("fine");
913        s0.next();
914        assert_eq!(s0.text_offset(), 4);
915        s0.next();
916        assert_eq!(s0.text_offset(), 5);
917        s0.next();
918        assert_eq!(s0.text_offset(), 5);
919        s0.next();
920        assert_eq!(s0.text_offset(), 5);
921        s0.prev();
922        assert_eq!(s0.text_offset(), 4);
923        s0.prev();
924        assert_eq!(s0.text_offset(), 3);
925        s0.prev();
926        assert_eq!(s0.text_offset(), 2);
927        s0.prev();
928        assert_eq!(s0.text_offset(), 1);
929        s0.prev();
930        assert_eq!(s0.text_offset(), 1);
931    }
932
933    #[test]
934    fn test_rope_graphemes5() {
935        // offsets and partial slices
936        let s = Rope::from("qwertz");
937        let mut s0 = RopeGraphemes::new_offset(1, s.byte_slice(1..5), 2)
938            .expect("fine")
939            .rev_cursor();
940        assert_eq!(s0.next().unwrap(), "e");
941        assert_eq!(s0.text_offset(), 2);
942
943        assert_eq!(s0.next().unwrap(), "w");
944        assert_eq!(s0.text_offset(), 1);
945
946        assert_eq!(s0.prev().unwrap(), "w");
947        assert_eq!(s0.text_offset(), 2);
948
949        assert_eq!(s0.prev().unwrap(), "e");
950        assert_eq!(s0.text_offset(), 3);
951
952        assert_eq!(s0.prev().unwrap(), "r");
953        assert_eq!(s0.text_offset(), 4);
954
955        assert_eq!(s0.prev().unwrap(), "t");
956        assert_eq!(s0.text_offset(), 5);
957    }
958
959    #[test]
960    fn test_rope_graphemes6() {
961        // text rope boundary
962        let s = Rope::from(
963            "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
964             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
965             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
966             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
967             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
968             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
969             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
970             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
971             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
972             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
973             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
974             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
975             "
976        );
977        assert_eq!(s.len_bytes(), 1200);
978        let mut s0 = RopeGraphemes::new_offset(1, s.byte_slice(1..1199), 0).expect("fine");
979        assert_eq!(s0.nth(598).unwrap(), "J");
980
981        assert_eq!(s0.next().unwrap(), "0");
982        assert_eq!(s0.text_offset(), 601);
983        assert_eq!(s0.next().unwrap(), "1");
984        assert_eq!(s0.text_offset(), 602);
985        assert_eq!(s0.prev().unwrap(), "1");
986        assert_eq!(s0.text_offset(), 601);
987        assert_eq!(s0.prev().unwrap(), "0");
988        assert_eq!(s0.text_offset(), 600);
989        assert_eq!(s0.prev().unwrap(), "J");
990        assert_eq!(s0.text_offset(), 599);
991    }
992
993    #[test]
994    fn test_rope_graphemes7() {
995        // test complicated grapheme at rope boundary
996        let s = Rope::from(
997            "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
998             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
999             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
1000             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
1001             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
1002             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghi🤷‍♂️\
1003             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
1004             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
1005             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
1006             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
1007             012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678)\
1008             abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghiJ\
1009             "
1010        );
1011        assert_eq!(s.len_bytes(), 1212);
1012        assert_eq!(s.chunks().next().unwrap().len(), 606);
1013        let mut s0 = RopeGraphemes::new_offset(1, s.byte_slice(1..1199), 0).expect("fine");
1014        assert_eq!(s0.nth(598).unwrap(), "🤷‍♂️");
1015
1016        assert_eq!(s0.next().unwrap(), "0");
1017        assert_eq!(s0.text_offset(), 613);
1018        assert_eq!(s0.next().unwrap(), "1");
1019        assert_eq!(s0.text_offset(), 614);
1020        assert_eq!(s0.prev().unwrap(), "1");
1021        assert_eq!(s0.text_offset(), 613);
1022        assert_eq!(s0.prev().unwrap(), "0");
1023        assert_eq!(s0.text_offset(), 612);
1024        assert_eq!(s0.prev().unwrap(), "🤷‍♂️");
1025        assert_eq!(s0.text_offset(), 599);
1026        assert_eq!(s0.prev().unwrap(), "i");
1027        assert_eq!(s0.text_offset(), 598);
1028
1029        assert_eq!(s0.next().unwrap(), "i");
1030        assert_eq!(s0.text_offset(), 599);
1031        assert_eq!(s0.next().unwrap(), "🤷‍♂️");
1032        assert_eq!(s0.text_offset(), 612);
1033        assert_eq!(s0.next().unwrap(), "0");
1034        assert_eq!(s0.text_offset(), 613);
1035        assert_eq!(s0.next().unwrap(), "1");
1036        assert_eq!(s0.text_offset(), 614);
1037    }
1038}
1039
1040#[cfg(test)]
1041mod test_glyph {
1042    use crate::grapheme::{GlyphIter, RopeGraphemes};
1043    use crate::TextPosition;
1044    use ropey::Rope;
1045
1046    #[test]
1047    fn test_glyph1() {
1048        let s = Rope::from(
1049            r#"0123456789
1050abcdefghij
1051jklöjklöjk
1052uiopü+uiop"#,
1053        );
1054        let r = RopeGraphemes::new(0, s.byte_slice(..));
1055        let mut glyphs = GlyphIter::new(TextPosition::new(0, 0), r);
1056
1057        let n = glyphs.next().unwrap();
1058        assert_eq!(n.glyph(), "0");
1059        assert_eq!(n.text_bytes(), 0..1);
1060        assert_eq!(n.screen_pos(), (0, 0));
1061        assert_eq!(n.pos(), TextPosition::new(0, 0));
1062        assert_eq!(n.screen_width(), 1);
1063
1064        let n = glyphs.next().unwrap();
1065        assert_eq!(n.glyph(), "1");
1066        assert_eq!(n.text_bytes(), 1..2);
1067        assert_eq!(n.screen_pos(), (1, 0));
1068        assert_eq!(n.pos(), TextPosition::new(1, 0));
1069        assert_eq!(n.screen_width(), 1);
1070
1071        let n = glyphs.next().unwrap();
1072        assert_eq!(n.glyph(), "2");
1073        assert_eq!(n.text_bytes(), 2..3);
1074        assert_eq!(n.screen_pos(), (2, 0));
1075        assert_eq!(n.pos(), TextPosition::new(2, 0));
1076        assert_eq!(n.screen_width(), 1);
1077
1078        let n = glyphs.nth(7).unwrap();
1079        assert_eq!(n.glyph(), "");
1080        assert_eq!(n.text_bytes(), 10..11);
1081        assert_eq!(n.screen_pos(), (10, 0));
1082        assert_eq!(n.pos(), TextPosition::new(10, 0));
1083        assert_eq!(n.screen_width(), 0);
1084
1085        let n = glyphs.next().unwrap();
1086        assert_eq!(n.glyph(), "a");
1087        assert_eq!(n.text_bytes(), 11..12);
1088        assert_eq!(n.screen_pos(), (0, 1));
1089        assert_eq!(n.pos(), TextPosition::new(0, 1));
1090        assert_eq!(n.screen_width(), 1);
1091    }
1092
1093    #[test]
1094    fn test_glyph2() {
1095        // screen offset
1096        let s = Rope::from(
1097            r#"0123456789
1098abcdefghij
1099jklöjklöjk
1100uiopü+uiop"#,
1101        );
1102        let r = RopeGraphemes::new(0, s.byte_slice(..));
1103        let mut glyphs = GlyphIter::new(TextPosition::new(0, 0), r);
1104        glyphs.set_screen_offset(2);
1105        glyphs.set_screen_width(100);
1106
1107        let n = glyphs.next().unwrap();
1108        assert_eq!(n.glyph(), "2");
1109        assert_eq!(n.text_bytes(), 2..3);
1110        assert_eq!(n.screen_pos(), (0, 0));
1111        assert_eq!(n.pos(), TextPosition::new(2, 0));
1112        assert_eq!(n.screen_width(), 1);
1113
1114        let n = glyphs.next().unwrap();
1115        assert_eq!(n.glyph(), "3");
1116        assert_eq!(n.text_bytes(), 3..4);
1117        assert_eq!(n.screen_pos(), (1, 0));
1118        assert_eq!(n.pos(), TextPosition::new(3, 0));
1119        assert_eq!(n.screen_width(), 1);
1120
1121        let n = glyphs.nth(6).unwrap();
1122        assert_eq!(n.glyph(), "");
1123        assert_eq!(n.text_bytes(), 10..11);
1124        assert_eq!(n.screen_pos(), (8, 0));
1125        assert_eq!(n.pos(), TextPosition::new(10, 0));
1126        assert_eq!(n.screen_width(), 0);
1127
1128        let n = glyphs.next().unwrap();
1129        assert_eq!(n.glyph(), "c");
1130        assert_eq!(n.text_bytes(), 13..14);
1131        assert_eq!(n.screen_pos(), (0, 1));
1132        assert_eq!(n.pos(), TextPosition::new(2, 1));
1133        assert_eq!(n.screen_width(), 1);
1134    }
1135
1136    #[test]
1137    fn test_glyph3() {
1138        // screen offset + width
1139        let s = Rope::from(
1140            r#"0123456789
1141abcdefghij
1142jklöjklöjk
1143uiopü+uiop"#,
1144        );
1145        let r = RopeGraphemes::new(0, s.byte_slice(..));
1146        let mut glyphs = GlyphIter::new(TextPosition::new(0, 0), r);
1147        glyphs.set_screen_offset(2);
1148        glyphs.set_screen_width(6);
1149
1150        let n = glyphs.next().unwrap();
1151        assert_eq!(n.glyph(), "2");
1152        assert_eq!(n.text_bytes(), 2..3);
1153        assert_eq!(n.screen_pos(), (0, 0));
1154        assert_eq!(n.pos(), TextPosition::new(2, 0));
1155        assert_eq!(n.screen_width(), 1);
1156
1157        let n = glyphs.next().unwrap();
1158        assert_eq!(n.glyph(), "3");
1159        assert_eq!(n.text_bytes(), 3..4);
1160        assert_eq!(n.screen_pos(), (1, 0));
1161        assert_eq!(n.pos(), TextPosition::new(3, 0));
1162        assert_eq!(n.screen_width(), 1);
1163
1164        let n = glyphs.nth(2).unwrap();
1165        assert_eq!(n.glyph(), "6");
1166
1167        let n = glyphs.next().unwrap();
1168        assert_eq!(n.glyph(), "7");
1169        assert_eq!(n.text_bytes(), 7..8);
1170        assert_eq!(n.screen_pos(), (5, 0));
1171        assert_eq!(n.pos(), TextPosition::new(7, 0));
1172        assert_eq!(n.screen_width(), 1);
1173
1174        let n = glyphs.next().unwrap();
1175        assert_eq!(n.glyph(), "c");
1176        assert_eq!(n.text_bytes(), 13..14);
1177        assert_eq!(n.screen_pos(), (0, 1));
1178        assert_eq!(n.pos(), TextPosition::new(2, 1));
1179        assert_eq!(n.screen_width(), 1);
1180    }
1181
1182    #[test]
1183    fn test_glyph4() {
1184        // tabs
1185        let s = Rope::from(
1186            "012\t3456789
1187abcdefghij
1188jklöjklöjk
1189uiopü+uiop",
1190        );
1191        let r = RopeGraphemes::new(0, s.byte_slice(..));
1192        let mut glyphs = GlyphIter::new(TextPosition::new(0, 0), r);
1193        glyphs.set_screen_offset(2);
1194        glyphs.set_screen_width(100);
1195
1196        let n = glyphs.next().unwrap();
1197        assert_eq!(n.glyph(), "2");
1198        assert_eq!(n.text_bytes(), 2..3);
1199        assert_eq!(n.screen_pos(), (0, 0));
1200        assert_eq!(n.pos(), TextPosition::new(2, 0));
1201        assert_eq!(n.screen_width(), 1);
1202
1203        let n = glyphs.next().unwrap();
1204        assert_eq!(n.glyph(), " ");
1205        assert_eq!(n.text_bytes(), 3..4);
1206        assert_eq!(n.screen_pos(), (1, 0));
1207        assert_eq!(n.pos(), TextPosition::new(3, 0));
1208        assert_eq!(n.screen_width(), 5);
1209
1210        let n = glyphs.nth(7).unwrap();
1211        assert_eq!(n.glyph(), "");
1212        assert_eq!(n.text_bytes(), 11..12);
1213        assert_eq!(n.screen_pos(), (13, 0));
1214        assert_eq!(n.pos(), TextPosition::new(11, 0));
1215        assert_eq!(n.screen_width(), 0);
1216
1217        let n = glyphs.next().unwrap();
1218        assert_eq!(n.glyph(), "c");
1219        assert_eq!(n.text_bytes(), 14..15);
1220        assert_eq!(n.screen_pos(), (0, 1));
1221        assert_eq!(n.pos(), TextPosition::new(2, 1));
1222        assert_eq!(n.screen_width(), 1);
1223    }
1224
1225    #[test]
1226    fn test_glyph5() {
1227        // clipping wide
1228        let s = Rope::from(
1229            "0\t12345678\t9
1230abcdefghij
1231jklöjklöjk
1232uiopü+uiop",
1233        );
1234        let r = RopeGraphemes::new(0, s.byte_slice(..));
1235        let mut glyphs = GlyphIter::new(TextPosition::new(0, 0), r);
1236        glyphs.set_screen_offset(2);
1237        glyphs.set_screen_width(20);
1238
1239        let n = glyphs.next().unwrap();
1240        assert_eq!(n.glyph(), "∃");
1241        assert_eq!(n.text_bytes(), 1..2);
1242        assert_eq!(n.screen_pos(), (0, 0));
1243        assert_eq!(n.pos(), TextPosition::new(1, 0));
1244        assert_eq!(n.screen_width(), 6);
1245
1246        let n = glyphs.next().unwrap();
1247        assert_eq!(n.glyph(), "1");
1248        assert_eq!(n.text_bytes(), 2..3);
1249        assert_eq!(n.screen_pos(), (6, 0));
1250        assert_eq!(n.pos(), TextPosition::new(2, 0));
1251        assert_eq!(n.screen_width(), 1);
1252
1253        let n = glyphs.nth(6).unwrap();
1254        assert_eq!(n.glyph(), "8");
1255
1256        let n = glyphs.next().unwrap();
1257        assert_eq!(n.glyph(), "∃");
1258        assert_eq!(n.text_bytes(), 10..11);
1259        assert_eq!(n.screen_pos(), (14, 0));
1260        assert_eq!(n.pos(), TextPosition::new(10, 0));
1261        assert_eq!(n.screen_width(), 2);
1262
1263        let n = glyphs.next().unwrap();
1264        assert_eq!(n.glyph(), "c");
1265        assert_eq!(n.text_bytes(), 15..16);
1266        assert_eq!(n.screen_pos(), (0, 1));
1267        assert_eq!(n.pos(), TextPosition::new(2, 1));
1268        assert_eq!(n.screen_width(), 1);
1269    }
1270}