term_model/grid/
mod.rs

1// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A specialized 2d grid implementation optimized for use in a terminal.
16
17use std::cmp::{max, min, Ordering};
18use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeTo};
19
20use serde::{Deserialize, Serialize};
21
22use crate::index::{Column, IndexRange, Line, Point};
23use crate::selection::Selection;
24use crate::term::cell::Flags;
25
26mod row;
27pub use self::row::Row;
28
29#[cfg(test)]
30mod tests;
31
32mod storage;
33use self::storage::Storage;
34
35/// Bidirection iterator
36pub trait BidirectionalIterator: Iterator {
37    fn prev(&mut self) -> Option<Self::Item>;
38}
39
40/// An item in the grid along with its Line and Column.
41pub struct Indexed<T> {
42    pub inner: T,
43    pub line: Line,
44    pub column: Column,
45}
46
47impl<T> Deref for Indexed<T> {
48    type Target = T;
49
50    #[inline]
51    fn deref(&self) -> &T {
52        &self.inner
53    }
54}
55
56impl<T: PartialEq> ::std::cmp::PartialEq for Grid<T> {
57    fn eq(&self, other: &Self) -> bool {
58        // Compare struct fields and check result of grid comparison
59        self.raw.eq(&other.raw)
60            && self.cols.eq(&other.cols)
61            && self.lines.eq(&other.lines)
62            && self.display_offset.eq(&other.display_offset)
63            && self.selection.eq(&other.selection)
64    }
65}
66
67pub trait GridCell {
68    fn is_empty(&self) -> bool;
69    fn flags(&self) -> &Flags;
70    fn flags_mut(&mut self) -> &mut Flags;
71
72    /// Fast equality approximation.
73    ///
74    /// This is a faster alternative to [`PartialEq`],
75    /// but might report inequal cells as equal.
76    fn fast_eq(&self, other: Self) -> bool;
77}
78
79/// Represents the terminal display contents
80///
81/// ```notrust
82/// ┌─────────────────────────┐  <-- max_scroll_limit + lines
83/// │                         │
84/// │      UNINITIALIZED      │
85/// │                         │
86/// ├─────────────────────────┤  <-- self.raw.inner.len()
87/// │                         │
88/// │      RESIZE BUFFER      │
89/// │                         │
90/// ├─────────────────────────┤  <-- self.history_size() + lines
91/// │                         │
92/// │     SCROLLUP REGION     │
93/// │                         │
94/// ├─────────────────────────┤v lines
95/// │                         │|
96/// │     VISIBLE  REGION     │|
97/// │                         │|
98/// ├─────────────────────────┤^ <-- display_offset
99/// │                         │
100/// │    SCROLLDOWN REGION    │
101/// │                         │
102/// └─────────────────────────┘  <-- zero
103///                           ^
104///                          cols
105/// ```
106#[derive(Clone, Debug, Deserialize, Serialize)]
107pub struct Grid<T> {
108    /// Lines in the grid. Each row holds a list of cells corresponding to the
109    /// columns in that row.
110    raw: Storage<T>,
111
112    /// Number of columns.
113    cols: Column,
114
115    /// Number of visible lines.
116    lines: Line,
117
118    /// Offset of displayed area.
119    ///
120    /// If the displayed region isn't at the bottom of the screen, it stays
121    /// stationary while more text is emitted. The scrolling implementation
122    /// updates this offset accordingly.
123    display_offset: usize,
124
125    /// Selected region.
126    #[serde(skip)]
127    pub selection: Option<Selection>,
128
129    /// Maximum number of lines in history.
130    max_scroll_limit: usize,
131}
132
133#[derive(Copy, Clone)]
134pub enum Scroll {
135    Lines(isize),
136    PageUp,
137    PageDown,
138    Top,
139    Bottom,
140}
141
142impl<T: GridCell + PartialEq + Copy> Grid<T> {
143    pub fn new(lines: Line, cols: Column, scrollback: usize, template: T) -> Grid<T> {
144        let raw = Storage::with_capacity(lines, Row::new(cols, &template));
145        Grid { raw, cols, lines, display_offset: 0, selection: None, max_scroll_limit: scrollback }
146    }
147
148    pub fn buffer_to_visible(&self, point: impl Into<Point<usize>>) -> Option<Point<usize>> {
149        let mut point = point.into();
150
151        if point.line < self.display_offset || point.line >= self.display_offset + self.lines.0 {
152            return None;
153        }
154
155        point.line = self.lines.0 + self.display_offset - point.line - 1;
156
157        Some(point)
158    }
159
160    pub fn visible_to_buffer(&self, point: Point) -> Point<usize> {
161        Point { line: self.visible_line_to_buffer(point.line), col: point.col }
162    }
163
164    fn visible_line_to_buffer(&self, line: Line) -> usize {
165        self.line_to_offset(line) + self.display_offset
166    }
167
168    /// Update the size of the scrollback history
169    pub fn update_history(&mut self, history_size: usize) {
170        let current_history_size = self.history_size();
171        if current_history_size > history_size {
172            self.raw.shrink_lines(current_history_size - history_size);
173        }
174        self.display_offset = min(self.display_offset, history_size);
175        self.max_scroll_limit = history_size;
176    }
177
178    pub fn scroll_display(&mut self, scroll: Scroll) {
179        match scroll {
180            Scroll::Lines(count) => {
181                self.display_offset = min(
182                    max((self.display_offset as isize) + count, 0isize) as usize,
183                    self.history_size(),
184                );
185            }
186            Scroll::PageUp => {
187                self.display_offset = min(self.display_offset + self.lines.0, self.history_size());
188            }
189            Scroll::PageDown => {
190                self.display_offset -= min(self.display_offset, self.lines.0);
191            }
192            Scroll::Top => self.display_offset = self.history_size(),
193            Scroll::Bottom => self.display_offset = 0,
194        }
195    }
196
197    pub fn resize(
198        &mut self,
199        reflow: bool,
200        lines: Line,
201        cols: Column,
202        cursor_pos: &mut Point,
203        template: &T,
204    ) {
205        // Check that there's actually work to do and return early if not
206        if lines == self.lines && cols == self.cols {
207            return;
208        }
209
210        match self.lines.cmp(&lines) {
211            Ordering::Less => self.grow_lines(lines, template),
212            Ordering::Greater => self.shrink_lines(lines),
213            Ordering::Equal => (),
214        }
215
216        match self.cols.cmp(&cols) {
217            Ordering::Less => self.grow_cols(reflow, cols, cursor_pos, template),
218            Ordering::Greater => self.shrink_cols(reflow, cols, template),
219            Ordering::Equal => (),
220        }
221    }
222
223    fn increase_scroll_limit(&mut self, count: usize, template: &T) {
224        let count = min(count, self.max_scroll_limit - self.history_size());
225        if count != 0 {
226            self.raw.initialize(count, template, self.cols);
227        }
228    }
229
230    fn decrease_scroll_limit(&mut self, count: usize) {
231        let count = min(count, self.history_size());
232        if count != 0 {
233            self.raw.shrink_lines(min(count, self.history_size()));
234        }
235    }
236
237    /// Add lines to the visible area
238    ///
239    /// Alacritty keeps the cursor at the bottom of the terminal as long as there
240    /// is scrollback available. Once scrollback is exhausted, new lines are
241    /// simply added to the bottom of the screen.
242    fn grow_lines(&mut self, new_line_count: Line, template: &T) {
243        let lines_added = new_line_count - self.lines;
244
245        // Need to "resize" before updating buffer
246        self.raw.grow_visible_lines(new_line_count, Row::new(self.cols, template));
247        self.lines = new_line_count;
248
249        // Move existing lines up if there is no scrollback to fill new lines
250        let history_size = self.history_size();
251        if lines_added.0 > history_size {
252            self.scroll_up(&(Line(0)..new_line_count), lines_added - history_size, template);
253        }
254
255        self.decrease_scroll_limit(*lines_added);
256        self.display_offset = self.display_offset.saturating_sub(*lines_added);
257    }
258
259    // Grow number of columns in each row, reflowing if necessary
260    fn grow_cols(&mut self, reflow: bool, cols: Column, cursor_pos: &mut Point, template: &T) {
261        // Check if a row needs to be wrapped
262        let should_reflow = |row: &Row<T>| -> bool {
263            let len = Column(row.len());
264            reflow && len < cols && row[len - 1].flags().contains(Flags::WRAPLINE)
265        };
266
267        let mut new_empty_lines = 0;
268        let mut reversed: Vec<Row<T>> = Vec::with_capacity(self.raw.len());
269        for (i, mut row) in self.raw.drain().enumerate().rev() {
270            // FIXME: Rust 1.39.0+ allows moving in pattern guard here
271            // Check if reflowing shoud be performed
272            let mut last_row = reversed.last_mut();
273            let last_row = match last_row {
274                Some(ref mut last_row) if should_reflow(last_row) => last_row,
275                _ => {
276                    reversed.push(row);
277                    continue;
278                }
279            };
280
281            // Remove wrap flag before appending additional cells
282            if let Some(cell) = last_row.last_mut() {
283                cell.flags_mut().remove(Flags::WRAPLINE);
284            }
285
286            // Remove leading spacers when reflowing wide char to the previous line
287            let last_len = last_row.len();
288            if last_len >= 2
289                && !last_row[Column(last_len - 2)].flags().contains(Flags::WIDE_CHAR)
290                && last_row[Column(last_len - 1)].flags().contains(Flags::WIDE_CHAR_SPACER)
291            {
292                last_row.shrink(Column(last_len - 1));
293            }
294
295            // Append as many cells from the next line as possible
296            let len = min(row.len(), cols.0 - last_row.len());
297
298            // Insert leading spacer when there's not enough room for reflowing wide char
299            let mut cells = if row[Column(len - 1)].flags().contains(Flags::WIDE_CHAR) {
300                let mut cells = row.front_split_off(len - 1);
301
302                let mut spacer = *template;
303                spacer.flags_mut().insert(Flags::WIDE_CHAR_SPACER);
304                cells.push(spacer);
305
306                cells
307            } else {
308                row.front_split_off(len)
309            };
310
311            last_row.append(&mut cells);
312
313            if row.is_empty() {
314                if i + reversed.len() < self.lines.0 {
315                    // Add new line and move lines up if we can't pull from history
316                    cursor_pos.line = Line(cursor_pos.line.saturating_sub(1));
317                    new_empty_lines += 1;
318                } else if i < self.display_offset {
319                    // Keep viewport in place if line is outside of the visible area
320                    self.display_offset = self.display_offset.saturating_sub(1);
321                }
322
323                // Don't push line into the new buffer
324                continue;
325            } else if let Some(cell) = last_row.last_mut() {
326                // Set wrap flag if next line still has cells
327                cell.flags_mut().insert(Flags::WRAPLINE);
328            }
329
330            reversed.push(row);
331        }
332
333        // Add padding lines
334        reversed.append(&mut vec![Row::new(cols, template); new_empty_lines]);
335
336        // Fill remaining cells and reverse iterator
337        let mut new_raw = Vec::with_capacity(reversed.len());
338        for mut row in reversed.drain(..).rev() {
339            if row.len() < cols.0 {
340                row.grow(cols, template);
341            }
342            new_raw.push(row);
343        }
344
345        self.raw.replace_inner(new_raw);
346
347        self.display_offset = min(self.display_offset, self.history_size());
348        self.cols = cols;
349    }
350
351    // Shrink number of columns in each row, reflowing if necessary
352    fn shrink_cols(&mut self, reflow: bool, cols: Column, template: &T) {
353        let mut new_raw = Vec::with_capacity(self.raw.len());
354        let mut buffered = None;
355        for (i, mut row) in self.raw.drain().enumerate().rev() {
356            // Append lines left over from previous row
357            if let Some(buffered) = buffered.take() {
358                row.append_front(buffered);
359            }
360
361            loop {
362                // FIXME: Rust 1.39.0+ allows moving in pattern guard here
363                // Check if reflowing shoud be performed
364                let wrapped = row.shrink(cols);
365                let mut wrapped = match wrapped {
366                    Some(_) if reflow => wrapped.unwrap(),
367                    _ => {
368                        new_raw.push(row);
369                        break;
370                    }
371                };
372
373                // Insert spacer if a wide char would be wrapped into the last column
374                if row.len() >= cols.0 && row[cols - 1].flags().contains(Flags::WIDE_CHAR) {
375                    wrapped.insert(0, row[cols - 1]);
376
377                    let mut spacer = *template;
378                    spacer.flags_mut().insert(Flags::WIDE_CHAR_SPACER);
379                    row[cols - 1] = spacer;
380                }
381
382                // Remove wide char spacer before shrinking
383                let len = wrapped.len();
384                if (len == 1 || (len >= 2 && !wrapped[len - 2].flags().contains(Flags::WIDE_CHAR)))
385                    && wrapped[len - 1].flags().contains(Flags::WIDE_CHAR_SPACER)
386                {
387                    if len == 1 {
388                        row[cols - 1].flags_mut().insert(Flags::WRAPLINE);
389                        new_raw.push(row);
390                        break;
391                    } else {
392                        wrapped[len - 2].flags_mut().insert(Flags::WRAPLINE);
393                        wrapped.truncate(len - 1);
394                    }
395                }
396
397                new_raw.push(row);
398
399                // Set line as wrapped if cells got removed
400                if let Some(cell) = new_raw.last_mut().and_then(|r| r.last_mut()) {
401                    cell.flags_mut().insert(Flags::WRAPLINE);
402                }
403
404                if wrapped
405                    .last()
406                    .map(|c| c.flags().contains(Flags::WRAPLINE) && i >= 1)
407                    .unwrap_or(false)
408                    && wrapped.len() < cols.0
409                {
410                    // Make sure previous wrap flag doesn't linger around
411                    if let Some(cell) = wrapped.last_mut() {
412                        cell.flags_mut().remove(Flags::WRAPLINE);
413                    }
414
415                    // Add removed cells to start of next row
416                    buffered = Some(wrapped);
417                    break;
418                } else {
419                    // Make sure viewport doesn't move if line is outside of the visible area
420                    if i < self.display_offset {
421                        self.display_offset = min(self.display_offset + 1, self.max_scroll_limit);
422                    }
423
424                    // Make sure new row is at least as long as new width
425                    let occ = wrapped.len();
426                    if occ < cols.0 {
427                        wrapped.append(&mut vec![*template; cols.0 - occ]);
428                    }
429                    row = Row::from_vec(wrapped, occ);
430                }
431            }
432        }
433
434        let mut reversed: Vec<Row<T>> = new_raw.drain(..).rev().collect();
435        reversed.truncate(self.max_scroll_limit + self.lines.0);
436        self.raw.replace_inner(reversed);
437        self.cols = cols;
438    }
439
440    /// Remove lines from the visible area
441    ///
442    /// The behavior in Terminal.app and iTerm.app is to keep the cursor at the
443    /// bottom of the screen. This is achieved by pushing history "out the top"
444    /// of the terminal window.
445    ///
446    /// Alacritty takes the same approach.
447    fn shrink_lines(&mut self, target: Line) {
448        let prev = self.lines;
449
450        self.selection = None;
451        self.raw.rotate(*prev as isize - *target as isize);
452        self.raw.shrink_visible_lines(target);
453        self.lines = target;
454    }
455
456    /// Convert a Line index (active region) to a buffer offset
457    ///
458    /// # Panics
459    ///
460    /// This method will panic if `Line` is larger than the grid dimensions
461    pub fn line_to_offset(&self, line: Line) -> usize {
462        assert!(line < self.num_lines());
463
464        *(self.num_lines() - line - 1)
465    }
466
467    #[inline]
468    pub fn scroll_down(&mut self, region: &Range<Line>, positions: Line, template: &T) {
469        let num_lines = self.num_lines().0;
470        let num_cols = self.num_cols().0;
471
472        // Whether or not there is a scrolling region active, as long as it
473        // starts at the top, we can do a full rotation which just involves
474        // changing the start index.
475        //
476        // To accommodate scroll regions, rows are reordered at the end.
477        if region.start == Line(0) {
478            // Rotate the entire line buffer. If there's a scrolling region
479            // active, the bottom lines are restored in the next step.
480            self.raw.rotate_up(*positions);
481            self.selection = self
482                .selection
483                .take()
484                .and_then(|s| s.rotate(num_lines, num_cols, region, -(*positions as isize)));
485
486            self.decrease_scroll_limit(*positions);
487
488            // Now, restore any scroll region lines
489            let lines = self.lines;
490            for i in IndexRange(region.end..lines) {
491                self.raw.swap_lines(i, i + positions);
492            }
493
494            // Finally, reset recycled lines
495            for i in IndexRange(Line(0)..positions) {
496                self.raw[i].reset(&template);
497            }
498        } else {
499            // Rotate selection to track content
500            self.selection = self
501                .selection
502                .take()
503                .and_then(|s| s.rotate(num_lines, num_cols, region, -(*positions as isize)));
504
505            // Subregion rotation
506            for line in IndexRange((region.start + positions)..region.end).rev() {
507                self.raw.swap_lines(line, line - positions);
508            }
509
510            for line in IndexRange(region.start..(region.start + positions)) {
511                self.raw[line].reset(&template);
512            }
513        }
514    }
515
516    /// scroll_up moves lines at the bottom towards the top
517    ///
518    /// This is the performance-sensitive part of scrolling.
519    pub fn scroll_up(&mut self, region: &Range<Line>, positions: Line, template: &T) {
520        let num_lines = self.num_lines().0;
521        let num_cols = self.num_cols().0;
522
523        if region.start == Line(0) {
524            // Update display offset when not pinned to active area
525            if self.display_offset != 0 {
526                self.display_offset = min(self.display_offset + *positions, self.len() - num_lines);
527            }
528
529            self.increase_scroll_limit(*positions, template);
530
531            // Rotate the entire line buffer. If there's a scrolling region
532            // active, the bottom lines are restored in the next step.
533            self.raw.rotate(-(*positions as isize));
534            self.selection = self
535                .selection
536                .take()
537                .and_then(|s| s.rotate(num_lines, num_cols, region, *positions as isize));
538
539            // This next loop swaps "fixed" lines outside of a scroll region
540            // back into place after the rotation. The work is done in buffer-
541            // space rather than terminal-space to avoid redundant
542            // transformations.
543            let fixed_lines = num_lines - *region.end;
544
545            for i in 0..fixed_lines {
546                self.raw.swap(i, i + *positions);
547            }
548
549            // Finally, reset recycled lines
550            //
551            // Recycled lines are just above the end of the scrolling region.
552            for i in 0..*positions {
553                self.raw[i + fixed_lines].reset(&template);
554            }
555        } else {
556            // Rotate selection to track content
557            self.selection = self
558                .selection
559                .take()
560                .and_then(|s| s.rotate(num_lines, num_cols, region, *positions as isize));
561
562            // Subregion rotation
563            for line in IndexRange(region.start..(region.end - positions)) {
564                self.raw.swap_lines(line, line + positions);
565            }
566
567            // Clear reused lines
568            for line in IndexRange((region.end - positions)..region.end) {
569                self.raw[line].reset(&template);
570            }
571        }
572    }
573
574    pub fn clear_viewport(&mut self, template: &T) {
575        // Determine how many lines to scroll up by.
576        let end = Point { line: 0, col: self.num_cols() };
577        let mut iter = self.iter_from(end);
578        while let Some(cell) = iter.prev() {
579            if !cell.is_empty() || iter.cur.line >= *self.lines {
580                break;
581            }
582        }
583        debug_assert!(iter.cur.line <= *self.lines);
584        let positions = self.lines - iter.cur.line;
585        let region = Line(0)..self.num_lines();
586
587        // Reset display offset
588        self.display_offset = 0;
589
590        // Clear the viewport
591        self.scroll_up(&region, positions, template);
592
593        // Reset rotated lines
594        for i in positions.0..self.lines.0 {
595            self.raw[i].reset(&template);
596        }
597    }
598
599    // Completely reset the grid state
600    pub fn reset(&mut self, template: &T) {
601        self.clear_history();
602
603        // Reset all visible lines
604        for row in 0..self.raw.len() {
605            self.raw[row].reset(template);
606        }
607
608        self.display_offset = 0;
609        self.selection = None;
610    }
611}
612
613#[allow(clippy::len_without_is_empty)]
614impl<T> Grid<T> {
615    #[inline]
616    pub fn num_lines(&self) -> Line {
617        self.lines
618    }
619
620    #[inline]
621    pub fn display_iter(&self) -> DisplayIter<'_, T> {
622        DisplayIter::new(self)
623    }
624
625    #[inline]
626    pub fn num_cols(&self) -> Column {
627        self.cols
628    }
629
630    #[inline]
631    pub fn clear_history(&mut self) {
632        // Explicitly purge all lines from history
633        self.raw.shrink_lines(self.history_size());
634    }
635
636    /// Total number of lines in the buffer, this includes scrollback + visible lines
637    #[inline]
638    pub fn len(&self) -> usize {
639        self.raw.len()
640    }
641
642    #[inline]
643    pub fn history_size(&self) -> usize {
644        self.raw.len() - *self.lines
645    }
646
647    /// This is used only for initializing after loading ref-tests
648    #[inline]
649    pub fn initialize_all(&mut self, template: &T)
650    where
651        T: Copy + GridCell,
652    {
653        // Remove all cached lines to clear them of any content
654        self.truncate();
655
656        // Initialize everything with empty new lines
657        self.raw.initialize(self.max_scroll_limit - self.history_size(), template, self.cols);
658    }
659
660    /// This is used only for truncating before saving ref-tests
661    #[inline]
662    pub fn truncate(&mut self) {
663        self.raw.truncate();
664    }
665
666    #[inline]
667    pub fn iter_from(&self, point: Point<usize>) -> GridIterator<'_, T> {
668        GridIterator { grid: self, cur: point }
669    }
670
671    #[inline]
672    pub fn contains(&self, point: &Point) -> bool {
673        self.lines > point.line && self.cols > point.col
674    }
675
676    #[inline]
677    pub fn display_offset(&self) -> usize {
678        self.display_offset
679    }
680}
681
682pub struct GridIterator<'a, T> {
683    /// Immutable grid reference
684    grid: &'a Grid<T>,
685
686    /// Current position of the iterator within the grid.
687    cur: Point<usize>,
688}
689
690impl<'a, T> GridIterator<'a, T> {
691    pub fn point(&self) -> Point<usize> {
692        self.cur
693    }
694
695    pub fn cell(&self) -> &'a T {
696        &self.grid[self.cur.line][self.cur.col]
697    }
698}
699
700impl<'a, T> Iterator for GridIterator<'a, T> {
701    type Item = &'a T;
702
703    fn next(&mut self) -> Option<Self::Item> {
704        let last_col = self.grid.num_cols() - Column(1);
705        match self.cur {
706            Point { line, col } if line == 0 && col == last_col => None,
707            Point { col, .. } if (col == last_col) => {
708                self.cur.line -= 1;
709                self.cur.col = Column(0);
710                Some(&self.grid[self.cur.line][self.cur.col])
711            }
712            _ => {
713                self.cur.col += Column(1);
714                Some(&self.grid[self.cur.line][self.cur.col])
715            }
716        }
717    }
718}
719
720impl<'a, T> BidirectionalIterator for GridIterator<'a, T> {
721    fn prev(&mut self) -> Option<Self::Item> {
722        let num_cols = self.grid.num_cols();
723
724        match self.cur {
725            Point { line, col: Column(0) } if line == self.grid.len() - 1 => None,
726            Point { col: Column(0), .. } => {
727                self.cur.line += 1;
728                self.cur.col = num_cols - Column(1);
729                Some(&self.grid[self.cur.line][self.cur.col])
730            }
731            _ => {
732                self.cur.col -= Column(1);
733                Some(&self.grid[self.cur.line][self.cur.col])
734            }
735        }
736    }
737}
738
739/// Index active region by line
740impl<T> Index<Line> for Grid<T> {
741    type Output = Row<T>;
742
743    #[inline]
744    fn index(&self, index: Line) -> &Row<T> {
745        &self.raw[index]
746    }
747}
748
749/// Index with buffer offset
750impl<T> Index<usize> for Grid<T> {
751    type Output = Row<T>;
752
753    #[inline]
754    fn index(&self, index: usize) -> &Row<T> {
755        &self.raw[index]
756    }
757}
758
759impl<T> IndexMut<Line> for Grid<T> {
760    #[inline]
761    fn index_mut(&mut self, index: Line) -> &mut Row<T> {
762        &mut self.raw[index]
763    }
764}
765
766impl<T> IndexMut<usize> for Grid<T> {
767    #[inline]
768    fn index_mut(&mut self, index: usize) -> &mut Row<T> {
769        &mut self.raw[index]
770    }
771}
772
773impl<'point, T> Index<&'point Point> for Grid<T> {
774    type Output = T;
775
776    #[inline]
777    fn index<'a>(&'a self, point: &Point) -> &'a T {
778        &self[point.line][point.col]
779    }
780}
781
782impl<'point, T> IndexMut<&'point Point> for Grid<T> {
783    #[inline]
784    fn index_mut<'a, 'b>(&'a mut self, point: &'b Point) -> &'a mut T {
785        &mut self[point.line][point.col]
786    }
787}
788
789// -------------------------------------------------------------------------------------------------
790// REGIONS
791// -------------------------------------------------------------------------------------------------
792
793/// A subset of lines in the grid
794///
795/// May be constructed using Grid::region(..)
796pub struct Region<'a, T> {
797    start: Line,
798    end: Line,
799    raw: &'a Storage<T>,
800}
801
802/// A mutable subset of lines in the grid
803///
804/// May be constructed using Grid::region_mut(..)
805pub struct RegionMut<'a, T> {
806    start: Line,
807    end: Line,
808    raw: &'a mut Storage<T>,
809}
810
811impl<'a, T> RegionMut<'a, T> {
812    /// Call the provided function for every item in this region
813    pub fn each<F: Fn(&mut T)>(self, func: F) {
814        for row in self {
815            for item in row {
816                func(item)
817            }
818        }
819    }
820}
821
822pub trait IndexRegion<I, T> {
823    /// Get an immutable region of Self
824    fn region(&self, _: I) -> Region<'_, T>;
825
826    /// Get a mutable region of Self
827    fn region_mut(&mut self, _: I) -> RegionMut<'_, T>;
828}
829
830impl<T> IndexRegion<Range<Line>, T> for Grid<T> {
831    fn region(&self, index: Range<Line>) -> Region<'_, T> {
832        assert!(index.start < self.num_lines());
833        assert!(index.end <= self.num_lines());
834        assert!(index.start <= index.end);
835        Region { start: index.start, end: index.end, raw: &self.raw }
836    }
837
838    fn region_mut(&mut self, index: Range<Line>) -> RegionMut<'_, T> {
839        assert!(index.start < self.num_lines());
840        assert!(index.end <= self.num_lines());
841        assert!(index.start <= index.end);
842        RegionMut { start: index.start, end: index.end, raw: &mut self.raw }
843    }
844}
845
846impl<T> IndexRegion<RangeTo<Line>, T> for Grid<T> {
847    fn region(&self, index: RangeTo<Line>) -> Region<'_, T> {
848        assert!(index.end <= self.num_lines());
849        Region { start: Line(0), end: index.end, raw: &self.raw }
850    }
851
852    fn region_mut(&mut self, index: RangeTo<Line>) -> RegionMut<'_, T> {
853        assert!(index.end <= self.num_lines());
854        RegionMut { start: Line(0), end: index.end, raw: &mut self.raw }
855    }
856}
857
858impl<T> IndexRegion<RangeFrom<Line>, T> for Grid<T> {
859    fn region(&self, index: RangeFrom<Line>) -> Region<'_, T> {
860        assert!(index.start < self.num_lines());
861        Region { start: index.start, end: self.num_lines(), raw: &self.raw }
862    }
863
864    fn region_mut(&mut self, index: RangeFrom<Line>) -> RegionMut<'_, T> {
865        assert!(index.start < self.num_lines());
866        RegionMut { start: index.start, end: self.num_lines(), raw: &mut self.raw }
867    }
868}
869
870impl<T> IndexRegion<RangeFull, T> for Grid<T> {
871    fn region(&self, _: RangeFull) -> Region<'_, T> {
872        Region { start: Line(0), end: self.num_lines(), raw: &self.raw }
873    }
874
875    fn region_mut(&mut self, _: RangeFull) -> RegionMut<'_, T> {
876        RegionMut { start: Line(0), end: self.num_lines(), raw: &mut self.raw }
877    }
878}
879
880pub struct RegionIter<'a, T> {
881    end: Line,
882    cur: Line,
883    raw: &'a Storage<T>,
884}
885
886pub struct RegionIterMut<'a, T> {
887    end: Line,
888    cur: Line,
889    raw: &'a mut Storage<T>,
890}
891
892impl<'a, T> IntoIterator for Region<'a, T> {
893    type IntoIter = RegionIter<'a, T>;
894    type Item = &'a Row<T>;
895
896    fn into_iter(self) -> Self::IntoIter {
897        RegionIter { end: self.end, cur: self.start, raw: self.raw }
898    }
899}
900
901impl<'a, T> IntoIterator for RegionMut<'a, T> {
902    type IntoIter = RegionIterMut<'a, T>;
903    type Item = &'a mut Row<T>;
904
905    fn into_iter(self) -> Self::IntoIter {
906        RegionIterMut { end: self.end, cur: self.start, raw: self.raw }
907    }
908}
909
910impl<'a, T> Iterator for RegionIter<'a, T> {
911    type Item = &'a Row<T>;
912
913    fn next(&mut self) -> Option<Self::Item> {
914        if self.cur < self.end {
915            let index = self.cur;
916            self.cur += 1;
917            Some(&self.raw[index])
918        } else {
919            None
920        }
921    }
922}
923
924impl<'a, T> Iterator for RegionIterMut<'a, T> {
925    type Item = &'a mut Row<T>;
926
927    fn next(&mut self) -> Option<Self::Item> {
928        if self.cur < self.end {
929            let index = self.cur;
930            self.cur += 1;
931            unsafe { Some(&mut *(&mut self.raw[index] as *mut _)) }
932        } else {
933            None
934        }
935    }
936}
937
938// -------------------------------------------------------------------------------------------------
939// DISPLAY ITERATOR
940// -------------------------------------------------------------------------------------------------
941
942/// Iterates over the visible area accounting for buffer transform
943pub struct DisplayIter<'a, T> {
944    grid: &'a Grid<T>,
945    offset: usize,
946    limit: usize,
947    col: Column,
948    line: Line,
949}
950
951impl<'a, T: 'a> DisplayIter<'a, T> {
952    pub fn new(grid: &'a Grid<T>) -> DisplayIter<'a, T> {
953        let offset = grid.display_offset + *grid.num_lines() - 1;
954        let limit = grid.display_offset;
955        let col = Column(0);
956        let line = Line(0);
957
958        DisplayIter { grid, offset, col, limit, line }
959    }
960
961    pub fn offset(&self) -> usize {
962        self.offset
963    }
964
965    pub fn column(&self) -> Column {
966        self.col
967    }
968
969    pub fn line(&self) -> Line {
970        self.line
971    }
972}
973
974impl<'a, T: Copy + 'a> Iterator for DisplayIter<'a, T> {
975    type Item = Indexed<T>;
976
977    #[inline]
978    fn next(&mut self) -> Option<Self::Item> {
979        // Return None if we've reached the end.
980        if self.offset == self.limit && self.grid.num_cols() == self.col {
981            return None;
982        }
983
984        // Get the next item.
985        let item = Some(Indexed {
986            inner: self.grid.raw[self.offset][self.col],
987            line: self.line,
988            column: self.col,
989        });
990
991        // Update line/col to point to next item
992        self.col += 1;
993        if self.col == self.grid.num_cols() && self.offset != self.limit {
994            self.offset -= 1;
995
996            self.col = Column(0);
997            self.line = Line(*self.grid.lines - 1 - (self.offset - self.limit));
998        }
999
1000        item
1001    }
1002}