amp/view/terminal/
buffer_iterator.rs

1use crate::view::terminal::Cell;
2use scribe::buffer::Position;
3use unicode_segmentation::UnicodeSegmentation;
4
5/// Iterates over the provided cells, yielding slices for each line.
6pub struct TerminalBufferIterator<'c> {
7    index: usize,
8    width: usize,
9    cells: &'c Vec<Cell<'c>>,
10}
11
12impl<'c> TerminalBufferIterator<'c> {
13    pub fn new(width: usize, cells: &'c Vec<Cell<'c>>) -> TerminalBufferIterator {
14        TerminalBufferIterator {
15            index: 0,
16            width,
17            cells,
18        }
19    }
20}
21
22impl<'c> Iterator for TerminalBufferIterator<'c> {
23    type Item = (Position, &'c Cell<'c>);
24
25    /// Iterates over lines of cells.
26    fn next(&mut self) -> Option<Self::Item> {
27        if self.index < self.cells.len() {
28            let position = Position {
29                line: self.index / self.width,
30                offset: self.index % self.width,
31            };
32            let cell = &self.cells[self.index];
33            self.index += cell.content.graphemes(true).count().max(1);
34
35            Some((position, cell))
36        } else {
37            None
38        }
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::TerminalBufferIterator;
45    use crate::view::terminal::Cell;
46    use crate::view::{Colors, Style};
47    use scribe::buffer::Position;
48    use std::borrow::Cow;
49
50    #[test]
51    fn terminal_buffer_iterator_yields_cells_and_their_positions() {
52        let width = 3;
53        let cells = vec![
54            Cell {
55                content: Cow::from("a"),
56                colors: Colors::Default,
57                style: Style::Default,
58            },
59            Cell {
60                content: Cow::from("m"),
61                colors: Colors::Default,
62                style: Style::Default,
63            },
64            Cell {
65                content: Cow::from("p"),
66                colors: Colors::Default,
67                style: Style::Default,
68            },
69        ];
70        let mut iterator = TerminalBufferIterator::new(width, &cells);
71
72        assert_eq!(
73            iterator.next(),
74            Some((Position { line: 0, offset: 0 }, &cells[0]))
75        );
76        assert_eq!(
77            iterator.next(),
78            Some((Position { line: 0, offset: 1 }, &cells[1]))
79        );
80        assert_eq!(
81            iterator.next(),
82            Some((Position { line: 0, offset: 2 }, &cells[2]))
83        );
84        assert_eq!(iterator.next(), None);
85    }
86
87    #[test]
88    fn terminal_buffer_iterator_considers_width_when_calculating_positions() {
89        let width = 2;
90        let cells = vec![
91            Cell {
92                content: Cow::from("a"),
93                colors: Colors::Default,
94                style: Style::Default,
95            },
96            Cell {
97                content: Cow::from("m"),
98                colors: Colors::Default,
99                style: Style::Default,
100            },
101            Cell {
102                content: Cow::from("p"),
103                colors: Colors::Default,
104                style: Style::Default,
105            },
106        ];
107        let mut iterator = TerminalBufferIterator::new(width, &cells);
108
109        assert_eq!(
110            iterator.nth(2),
111            Some((Position { line: 1, offset: 0 }, &cells[2]))
112        );
113    }
114
115    #[test]
116    fn terminal_buffer_iterator_handles_overlapping_cells_correctly() {
117        let width = 4;
118        let cells = vec![
119            Cell {
120                content: Cow::from("amp"),
121                colors: Colors::Default,
122                style: Style::Default,
123            },
124            Cell {
125                content: Cow::from("b"),
126                colors: Colors::Default,
127                style: Style::Default,
128            },
129            Cell {
130                content: Cow::from("c"),
131                colors: Colors::Default,
132                style: Style::Default,
133            },
134            Cell {
135                content: Cow::from("d"),
136                colors: Colors::Default,
137                style: Style::Default,
138            },
139        ];
140        let mut iterator = TerminalBufferIterator::new(width, &cells);
141
142        assert_eq!(
143            iterator.next(),
144            Some((Position { line: 0, offset: 0 }, &cells[0]))
145        );
146        assert_eq!(
147            iterator.next(),
148            Some((Position { line: 0, offset: 3 }, &cells[3]))
149        );
150        assert_eq!(iterator.next(), None);
151    }
152
153    #[test]
154    fn terminal_buffer_iterator_handles_empty_cells_correctly() {
155        let width = 4;
156        let cells = vec![
157            Cell {
158                content: Cow::from(""),
159                colors: Colors::Default,
160                style: Style::Default,
161            },
162            Cell {
163                content: Cow::from("a"),
164                colors: Colors::Default,
165                style: Style::Default,
166            },
167        ];
168        let mut iterator = TerminalBufferIterator::new(width, &cells);
169
170        assert_eq!(
171            iterator.next(),
172            Some((Position { line: 0, offset: 0 }, &cells[0]))
173        );
174        assert_eq!(
175            iterator.next(),
176            Some((Position { line: 0, offset: 1 }, &cells[1]))
177        );
178        assert_eq!(iterator.next(), None);
179    }
180}