terminal_emulator/term/
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//! Exports the `Term` type which is a high-level API for the Grid
16use std::cmp::min;
17use std::ops::{Index, IndexMut, Range};
18use std::time::{Duration, Instant};
19use std::{io, ptr};
20
21use arraydeque::ArrayDeque;
22use unicode_width::UnicodeWidthChar;
23
24use crate::ansi::{
25    self, Attr, CharsetIndex, Color, CursorStyle, Handler, MouseCursor, NamedColor, StandardCharset,
26};
27use crate::grid::{
28    BidirectionalIterator, DisplayIter, Grid, IndexRegion, Indexed, Scroll, ViewportPosition,
29};
30use crate::index;
31use crate::selection::{self, Locations, Selection};
32use crate::term::cell::{Cell, LineLength};
33
34pub mod cell;
35
36/// A type that can expand a given point to a region
37///
38/// Usually this is implemented for some 2-D array type since
39/// points are two dimensional indices.
40pub trait Search {
41    /// Find the nearest semantic boundary _to the left_ of provided point.
42    fn semantic_search_left(&self, _: index::Point<usize>) -> index::Point<usize>;
43    /// Find the nearest semantic boundary _to the point_ of provided point.
44    fn semantic_search_right(&self, _: index::Point<usize>) -> index::Point<usize>;
45    /// Find the nearest URL boundary in both directions.
46    fn url_search(&self, _: index::Point<usize>) -> Option<String>;
47}
48
49impl Search for Term {
50    fn semantic_search_left(&self, mut point: index::Point<usize>) -> index::Point<usize> {
51        // Limit the starting point to the last line in the history
52        point.line = min(point.line, self.grid.len() - 1);
53
54        let mut iter = self.grid.iter_from(point);
55        let last_col = self.grid.num_cols() - index::Column(1);
56
57        while let Some(cell) = iter.prev() {
58            if self.semantic_escape_chars.contains(cell.c) {
59                break;
60            }
61
62            if iter.cur.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) {
63                break; // cut off if on new line or hit escape char
64            }
65
66            point = iter.cur;
67        }
68
69        point
70    }
71
72    fn semantic_search_right(&self, mut point: index::Point<usize>) -> index::Point<usize> {
73        // Limit the starting point to the last line in the history
74        point.line = min(point.line, self.grid.len() - 1);
75
76        let mut iter = self.grid.iter_from(point);
77        let last_col = self.grid.num_cols() - index::Column(1);
78
79        while let Some(cell) = iter.next() {
80            if self.semantic_escape_chars.contains(cell.c) {
81                break;
82            }
83
84            point = iter.cur;
85
86            if iter.cur.col == last_col && !cell.flags.contains(cell::Flags::WRAPLINE) {
87                break; // cut off if on new line or hit escape char
88            }
89        }
90
91        point
92    }
93
94    fn url_search(&self, _: index::Point<usize>) -> Option<String> {
95        None // TODO
96    }
97}
98
99impl selection::Dimensions for Term {
100    fn dimensions(&self) -> index::Point {
101        index::Point {
102            col: self.grid.num_cols(),
103            line: self.grid.num_lines(),
104        }
105    }
106}
107
108/// Iterator that yields cells needing render
109///
110/// Yields cells that require work to be displayed (that is, not a an empty
111/// background cell). Additionally, this manages some state of the grid only
112/// relevant for rendering like temporarily changing the cell with the cursor.
113///
114/// This manages the cursor during a render. The cursor location is inverted to
115/// draw it, and reverted after drawing to maintain state.
116pub struct RenderableCellsIter<'a> {
117    inner: DisplayIter<'a, Cell>,
118    grid: &'a Grid<Cell>,
119    cursor: &'a index::Point,
120    cursor_offset: usize,
121    mode: TermMode,
122    selection: Option<index::RangeInclusive<index::Linear>>,
123    cursor_cells: ArrayDeque<[Indexed<Cell>; 3]>,
124}
125
126impl<'a> RenderableCellsIter<'a> {
127    /// Create the renderable cells iterator
128    ///
129    /// The cursor and terminal mode are required for properly displaying the
130    /// cursor.
131    fn new<'b>(
132        grid: &'b Grid<Cell>,
133        cursor: &'b index::Point,
134        mode: TermMode,
135        selection: Option<Locations>,
136        cursor_style: CursorStyle,
137    ) -> RenderableCellsIter<'b> {
138        let cursor_offset = grid.line_to_offset(cursor.line);
139        let inner = grid.display_iter();
140
141        let mut selection_range = None;
142        if let Some(loc) = selection {
143            // Get on-screen lines of the selection's locations
144            let start_line = grid.buffer_line_to_visible(loc.start.line);
145            let end_line = grid.buffer_line_to_visible(loc.end.line);
146
147            // Get start/end locations based on what part of selection is on screen
148            let locations = match (start_line, end_line) {
149                (ViewportPosition::Visible(start_line), ViewportPosition::Visible(end_line)) => {
150                    Some((start_line, loc.start.col, end_line, loc.end.col))
151                }
152                (ViewportPosition::Visible(start_line), ViewportPosition::Above) => {
153                    Some((start_line, loc.start.col, index::Line(0), index::Column(0)))
154                }
155                (ViewportPosition::Below, ViewportPosition::Visible(end_line)) => {
156                    Some((grid.num_lines(), index::Column(0), end_line, loc.end.col))
157                }
158                (ViewportPosition::Below, ViewportPosition::Above) => Some((
159                    grid.num_lines(),
160                    index::Column(0),
161                    index::Line(0),
162                    index::Column(0),
163                )),
164                _ => None,
165            };
166
167            if let Some((start_line, start_col, end_line, end_col)) = locations {
168                // start and end *lines* are swapped as we switch from buffer to
169                // index::Line coordinates.
170                let mut end = index::Point {
171                    line: start_line,
172                    col: start_col,
173                };
174                let mut start = index::Point {
175                    line: end_line,
176                    col: end_col,
177                };
178
179                if start > end {
180                    ::std::mem::swap(&mut start, &mut end);
181                }
182
183                let cols = grid.num_cols();
184                let start = index::Linear(start.line.0 * cols.0 + start.col.0);
185                let end = index::Linear(end.line.0 * cols.0 + end.col.0);
186
187                // Update the selection
188                selection_range = Some(index::RangeInclusive::new(start, end));
189            }
190        }
191
192        RenderableCellsIter {
193            cursor,
194            cursor_offset,
195            grid,
196            inner,
197            mode,
198            selection: selection_range,
199            cursor_cells: ArrayDeque::new(),
200        }
201        .initialize(cursor_style)
202    }
203
204    fn push_cursor_cells(&mut self, original: Cell, cursor: Cell, wide: Cell) {
205        // Prints the char under the cell if cursor is situated on a non-empty cell
206        self.cursor_cells
207            .push_back(Indexed {
208                line: self.cursor.line,
209                column: self.cursor.col,
210                inner: original,
211            })
212            .expect("won't exceed capacity");
213
214        // Prints the cursor
215        self.cursor_cells
216            .push_back(Indexed {
217                line: self.cursor.line,
218                column: self.cursor.col,
219                inner: cursor,
220            })
221            .expect("won't exceed capacity");
222
223        // If cursor is over a wide (2 cell size) character,
224        // print the second cursor cell
225        if self.is_wide_cursor(&cursor) {
226            self.cursor_cells
227                .push_back(Indexed {
228                    line: self.cursor.line,
229                    column: self.cursor.col + 1,
230                    inner: wide,
231                })
232                .expect("won't exceed capacity");
233        }
234    }
235
236    fn populate_block_cursor(&mut self) {
237        let text_color = Color::Named(NamedColor::CursorText);
238        let cursor_color = Color::Named(NamedColor::Cursor);
239
240        let original_cell = self.grid[self.cursor];
241
242        let mut cursor_cell = self.grid[self.cursor];
243        cursor_cell.fg = text_color;
244        cursor_cell.bg = cursor_color;
245
246        let mut wide_cell = cursor_cell;
247        wide_cell.c = ' ';
248
249        self.push_cursor_cells(original_cell, cursor_cell, wide_cell);
250    }
251
252    fn populate_char_cursor(&mut self, cursor_cell_char: char, wide_cell_char: char) {
253        let original_cell = self.grid[self.cursor];
254
255        let mut cursor_cell = self.grid[self.cursor];
256        let cursor_color = Color::Named(NamedColor::Cursor);
257        cursor_cell.c = cursor_cell_char;
258        cursor_cell.fg = cursor_color;
259
260        let mut wide_cell = cursor_cell;
261        wide_cell.c = wide_cell_char;
262
263        self.push_cursor_cells(original_cell, cursor_cell, wide_cell);
264    }
265
266    fn populate_underline_cursor(&mut self) {
267        self.populate_char_cursor('_', '_');
268    }
269
270    fn populate_beam_cursor(&mut self) {
271        self.populate_char_cursor('|', ' ');
272    }
273
274    fn populate_box_cursor(&mut self) {
275        self.populate_char_cursor('█', ' ');
276    }
277
278    #[inline]
279    fn is_wide_cursor(&self, cell: &Cell) -> bool {
280        cell.flags.contains(cell::Flags::WIDE_CHAR) && (self.cursor.col + 1) < self.grid.num_cols()
281    }
282
283    /// Populates list of cursor cells with the original cell
284    fn populate_no_cursor(&mut self) {
285        self.cursor_cells
286            .push_back(Indexed {
287                line: self.cursor.line,
288                column: self.cursor.col,
289                inner: self.grid[self.cursor],
290            })
291            .expect("won't exceed capacity");
292    }
293
294    fn initialize(mut self, cursor_style: CursorStyle) -> Self {
295        if self.cursor_is_visible() {
296            match cursor_style {
297                CursorStyle::HollowBlock => {
298                    self.populate_box_cursor();
299                }
300                CursorStyle::Block => {
301                    self.populate_block_cursor();
302                }
303                CursorStyle::Beam => {
304                    self.populate_beam_cursor();
305                }
306                CursorStyle::Underline => {
307                    self.populate_underline_cursor();
308                }
309            }
310        } else {
311            self.populate_no_cursor();
312        }
313        self
314    }
315
316    /// Check if the cursor should be rendered.
317    #[inline]
318    fn cursor_is_visible(&self) -> bool {
319        self.mode.contains(mode::TermMode::SHOW_CURSOR) && self.grid.contains(self.cursor)
320    }
321
322    fn compute_fg(&self, fg: Color, cell: &Cell) -> Color {
323        use self::cell::Flags;
324        match fg {
325            Color::Spec(rgb) => Color::Spec(rgb),
326            Color::Named(ansi) => {
327                match cell.flags & Flags::DIM_BOLD {
328                    // If no bright foreground is set, treat it like the BOLD flag doesn't exist
329                    self::cell::Flags::DIM_BOLD if ansi == NamedColor::Foreground => {
330                        Color::Named(NamedColor::DimForeground)
331                    }
332                    self::cell::Flags::DIM | self::cell::Flags::DIM_BOLD => {
333                        Color::Named(ansi.to_dim())
334                    }
335                    // None of the above, keep original color.
336                    _ => Color::Named(ansi),
337                }
338            }
339            Color::Indexed(idx) => {
340                let idx = match (cell.flags & Flags::DIM_BOLD, idx) {
341                    (self::cell::Flags::BOLD, 0..=7) => idx + 8,
342                    (self::cell::Flags::DIM, 8..=15) => idx - 8,
343                    // TODO
344                    // (self::cell::Flags::DIM, 0..=7) => idx as usize + 260,
345                    _ => idx,
346                };
347
348                Color::Indexed(idx)
349            }
350        }
351    }
352}
353
354#[derive(Copy, Clone, Debug)]
355pub struct RenderableCell {
356    /// A _Display_ line (not necessarily an _Active_ line)
357    pub line: index::Line,
358    pub column: index::Column,
359    pub chars: [char; cell::MAX_ZEROWIDTH_CHARS + 1],
360    pub fg: Color,
361    pub bg: Color,
362    pub flags: cell::Flags,
363}
364
365impl<'a> Iterator for RenderableCellsIter<'a> {
366    type Item = RenderableCell;
367
368    /// Gets the next renderable cell
369    ///
370    /// Skips empty (background) cells and applies any flags to the cell state
371    /// (eg. invert fg and bg colors).
372    #[inline]
373    fn next(&mut self) -> Option<Self::Item> {
374        loop {
375            // Handle cursor
376            let cell = if self.cursor_offset == self.inner.offset()
377                && self.inner.column() == self.cursor.col
378            {
379                // Cursor cell
380                let mut cell = self.cursor_cells.pop_front().unwrap();
381                cell.line = self.inner.line();
382
383                // Since there may be multiple cursor cells (for a wide
384                // char), only update iteration position after all cursor
385                // cells have been drawn.
386                if self.cursor_cells.is_empty() {
387                    self.inner.next();
388                }
389                cell
390            } else {
391                use crate::index::Contains;
392
393                let cell = self.inner.next()?;
394
395                let index = index::Linear(cell.line.0 * self.grid.num_cols().0 + cell.column.0);
396
397                let selected = self
398                    .selection
399                    .as_ref()
400                    .map(|range| range.contains_(index))
401                    .unwrap_or(false);
402
403                // Skip empty cells
404                if cell.is_empty() && !selected {
405                    continue;
406                }
407
408                cell
409            };
410
411            // Apply inversion and lookup RGB values
412            let fg = self.compute_fg(cell.fg, &cell);
413            let bg = cell.bg;
414
415            return Some(RenderableCell {
416                line: cell.line,
417                column: cell.column,
418                flags: cell.flags,
419                chars: cell.chars(),
420                fg,
421                bg,
422            });
423        }
424    }
425}
426
427pub mod mode {
428    use bitflags::bitflags;
429
430    bitflags! {
431        pub struct TermMode: u16 {
432            const SHOW_CURSOR         = 0b00_0000_0000_0001;
433            const APP_CURSOR          = 0b00_0000_0000_0010;
434            const APP_KEYPAD          = 0b00_0000_0000_0100;
435            const MOUSE_REPORT_CLICK  = 0b00_0000_0000_1000;
436            const BRACKETED_PASTE     = 0b00_0000_0001_0000;
437            const SGR_MOUSE           = 0b00_0000_0010_0000;
438            const MOUSE_MOTION        = 0b00_0000_0100_0000;
439            const LINE_WRAP           = 0b00_0000_1000_0000;
440            const LINE_FEED_NEW_LINE  = 0b00_0001_0000_0000;
441            const ORIGIN              = 0b00_0010_0000_0000;
442            const INSERT              = 0b00_0100_0000_0000;
443            const FOCUS_IN_OUT        = 0b00_1000_0000_0000;
444            const ALT_SCREEN          = 0b01_0000_0000_0000;
445            const MOUSE_DRAG          = 0b10_0000_0000_0000;
446            const ANY                 = 0b11_1111_1111_1111;
447            const NONE                = 0;
448        }
449    }
450
451    impl Default for TermMode {
452        fn default() -> TermMode {
453            TermMode::SHOW_CURSOR | TermMode::LINE_WRAP
454        }
455    }
456}
457
458pub use self::mode::TermMode;
459
460trait CharsetMapping {
461    fn map(&self, c: char) -> char {
462        c
463    }
464}
465
466impl CharsetMapping for StandardCharset {
467    /// Switch/Map character to the active charset. Ascii is the common case and
468    /// for that we want to do as little as possible.
469    #[inline]
470    fn map(&self, c: char) -> char {
471        match *self {
472            StandardCharset::Ascii => c,
473            StandardCharset::SpecialCharacterAndLineDrawing => match c {
474                '`' => '◆',
475                'a' => '▒',
476                'b' => '\t',
477                'c' => '\u{000c}',
478                'd' => '\r',
479                'e' => '\n',
480                'f' => '°',
481                'g' => '±',
482                'h' => '\u{2424}',
483                'i' => '\u{000b}',
484                'j' => '┘',
485                'k' => '┐',
486                'l' => '┌',
487                'm' => '└',
488                'n' => '┼',
489                'o' => '⎺',
490                'p' => '⎻',
491                'q' => '─',
492                'r' => '⎼',
493                's' => '⎽',
494                't' => '├',
495                'u' => '┤',
496                'v' => '┴',
497                'w' => '┬',
498                'x' => '│',
499                'y' => '≤',
500                'z' => '≥',
501                '{' => 'π',
502                '|' => '≠',
503                '}' => '£',
504                '~' => '·',
505                _ => c,
506            },
507        }
508    }
509}
510
511#[derive(Default, Copy, Clone)]
512struct Charsets([StandardCharset; 4]);
513
514impl Index<CharsetIndex> for Charsets {
515    type Output = StandardCharset;
516    fn index(&self, index: CharsetIndex) -> &StandardCharset {
517        &self.0[index as usize]
518    }
519}
520
521impl IndexMut<CharsetIndex> for Charsets {
522    fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset {
523        &mut self.0[index as usize]
524    }
525}
526
527#[derive(Default, Copy, Clone)]
528pub struct Cursor {
529    /// The location of this cursor
530    pub point: index::Point,
531
532    /// Template cell when using this cursor
533    template: Cell,
534
535    /// Currently configured graphic character sets
536    charsets: Charsets,
537}
538
539pub struct VisualBell {
540    /// Visual bell duration
541    duration: Duration,
542
543    /// The last time the visual bell rang, if at all
544    start_time: Option<Instant>,
545}
546
547impl VisualBell {
548    pub fn new() -> VisualBell {
549        VisualBell {
550            duration: Duration::from_secs(1),
551            start_time: None,
552        }
553    }
554
555    /// Ring the visual bell, and return its intensity.
556    pub fn ring(&mut self) -> f64 {
557        let now = Instant::now();
558        self.start_time = Some(now);
559        0.0
560    }
561
562    /// Get the currently intensity of the visual bell. The bell's intensity
563    /// ramps down from 1.0 to 0.0 at a rate determined by the bell's duration.
564    pub fn intensity(&self) -> f64 {
565        0.0
566    }
567
568    /// Check whether or not the visual bell has completed "ringing".
569    pub fn completed(&mut self) -> bool {
570        match self.start_time {
571            Some(earlier) => {
572                if Instant::now().duration_since(earlier) >= self.duration {
573                    self.start_time = None;
574                }
575                false
576            }
577            None => true,
578        }
579    }
580}
581
582pub struct Term {
583    /// The grid
584    grid: Grid<Cell>,
585
586    /// Tracks if the next call to input will need to first handle wrapping.
587    /// This is true after the last column is set with the input function. Any function that
588    /// implicitly sets the line or column needs to set this to false to avoid wrapping twice.
589    /// input_needs_wrap ensures that cursor.col is always valid for use into indexing into
590    /// arrays. Without it we would have to sanitize cursor.col every time we used it.
591    input_needs_wrap: bool,
592
593    /// Got a request to set title; it's buffered here until next draw.
594    ///
595    /// Would be nice to avoid the allocation...
596    next_title: Option<String>,
597
598    /// Got a request to set the mouse cursor; it's buffered here until the next draw
599    next_mouse_cursor: Option<MouseCursor>,
600
601    /// Alternate grid
602    alt_grid: Grid<Cell>,
603
604    /// Alt is active
605    alt: bool,
606
607    /// The cursor
608    cursor: Cursor,
609
610    /// The graphic character set, out of `charsets`, which ASCII is currently
611    /// being mapped to
612    active_charset: CharsetIndex,
613
614    /// Tabstops
615    tabs: TabStops,
616
617    /// Mode flags
618    mode: TermMode,
619
620    /// Scroll region
621    scroll_region: Range<index::Line>,
622
623    /// Size
624    size_info: SizeInfo,
625
626    pub dirty: bool,
627
628    pub visual_bell: VisualBell,
629    pub next_is_urgent: Option<bool>,
630
631    /// Saved cursor from main grid
632    cursor_save: Cursor,
633
634    /// Saved cursor from alt grid
635    cursor_save_alt: Cursor,
636
637    semantic_escape_chars: String,
638
639    /// Current style of the cursor
640    cursor_style: Option<CursorStyle>,
641
642    /// Default style for resetting the cursor
643    default_cursor_style: CursorStyle,
644
645    dynamic_title: bool,
646
647    /// Number of spaces in one tab
648    tabspaces: usize,
649
650    /// Automatically scroll to bottom when new lines are added
651    auto_scroll: bool,
652
653    /// Hint that Alacritty should be closed
654    should_exit: bool,
655}
656
657/// Terminal size info
658#[derive(Debug, Copy, Clone)]
659pub struct SizeInfo {
660    /// Terminal window width
661    pub width: f32,
662
663    /// Terminal window height
664    pub height: f32,
665
666    /// Width of individual cell
667    pub cell_width: f32,
668
669    /// Height of individual cell
670    pub cell_height: f32,
671
672    /// Horizontal window padding
673    pub padding_x: f32,
674
675    /// Horizontal window padding
676    pub padding_y: f32,
677
678    /// DPI factor of the current window
679    pub dpr: f64,
680}
681
682impl SizeInfo {
683    #[inline]
684    pub fn lines(&self) -> index::Line {
685        index::Line(((self.height - 2. * self.padding_y) / self.cell_height) as usize)
686    }
687
688    #[inline]
689    pub fn cols(&self) -> index::Column {
690        index::Column(((self.width - 2. * self.padding_x) / self.cell_width) as usize)
691    }
692
693    pub fn contains_point(&self, x: usize, y: usize) -> bool {
694        x < (self.width - self.padding_x) as usize
695            && x >= self.padding_x as usize
696            && y < (self.height - self.padding_y) as usize
697            && y >= self.padding_y as usize
698    }
699
700    pub fn pixels_to_coords(&self, x: usize, y: usize) -> index::Point {
701        let col =
702            index::Column(x.saturating_sub(self.padding_x as usize) / (self.cell_width as usize));
703        let line =
704            index::Line(y.saturating_sub(self.padding_y as usize) / (self.cell_height as usize));
705
706        index::Point {
707            line: min(line, index::Line(self.lines().saturating_sub(1))),
708            col: min(col, index::Column(self.cols().saturating_sub(1))),
709        }
710    }
711}
712
713impl Term {
714    pub fn selection(&self) -> &Option<Selection> {
715        &self.grid.selection
716    }
717
718    pub fn selection_mut(&mut self) -> &mut Option<Selection> {
719        &mut self.grid.selection
720    }
721
722    #[inline]
723    pub fn get_next_title(&mut self) -> Option<String> {
724        self.next_title.take()
725    }
726
727    pub fn scroll_display(&mut self, scroll: Scroll) {
728        self.grid.scroll_display(scroll);
729        self.dirty = true;
730    }
731
732    #[inline]
733    pub fn get_next_mouse_cursor(&mut self) -> Option<MouseCursor> {
734        self.next_mouse_cursor.take()
735    }
736
737    pub fn new(size: SizeInfo) -> Term {
738        let num_cols = size.cols();
739        let num_lines = size.lines();
740
741        let semantic_escape_chars = "".to_owned();
742        let history_size = 1024; // TODO
743        let default_cursor_style = ansi::CursorStyle::Block;
744        let dynamic_title = true;
745        let auto_scroll = true;
746        let grid = Grid::new(num_lines, num_cols, history_size, Cell::default());
747        let alt = Grid::new(
748            num_lines,
749            num_cols,
750            0, /* scroll history */
751            Cell::default(),
752        );
753
754        let tabspaces = 4;
755        let tabs = TabStops::new(grid.num_cols(), tabspaces);
756
757        let scroll_region = index::Line(0)..grid.num_lines();
758
759        Term {
760            next_title: None,
761            next_mouse_cursor: None,
762            dirty: false,
763            visual_bell: VisualBell::new(),
764            next_is_urgent: None,
765            input_needs_wrap: false,
766            grid,
767            alt_grid: alt,
768            alt: false,
769            active_charset: Default::default(),
770            cursor: Default::default(),
771            cursor_save: Default::default(),
772            cursor_save_alt: Default::default(),
773            tabs,
774            mode: Default::default(),
775            scroll_region,
776            size_info: size,
777            semantic_escape_chars,
778            cursor_style: None,
779            default_cursor_style,
780            dynamic_title,
781            tabspaces,
782            auto_scroll,
783            should_exit: false,
784        }
785    }
786
787    #[inline]
788    pub fn needs_draw(&self) -> bool {
789        self.dirty
790    }
791
792    pub fn selection_to_string(&self) -> Option<String> {
793        /// Need a generic push() for the Append trait
794        trait PushChar {
795            fn push_char(&mut self, c: char);
796            fn maybe_newline(&mut self, grid: &Grid<Cell>, line: usize, ending: index::Column) {
797                if ending != index::Column(0)
798                    && !grid[line][ending - 1].flags.contains(cell::Flags::WRAPLINE)
799                {
800                    self.push_char('\n');
801                }
802            }
803        }
804
805        impl PushChar for String {
806            #[inline]
807            fn push_char(&mut self, c: char) {
808                self.push(c);
809            }
810        }
811
812        use std::ops::Range;
813
814        trait Append: PushChar {
815            fn append(
816                &mut self,
817                grid: &Grid<Cell>,
818                tabs: &TabStops,
819                line: usize,
820                cols: Range<index::Column>,
821            );
822        }
823
824        impl Append for String {
825            fn append(
826                &mut self,
827                grid: &Grid<Cell>,
828                tabs: &TabStops,
829                mut line: usize,
830                cols: Range<index::Column>,
831            ) {
832                // Select until last line still within the buffer
833                line = min(line, grid.len() - 1);
834
835                let grid_line = &grid[line];
836                let line_length = grid_line.line_length();
837                let line_end = min(line_length, cols.end + 1);
838
839                if line_end.0 == 0 && cols.end >= grid.num_cols() - 1 {
840                    self.push('\n');
841                } else if cols.start < line_end {
842                    let mut tab_mode = false;
843
844                    for col in index::Range::from(cols.start..line_end) {
845                        let cell = grid_line[col];
846
847                        if tab_mode {
848                            // Skip over whitespace until next tab-stop once a tab was found
849                            if tabs[col] {
850                                tab_mode = false;
851                            } else if cell.c == ' ' {
852                                continue;
853                            }
854                        }
855
856                        if !cell.flags.contains(cell::Flags::WIDE_CHAR_SPACER) {
857                            self.push(cell.c);
858                            for c in (&cell.chars()[1..]).iter().filter(|c| **c != ' ') {
859                                self.push(*c);
860                            }
861                        }
862
863                        if cell.c == '\t' {
864                            tab_mode = true;
865                        }
866                    }
867
868                    if cols.end >= grid.num_cols() - 1 {
869                        self.maybe_newline(grid, line, line_end);
870                    }
871                }
872            }
873        }
874
875        let alt_screen = self.mode.contains(TermMode::ALT_SCREEN);
876        let selection = self.grid.selection.clone()?;
877        let span = selection.to_span(self, alt_screen)?;
878
879        let mut res = String::new();
880
881        let Locations { mut start, mut end } = span.to_locations();
882
883        if start > end {
884            ::std::mem::swap(&mut start, &mut end);
885        }
886
887        let line_count = end.line - start.line;
888        let max_col = index::Column(usize::max_value() - 1);
889
890        match line_count {
891            // Selection within single line
892            0 => {
893                res.append(&self.grid, &self.tabs, start.line, start.col..end.col);
894            }
895
896            // Selection ends on line following start
897            1 => {
898                // Ending line
899                res.append(&self.grid, &self.tabs, end.line, end.col..max_col);
900
901                // Starting line
902                res.append(
903                    &self.grid,
904                    &self.tabs,
905                    start.line,
906                    index::Column(0)..start.col,
907                );
908            }
909
910            // Multi line selection
911            _ => {
912                // Ending line
913                res.append(&self.grid, &self.tabs, end.line, end.col..max_col);
914
915                let middle_range = (start.line + 1)..(end.line);
916                for line in middle_range.rev() {
917                    res.append(&self.grid, &self.tabs, line, index::Column(0)..max_col);
918                }
919
920                // Starting line
921                res.append(
922                    &self.grid,
923                    &self.tabs,
924                    start.line,
925                    index::Column(0)..start.col,
926                );
927            }
928        }
929
930        Some(res)
931    }
932
933    /// Convert the given pixel values to a grid coordinate
934    ///
935    /// The mouse coordinates are expected to be relative to the top left. The
936    /// line and column returned are also relative to the top left.
937    ///
938    /// Returns None if the coordinates are outside the screen
939    pub fn pixels_to_coords(&self, x: usize, y: usize) -> Option<index::Point> {
940        if self.size_info.contains_point(x, y) {
941            Some(self.size_info.pixels_to_coords(x, y))
942        } else {
943            None
944        }
945    }
946
947    /// Access to the raw grid data structure
948    ///
949    /// This is a bit of a hack; when the window is closed, the event processor
950    /// serializes the grid state to a file.
951    pub fn grid(&self) -> &Grid<Cell> {
952        &self.grid
953    }
954
955    // Mutable access for swapping out the grid during tests
956    #[cfg(test)]
957    pub fn grid_mut(&mut self) -> &mut Grid<Cell> {
958        &mut self.grid
959    }
960
961    /// Iterate over the *renderable* cells in the terminal
962    ///
963    /// A renderable cell is any cell which has content other than the default
964    /// background color.  Cells with an alternate background color are
965    /// considered renderable as are cells with any text content.
966    pub fn renderable_cells(&self) -> RenderableCellsIter {
967        let alt_screen = self.mode.contains(TermMode::ALT_SCREEN);
968        let selection = self
969            .grid
970            .selection
971            .as_ref()
972            .and_then(|s| s.to_span(self, alt_screen))
973            .map(|span| span.to_locations());
974
975        let cursor = self.cursor_style.unwrap_or(self.default_cursor_style);
976
977        RenderableCellsIter::new(&self.grid, &self.cursor.point, self.mode, selection, cursor)
978    }
979
980    /// Resize terminal to new dimensions
981    pub fn resize(&mut self, size: &SizeInfo) {
982        debug!("Resizing terminal");
983
984        // Bounds check; lots of math assumes width and height are > 0
985        if size.width as usize <= 2 * self.size_info.padding_x as usize
986            || size.height as usize <= 2 * self.size_info.padding_y as usize
987        {
988            return;
989        }
990
991        let old_cols = self.grid.num_cols();
992        let old_lines = self.grid.num_lines();
993        let mut num_cols = size.cols();
994        let mut num_lines = size.lines();
995
996        self.size_info = *size;
997
998        if old_cols == num_cols && old_lines == num_lines {
999            debug!("Term::resize dimensions unchanged");
1000            return;
1001        }
1002
1003        self.grid.selection = None;
1004        self.alt_grid.selection = None;
1005
1006        // Should not allow less than 1 col, causes all sorts of checks to be required.
1007        if num_cols <= index::Column(1) {
1008            num_cols = index::Column(2);
1009        }
1010
1011        // Should not allow less than 1 line, causes all sorts of checks to be required.
1012        if num_lines <= index::Line(1) {
1013            num_lines = index::Line(2);
1014        }
1015
1016        // Scroll up to keep cursor in terminal
1017        if self.cursor.point.line >= num_lines {
1018            let lines = self.cursor.point.line - num_lines + 1;
1019            self.grid
1020                .scroll_up(&(index::Line(0)..old_lines), lines, &self.cursor.template);
1021        }
1022
1023        // Scroll up alt grid as well
1024        if self.cursor_save_alt.point.line >= num_lines {
1025            let lines = self.cursor_save_alt.point.line - num_lines + 1;
1026            self.alt_grid.scroll_up(
1027                &(index::Line(0)..old_lines),
1028                lines,
1029                &self.cursor_save_alt.template,
1030            );
1031        }
1032
1033        // Move prompt down when growing if scrollback lines are available
1034        if num_lines > old_lines {
1035            if self.mode.contains(TermMode::ALT_SCREEN) {
1036                let growage = min(
1037                    num_lines - old_lines,
1038                    index::Line(self.alt_grid.scroll_limit()),
1039                );
1040                self.cursor_save.point.line += growage;
1041            } else {
1042                let growage = min(num_lines - old_lines, index::Line(self.grid.scroll_limit()));
1043                self.cursor.point.line += growage;
1044            }
1045        }
1046
1047        debug!(
1048            "New num_cols is {} and num_lines is {}",
1049            num_cols, num_lines
1050        );
1051
1052        // Resize grids to new size
1053        self.grid.resize(num_lines, num_cols, &Cell::default());
1054        self.alt_grid.resize(num_lines, num_cols, &Cell::default());
1055
1056        // Reset scrolling region to new size
1057        self.scroll_region = index::Line(0)..self.grid.num_lines();
1058
1059        // Ensure cursors are in-bounds.
1060        self.cursor.point.col = min(self.cursor.point.col, num_cols - 1);
1061        self.cursor.point.line = min(self.cursor.point.line, num_lines - 1);
1062        self.cursor_save.point.col = min(self.cursor_save.point.col, num_cols - 1);
1063        self.cursor_save.point.line = min(self.cursor_save.point.line, num_lines - 1);
1064        self.cursor_save_alt.point.col = min(self.cursor_save_alt.point.col, num_cols - 1);
1065        self.cursor_save_alt.point.line = min(self.cursor_save_alt.point.line, num_lines - 1);
1066
1067        // Recreate tabs list
1068        self.tabs = TabStops::new(self.grid.num_cols(), self.tabspaces);
1069    }
1070
1071    #[inline]
1072    pub fn size_info(&self) -> &SizeInfo {
1073        &self.size_info
1074    }
1075
1076    #[inline]
1077    pub fn mode(&self) -> &TermMode {
1078        &self.mode
1079    }
1080
1081    #[inline]
1082    pub fn cursor(&self) -> &Cursor {
1083        &self.cursor
1084    }
1085
1086    pub fn swap_alt(&mut self) {
1087        if self.alt {
1088            let template = &self.cursor.template;
1089            self.grid.region_mut(..).each(|c| c.reset(template));
1090        }
1091
1092        self.alt = !self.alt;
1093        ::std::mem::swap(&mut self.grid, &mut self.alt_grid);
1094    }
1095
1096    /// Scroll screen down
1097    ///
1098    /// Text moves down; clear at bottom
1099    /// Expects origin to be in scroll range.
1100    #[inline]
1101    fn scroll_down_relative(&mut self, origin: index::Line, mut lines: index::Line) {
1102        trace!(
1103            "Scrolling down relative: origin={}, lines={}",
1104            origin,
1105            lines
1106        );
1107        lines = min(lines, self.scroll_region.end - self.scroll_region.start);
1108        lines = min(lines, self.scroll_region.end - origin);
1109
1110        // Scroll between origin and bottom
1111        self.grid.scroll_down(
1112            &(origin..self.scroll_region.end),
1113            lines,
1114            &self.cursor.template,
1115        );
1116    }
1117
1118    /// Scroll screen up
1119    ///
1120    /// Text moves up; clear at top
1121    /// Expects origin to be in scroll range.
1122    #[inline]
1123    fn scroll_up_relative(&mut self, origin: index::Line, lines: index::Line) {
1124        trace!("Scrolling up relative: origin={}, lines={}", origin, lines);
1125        let lines = min(lines, self.scroll_region.end - self.scroll_region.start);
1126
1127        // Scroll from origin to bottom less number of lines
1128        self.grid.scroll_up(
1129            &(origin..self.scroll_region.end),
1130            lines,
1131            &self.cursor.template,
1132        );
1133    }
1134
1135    fn deccolm(&mut self) {
1136        // Setting 132 column font makes no sense, but run the other side effects
1137        // Clear scrolling region
1138        let scroll_region = index::Line(0)..self.grid.num_lines();
1139        self.set_scrolling_region(scroll_region);
1140
1141        // Clear grid
1142        let template = self.cursor.template;
1143        self.grid.region_mut(..).each(|c| c.reset(&template));
1144    }
1145
1146    #[inline]
1147    pub fn exit(&mut self) {
1148        self.should_exit = true;
1149    }
1150
1151    #[inline]
1152    pub fn should_exit(&self) -> bool {
1153        self.should_exit
1154    }
1155}
1156
1157impl ansi::TermInfo for Term {
1158    #[inline]
1159    fn lines(&self) -> index::Line {
1160        self.grid.num_lines()
1161    }
1162
1163    #[inline]
1164    fn cols(&self) -> index::Column {
1165        self.grid.num_cols()
1166    }
1167}
1168
1169impl ansi::Handler for Term {
1170    /// Set the window title
1171    #[inline]
1172    fn set_title(&mut self, title: &str) {
1173        if self.dynamic_title {
1174            self.next_title = Some(title.to_owned());
1175        }
1176    }
1177
1178    /// Set the mouse cursor
1179    #[inline]
1180    fn set_mouse_cursor(&mut self, cursor: MouseCursor) {
1181        self.next_mouse_cursor = Some(cursor);
1182    }
1183
1184    #[inline]
1185    fn set_cursor_style(&mut self, style: Option<CursorStyle>) {
1186        trace!("Setting cursor style {:?}", style);
1187        self.cursor_style = style;
1188    }
1189
1190    /// A character to be displayed
1191    #[inline]
1192    fn input(&mut self, c: char) {
1193        // If enabled, scroll to bottom when character is received
1194        if self.auto_scroll {
1195            self.scroll_display(Scroll::Bottom);
1196        }
1197
1198        if self.input_needs_wrap {
1199            if !self.mode.contains(mode::TermMode::LINE_WRAP) {
1200                return;
1201            }
1202
1203            trace!("Wrapping input");
1204
1205            {
1206                let location = index::Point {
1207                    line: self.cursor.point.line,
1208                    col: self.cursor.point.col,
1209                };
1210
1211                let cell = &mut self.grid[&location];
1212                cell.flags.insert(cell::Flags::WRAPLINE);
1213            }
1214
1215            if (self.cursor.point.line + 1) >= self.scroll_region.end {
1216                self.linefeed();
1217            } else {
1218                self.cursor.point.line += 1;
1219            }
1220
1221            self.cursor.point.col = index::Column(0);
1222            self.input_needs_wrap = false;
1223        }
1224
1225        // Number of cells the char will occupy
1226        if let Some(width) = c.width() {
1227            let num_cols = self.grid.num_cols();
1228
1229            // If in insert mode, first shift cells to the right.
1230            if self.mode.contains(mode::TermMode::INSERT)
1231                && self.cursor.point.col + width < num_cols
1232            {
1233                let line = self.cursor.point.line;
1234                let col = self.cursor.point.col;
1235                let line = &mut self.grid[line];
1236
1237                let src = line[col..].as_ptr();
1238                let dst = line[(col + width)..].as_mut_ptr();
1239                unsafe {
1240                    // memmove
1241                    ptr::copy(src, dst, (num_cols - col - width).0);
1242                }
1243            }
1244
1245            // Handle zero-width characters
1246            if width == 0 {
1247                let col = self.cursor.point.col.0.saturating_sub(1);
1248                let line = self.cursor.point.line;
1249                if self.grid[line][index::Column(col)]
1250                    .flags
1251                    .contains(cell::Flags::WIDE_CHAR_SPACER)
1252                {
1253                    col.saturating_sub(1);
1254                }
1255                self.grid[line][index::Column(col)].push_extra(c);
1256                return;
1257            }
1258
1259            let cell = &mut self.grid[&self.cursor.point];
1260            *cell = self.cursor.template;
1261            cell.c = self.cursor.charsets[self.active_charset].map(c);
1262
1263            // Handle wide chars
1264            if width == 2 {
1265                cell.flags.insert(cell::Flags::WIDE_CHAR);
1266
1267                if self.cursor.point.col + 1 < num_cols {
1268                    self.cursor.point.col += 1;
1269                    let spacer = &mut self.grid[&self.cursor.point];
1270                    *spacer = self.cursor.template;
1271                    spacer.flags.insert(cell::Flags::WIDE_CHAR_SPACER);
1272                }
1273            }
1274        }
1275
1276        if (self.cursor.point.col + 1) < self.grid.num_cols() {
1277            self.cursor.point.col += 1;
1278        } else {
1279            self.input_needs_wrap = true;
1280        }
1281    }
1282
1283    #[inline]
1284    fn goto(&mut self, line: index::Line, col: index::Column) {
1285        trace!("Going to: line={}, col={}", line, col);
1286        let (y_offset, max_y) = if self.mode.contains(mode::TermMode::ORIGIN) {
1287            (self.scroll_region.start, self.scroll_region.end - 1)
1288        } else {
1289            (index::Line(0), self.grid.num_lines() - 1)
1290        };
1291
1292        self.cursor.point.line = min(line + y_offset, max_y);
1293        self.cursor.point.col = min(col, self.grid.num_cols() - 1);
1294        self.input_needs_wrap = false;
1295    }
1296
1297    #[inline]
1298    fn goto_line(&mut self, line: index::Line) {
1299        trace!("Going to line: {}", line);
1300        self.goto(line, self.cursor.point.col)
1301    }
1302
1303    #[inline]
1304    fn goto_col(&mut self, col: index::Column) {
1305        trace!("Going to column: {}", col);
1306        self.goto(self.cursor.point.line, col)
1307    }
1308
1309    #[inline]
1310    fn insert_blank(&mut self, count: index::Column) {
1311        // Ensure inserting within terminal bounds
1312
1313        let count = min(count, self.size_info.cols() - self.cursor.point.col);
1314
1315        let source = self.cursor.point.col;
1316        let destination = self.cursor.point.col + count;
1317        let num_cells = (self.size_info.cols() - destination).0;
1318
1319        let line = &mut self.grid[self.cursor.point.line];
1320
1321        unsafe {
1322            let src = line[source..].as_ptr();
1323            let dst = line[destination..].as_mut_ptr();
1324
1325            ptr::copy(src, dst, num_cells);
1326        }
1327
1328        // Cells were just moved out towards the end of the line; fill in
1329        // between source and dest with blanks.
1330        let template = self.cursor.template;
1331        for c in &mut line[source..destination] {
1332            c.reset(&template);
1333        }
1334    }
1335
1336    #[inline]
1337    fn move_up(&mut self, lines: index::Line) {
1338        trace!("Moving up: {}", lines);
1339        let move_to = index::Line(self.cursor.point.line.0.saturating_sub(lines.0));
1340        self.goto(move_to, self.cursor.point.col)
1341    }
1342
1343    #[inline]
1344    fn move_down(&mut self, lines: index::Line) {
1345        trace!("Moving down: {}", lines);
1346        let move_to = self.cursor.point.line + lines;
1347        self.goto(move_to, self.cursor.point.col)
1348    }
1349
1350    #[inline]
1351    fn identify_terminal<W: io::Write>(&mut self, writer: &mut W) {
1352        let _ = writer.write_all(b"\x1b[?6c");
1353    }
1354
1355    #[inline]
1356    fn device_status<W: io::Write>(&mut self, writer: &mut W, arg: usize) {
1357        trace!("Reporting device status: {}", arg);
1358        match arg {
1359            5 => {
1360                let _ = writer.write_all(b"\x1b[0n");
1361            }
1362            6 => {
1363                let pos = self.cursor.point;
1364                let _ = write!(writer, "\x1b[{};{}R", pos.line + 1, pos.col + 1);
1365            }
1366            _ => debug!("unknown device status query: {}", arg),
1367        };
1368    }
1369
1370    #[inline]
1371    fn move_forward(&mut self, cols: index::Column) {
1372        trace!("Moving forward: {}", cols);
1373        self.cursor.point.col = min(self.cursor.point.col + cols, self.grid.num_cols() - 1);
1374        self.input_needs_wrap = false;
1375    }
1376
1377    #[inline]
1378    fn move_backward(&mut self, cols: index::Column) {
1379        trace!("Moving backward: {}", cols);
1380        self.cursor.point.col -= min(self.cursor.point.col, cols);
1381        self.input_needs_wrap = false;
1382    }
1383
1384    #[inline]
1385    fn move_down_and_cr(&mut self, lines: index::Line) {
1386        trace!("Moving down and cr: {}", lines);
1387        let move_to = self.cursor.point.line + lines;
1388        self.goto(move_to, index::Column(0))
1389    }
1390
1391    #[inline]
1392    fn move_up_and_cr(&mut self, lines: index::Line) {
1393        trace!("Moving up and cr: {}", lines);
1394        let move_to = index::Line(self.cursor.point.line.0.saturating_sub(lines.0));
1395        self.goto(move_to, index::Column(0))
1396    }
1397
1398    #[inline]
1399    fn put_tab(&mut self, mut count: i64) {
1400        trace!("Putting tab: {}", count);
1401
1402        while self.cursor.point.col < self.grid.num_cols() && count != 0 {
1403            count -= 1;
1404
1405            let cell = &mut self.grid[&self.cursor.point];
1406            if cell.c == ' ' {
1407                cell.c = self.cursor.charsets[self.active_charset].map('\t');
1408            }
1409
1410            loop {
1411                if (self.cursor.point.col + 1) == self.grid.num_cols() {
1412                    break;
1413                }
1414
1415                self.cursor.point.col += 1;
1416
1417                if self.tabs[self.cursor.point.col] {
1418                    break;
1419                }
1420            }
1421        }
1422
1423        self.input_needs_wrap = false;
1424    }
1425
1426    /// Backspace `count` characters
1427    #[inline]
1428    fn backspace(&mut self) {
1429        trace!("Backspace");
1430        if self.cursor.point.col > index::Column(0) {
1431            self.cursor.point.col -= 1;
1432            self.input_needs_wrap = false;
1433        }
1434    }
1435
1436    /// Carriage return
1437    #[inline]
1438    fn carriage_return(&mut self) {
1439        trace!("Carriage return");
1440        self.cursor.point.col = index::Column(0);
1441        self.input_needs_wrap = false;
1442    }
1443
1444    /// Linefeed
1445    #[inline]
1446    fn linefeed(&mut self) {
1447        trace!("Linefeed");
1448        let next = self.cursor.point.line + 1;
1449        if next == self.scroll_region.end {
1450            self.scroll_up(index::Line(1));
1451        } else if next < self.grid.num_lines() {
1452            self.cursor.point.line += 1;
1453        }
1454    }
1455
1456    /// Set current position as a tabstop
1457    #[inline]
1458    fn bell(&mut self) {
1459        trace!("Bell");
1460        self.visual_bell.ring();
1461        self.next_is_urgent = Some(true);
1462    }
1463
1464    #[inline]
1465    fn substitute(&mut self) {
1466        trace!("[unimplemented] Substitute");
1467    }
1468
1469    /// Run LF/NL
1470    ///
1471    /// LF/NL mode has some interesting history. According to ECMA-48 4th
1472    /// edition, in LINE FEED mode,
1473    ///
1474    /// > The execution of the formatter functions LINE FEED (LF), FORM FEED
1475    /// (FF), LINE TABULATION (VT) cause only movement of the active position in
1476    /// the direction of the line progression.
1477    ///
1478    /// In NEW LINE mode,
1479    ///
1480    /// > The execution of the formatter functions LINE FEED (LF), FORM FEED
1481    /// (FF), LINE TABULATION (VT) cause movement to the line home position on
1482    /// the following line, the following form, etc. In the case of LF this is
1483    /// referred to as the New index::Line (NL) option.
1484    ///
1485    /// Additionally, ECMA-48 4th edition says that this option is deprecated.
1486    /// ECMA-48 5th edition only mentions this option (without explanation)
1487    /// saying that it's been removed.
1488    ///
1489    /// As an emulator, we need to support it since applications may still rely
1490    /// on it.
1491    #[inline]
1492    fn newline(&mut self) {
1493        self.linefeed();
1494
1495        if self.mode.contains(mode::TermMode::LINE_FEED_NEW_LINE) {
1496            self.carriage_return();
1497        }
1498    }
1499
1500    #[inline]
1501    fn set_horizontal_tabstop(&mut self) {
1502        trace!("Setting horizontal tabstop");
1503        let column = self.cursor.point.col;
1504        self.tabs[column] = true;
1505    }
1506
1507    #[inline]
1508    fn scroll_up(&mut self, lines: index::Line) {
1509        let origin = self.scroll_region.start;
1510        self.scroll_up_relative(origin, lines);
1511    }
1512
1513    #[inline]
1514    fn scroll_down(&mut self, lines: index::Line) {
1515        let origin = self.scroll_region.start;
1516        self.scroll_down_relative(origin, lines);
1517    }
1518
1519    #[inline]
1520    fn insert_blank_lines(&mut self, lines: index::Line) {
1521        use crate::index::Contains;
1522        trace!("Inserting blank {} lines", lines);
1523        if self.scroll_region.contains_(self.cursor.point.line) {
1524            let origin = self.cursor.point.line;
1525            self.scroll_down_relative(origin, lines);
1526        }
1527    }
1528
1529    #[inline]
1530    fn delete_lines(&mut self, lines: index::Line) {
1531        use crate::index::Contains;
1532        trace!("Deleting {} lines", lines);
1533        if self.scroll_region.contains_(self.cursor.point.line) {
1534            let origin = self.cursor.point.line;
1535            self.scroll_up_relative(origin, lines);
1536        }
1537    }
1538
1539    #[inline]
1540    fn erase_chars(&mut self, count: index::Column) {
1541        trace!(
1542            "Erasing chars: count={}, col={}",
1543            count,
1544            self.cursor.point.col
1545        );
1546        let start = self.cursor.point.col;
1547        let end = min(start + count, self.grid.num_cols());
1548
1549        let row = &mut self.grid[self.cursor.point.line];
1550        let template = self.cursor.template; // Cleared cells have current background color set
1551        for c in &mut row[start..end] {
1552            c.reset(&template);
1553        }
1554    }
1555
1556    #[inline]
1557    fn delete_chars(&mut self, count: index::Column) {
1558        // Ensure deleting within terminal bounds
1559        let count = min(count, self.size_info.cols());
1560
1561        let start = self.cursor.point.col;
1562        let end = min(start + count, self.grid.num_cols() - 1);
1563        let n = (self.size_info.cols() - end).0;
1564
1565        let line = &mut self.grid[self.cursor.point.line];
1566
1567        unsafe {
1568            let src = line[end..].as_ptr();
1569            let dst = line[start..].as_mut_ptr();
1570
1571            ptr::copy(src, dst, n);
1572        }
1573
1574        // Clear last `count` cells in line. If deleting 1 char, need to delete
1575        // 1 cell.
1576        let template = self.cursor.template;
1577        let end = self.size_info.cols() - count;
1578        for c in &mut line[end..] {
1579            c.reset(&template);
1580        }
1581    }
1582
1583    #[inline]
1584    fn move_backward_tabs(&mut self, count: i64) {
1585        trace!("Moving backward {} tabs", count);
1586
1587        for _ in 0..count {
1588            let mut col = self.cursor.point.col;
1589            for i in (0..(col.0)).rev() {
1590                if self.tabs[index::Column(i)] {
1591                    col = index::Column(i);
1592                    break;
1593                }
1594            }
1595            self.cursor.point.col = col;
1596        }
1597    }
1598
1599    #[inline]
1600    fn move_forward_tabs(&mut self, count: i64) {
1601        trace!("[unimplemented] Moving forward {} tabs", count);
1602    }
1603
1604    #[inline]
1605    fn save_cursor_position(&mut self) {
1606        trace!("Saving cursor position");
1607        let cursor = if self.alt {
1608            &mut self.cursor_save_alt
1609        } else {
1610            &mut self.cursor_save
1611        };
1612
1613        *cursor = self.cursor;
1614    }
1615
1616    #[inline]
1617    fn restore_cursor_position(&mut self) {
1618        trace!("Restoring cursor position");
1619        let source = if self.alt {
1620            &self.cursor_save_alt
1621        } else {
1622            &self.cursor_save
1623        };
1624
1625        self.cursor = *source;
1626        self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1);
1627        self.cursor.point.col = min(self.cursor.point.col, self.grid.num_cols() - 1);
1628    }
1629
1630    #[inline]
1631    fn clear_line(&mut self, mode: ansi::LineClearMode) {
1632        trace!("Clearing line: {:?}", mode);
1633        let mut template = self.cursor.template;
1634        template.flags ^= template.flags;
1635
1636        let col = self.cursor.point.col;
1637
1638        match mode {
1639            ansi::LineClearMode::Right => {
1640                let row = &mut self.grid[self.cursor.point.line];
1641                for cell in &mut row[col..] {
1642                    cell.reset(&template);
1643                }
1644            }
1645            ansi::LineClearMode::Left => {
1646                let row = &mut self.grid[self.cursor.point.line];
1647                for cell in &mut row[..=col] {
1648                    cell.reset(&template);
1649                }
1650            }
1651            ansi::LineClearMode::All => {
1652                let row = &mut self.grid[self.cursor.point.line];
1653                for cell in &mut row[..] {
1654                    cell.reset(&template);
1655                }
1656            }
1657        }
1658    }
1659
1660    #[inline]
1661    fn clear_screen(&mut self, mode: ansi::ClearMode) {
1662        trace!("Clearing screen: {:?}", mode);
1663        let mut template = self.cursor.template;
1664        template.flags ^= template.flags;
1665
1666        // Remove active selections
1667        self.grid.selection = None;
1668
1669        match mode {
1670            ansi::ClearMode::Below => {
1671                for cell in &mut self.grid[self.cursor.point.line][self.cursor.point.col..] {
1672                    cell.reset(&template);
1673                }
1674                if self.cursor.point.line < self.grid.num_lines() - 1 {
1675                    self.grid
1676                        .region_mut((self.cursor.point.line + 1)..)
1677                        .each(|cell| cell.reset(&template));
1678                }
1679            }
1680            ansi::ClearMode::All => self.grid.region_mut(..).each(|c| c.reset(&template)),
1681            ansi::ClearMode::Above => {
1682                // If clearing more than one line
1683                if self.cursor.point.line > index::Line(1) {
1684                    // Fully clear all lines before the current line
1685                    self.grid
1686                        .region_mut(..self.cursor.point.line)
1687                        .each(|cell| cell.reset(&template));
1688                }
1689                // Clear up to the current column in the current line
1690                let end = min(self.cursor.point.col + 1, self.grid.num_cols());
1691                for cell in &mut self.grid[self.cursor.point.line][..end] {
1692                    cell.reset(&template);
1693                }
1694            }
1695            // If scrollback is implemented, this should clear it
1696            ansi::ClearMode::Saved => self.grid.clear_history(),
1697        }
1698    }
1699
1700    #[inline]
1701    fn clear_tabs(&mut self, mode: ansi::TabulationClearMode) {
1702        trace!("Clearing tabs: {:?}", mode);
1703        match mode {
1704            ansi::TabulationClearMode::Current => {
1705                let column = self.cursor.point.col;
1706                self.tabs[column] = false;
1707            }
1708            ansi::TabulationClearMode::All => {
1709                self.tabs.clear_all();
1710            }
1711        }
1712    }
1713
1714    // Reset all important fields in the term struct
1715    #[inline]
1716    fn reset_state(&mut self) {
1717        self.input_needs_wrap = false;
1718        self.next_title = None;
1719        self.next_mouse_cursor = None;
1720        self.alt = false;
1721        self.cursor = Default::default();
1722        self.active_charset = Default::default();
1723        self.mode = Default::default();
1724        self.next_is_urgent = None;
1725        self.cursor_save = Default::default();
1726        self.cursor_save_alt = Default::default();
1727        self.cursor_style = None;
1728        self.grid.clear_history();
1729        self.grid.region_mut(..).each(|c| c.reset(&Cell::default()));
1730    }
1731
1732    #[inline]
1733    fn reverse_index(&mut self) {
1734        trace!("Reversing index");
1735        // if cursor is at the top
1736        if self.cursor.point.line == self.scroll_region.start {
1737            self.scroll_down(index::Line(1));
1738        } else {
1739            self.cursor.point.line -= min(self.cursor.point.line, index::Line(1));
1740        }
1741    }
1742
1743    /// set a terminal attribute
1744    #[inline]
1745    fn terminal_attribute(&mut self, attr: Attr) {
1746        trace!("Setting attribute: {:?}", attr);
1747        match attr {
1748            Attr::Foreground(color) => self.cursor.template.fg = color,
1749            Attr::Background(color) => self.cursor.template.bg = color,
1750            Attr::Reset => {
1751                self.cursor.template.fg = Color::Named(NamedColor::Foreground);
1752                self.cursor.template.bg = Color::Named(NamedColor::Background);
1753                self.cursor.template.flags = cell::Flags::empty();
1754            }
1755            Attr::Reverse => self.cursor.template.flags.insert(cell::Flags::INVERSE),
1756            Attr::CancelReverse => self.cursor.template.flags.remove(cell::Flags::INVERSE),
1757            Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD),
1758            Attr::CancelBold => self.cursor.template.flags.remove(cell::Flags::BOLD),
1759            Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM),
1760            Attr::CancelBoldDim => self
1761                .cursor
1762                .template
1763                .flags
1764                .remove(cell::Flags::BOLD | cell::Flags::DIM),
1765            Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC),
1766            Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC),
1767            Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE),
1768            Attr::CancelUnderline => self.cursor.template.flags.remove(cell::Flags::UNDERLINE),
1769            Attr::Hidden => self.cursor.template.flags.insert(cell::Flags::HIDDEN),
1770            Attr::CancelHidden => self.cursor.template.flags.remove(cell::Flags::HIDDEN),
1771            Attr::Strike => self.cursor.template.flags.insert(cell::Flags::STRIKEOUT),
1772            Attr::CancelStrike => self.cursor.template.flags.remove(cell::Flags::STRIKEOUT),
1773            _ => {
1774                debug!("Term got unhandled attr: {:?}", attr);
1775            }
1776        }
1777    }
1778
1779    #[inline]
1780    fn set_mode(&mut self, mode: ansi::Mode) {
1781        trace!("Setting mode: {:?}", mode);
1782        match mode {
1783            ansi::Mode::SwapScreenAndSetRestoreCursor => {
1784                self.mode.insert(mode::TermMode::ALT_SCREEN);
1785                self.save_cursor_position();
1786                if !self.alt {
1787                    self.swap_alt();
1788                }
1789                self.save_cursor_position();
1790            }
1791            ansi::Mode::ShowCursor => self.mode.insert(mode::TermMode::SHOW_CURSOR),
1792            ansi::Mode::CursorKeys => self.mode.insert(mode::TermMode::APP_CURSOR),
1793            ansi::Mode::ReportMouseClicks => {
1794                self.mode.insert(mode::TermMode::MOUSE_REPORT_CLICK);
1795                self.set_mouse_cursor(MouseCursor::Arrow);
1796            }
1797            ansi::Mode::ReportCellMouseMotion => {
1798                self.mode.insert(mode::TermMode::MOUSE_DRAG);
1799                self.set_mouse_cursor(MouseCursor::Arrow);
1800            }
1801            ansi::Mode::ReportAllMouseMotion => {
1802                self.mode.insert(mode::TermMode::MOUSE_MOTION);
1803                self.set_mouse_cursor(MouseCursor::Arrow);
1804            }
1805            ansi::Mode::ReportFocusInOut => self.mode.insert(mode::TermMode::FOCUS_IN_OUT),
1806            ansi::Mode::BracketedPaste => self.mode.insert(mode::TermMode::BRACKETED_PASTE),
1807            ansi::Mode::SgrMouse => self.mode.insert(mode::TermMode::SGR_MOUSE),
1808            ansi::Mode::LineWrap => self.mode.insert(mode::TermMode::LINE_WRAP),
1809            ansi::Mode::LineFeedNewLine => self.mode.insert(mode::TermMode::LINE_FEED_NEW_LINE),
1810            ansi::Mode::Origin => self.mode.insert(mode::TermMode::ORIGIN),
1811            ansi::Mode::DECCOLM => self.deccolm(),
1812            ansi::Mode::Insert => self.mode.insert(mode::TermMode::INSERT), // heh
1813            ansi::Mode::BlinkingCursor => {
1814                trace!("... unimplemented mode");
1815            }
1816        }
1817    }
1818
1819    #[inline]
1820    fn unset_mode(&mut self, mode: ansi::Mode) {
1821        trace!("Unsetting mode: {:?}", mode);
1822        match mode {
1823            ansi::Mode::SwapScreenAndSetRestoreCursor => {
1824                self.mode.remove(mode::TermMode::ALT_SCREEN);
1825                self.restore_cursor_position();
1826                if self.alt {
1827                    self.swap_alt();
1828                }
1829                self.restore_cursor_position();
1830            }
1831            ansi::Mode::ShowCursor => self.mode.remove(mode::TermMode::SHOW_CURSOR),
1832            ansi::Mode::CursorKeys => self.mode.remove(mode::TermMode::APP_CURSOR),
1833            ansi::Mode::ReportMouseClicks => {
1834                self.mode.remove(mode::TermMode::MOUSE_REPORT_CLICK);
1835                self.set_mouse_cursor(MouseCursor::Text);
1836            }
1837            ansi::Mode::ReportCellMouseMotion => {
1838                self.mode.remove(mode::TermMode::MOUSE_DRAG);
1839                self.set_mouse_cursor(MouseCursor::Text);
1840            }
1841            ansi::Mode::ReportAllMouseMotion => {
1842                self.mode.remove(mode::TermMode::MOUSE_MOTION);
1843                self.set_mouse_cursor(MouseCursor::Text);
1844            }
1845            ansi::Mode::ReportFocusInOut => self.mode.remove(mode::TermMode::FOCUS_IN_OUT),
1846            ansi::Mode::BracketedPaste => self.mode.remove(mode::TermMode::BRACKETED_PASTE),
1847            ansi::Mode::SgrMouse => self.mode.remove(mode::TermMode::SGR_MOUSE),
1848            ansi::Mode::LineWrap => self.mode.remove(mode::TermMode::LINE_WRAP),
1849            ansi::Mode::LineFeedNewLine => self.mode.remove(mode::TermMode::LINE_FEED_NEW_LINE),
1850            ansi::Mode::Origin => self.mode.remove(mode::TermMode::ORIGIN),
1851            ansi::Mode::DECCOLM => self.deccolm(),
1852            ansi::Mode::Insert => self.mode.remove(mode::TermMode::INSERT),
1853            ansi::Mode::BlinkingCursor => {
1854                trace!("... unimplemented mode");
1855            }
1856        }
1857    }
1858
1859    #[inline]
1860    fn set_scrolling_region(&mut self, region: Range<index::Line>) {
1861        trace!("Setting scrolling region: {:?}", region);
1862        self.scroll_region.start = min(region.start, self.grid.num_lines());
1863        self.scroll_region.end = min(region.end, self.grid.num_lines());
1864        self.goto(index::Line(0), index::Column(0));
1865    }
1866
1867    #[inline]
1868    fn set_keypad_application_mode(&mut self) {
1869        trace!("Setting keypad application mode");
1870        self.mode.insert(mode::TermMode::APP_KEYPAD);
1871    }
1872
1873    #[inline]
1874    fn unset_keypad_application_mode(&mut self) {
1875        trace!("Unsetting keypad application mode");
1876        self.mode.remove(mode::TermMode::APP_KEYPAD);
1877    }
1878
1879    #[inline]
1880    fn set_active_charset(&mut self, index: CharsetIndex) {
1881        trace!("Setting active charset {:?}", index);
1882        self.active_charset = index;
1883    }
1884
1885    #[inline]
1886    fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
1887        trace!("Configuring charset {:?} as {:?}", index, charset);
1888        self.cursor.charsets[index] = charset;
1889    }
1890
1891    /// Set the clipboard
1892    #[inline]
1893    fn set_clipboard(&mut self, _string: &str) {
1894        // TODO
1895    }
1896
1897    #[inline]
1898    fn dectest(&mut self) {
1899        trace!("Dectesting");
1900        let mut template = self.cursor.template;
1901        template.c = 'E';
1902
1903        self.grid.region_mut(..).each(|c| c.reset(&template));
1904    }
1905}
1906
1907struct TabStops {
1908    tabs: Vec<bool>,
1909}
1910
1911impl TabStops {
1912    fn new(num_cols: index::Column, tabspaces: usize) -> TabStops {
1913        TabStops {
1914            tabs: index::Range::from(index::Column(0)..num_cols)
1915                .map(|i| (*i as usize) % tabspaces == 0)
1916                .collect::<Vec<bool>>(),
1917        }
1918    }
1919
1920    fn clear_all(&mut self) {
1921        unsafe {
1922            ptr::write_bytes(self.tabs.as_mut_ptr(), 0, self.tabs.len());
1923        }
1924    }
1925}
1926
1927impl Index<index::Column> for TabStops {
1928    type Output = bool;
1929
1930    fn index(&self, index: index::Column) -> &bool {
1931        &self.tabs[index.0]
1932    }
1933}
1934
1935impl IndexMut<index::Column> for TabStops {
1936    fn index_mut(&mut self, index: index::Column) -> &mut bool {
1937        self.tabs.index_mut(index.0)
1938    }
1939}
1940
1941#[cfg(test)]
1942mod tests {
1943    use super::{Cell, SizeInfo, Term};
1944    use crate::term::cell;
1945
1946    use crate::ansi::{self, CharsetIndex, Handler, StandardCharset};
1947    use crate::grid::{Grid, Scroll};
1948    use crate::index;
1949    use crate::selection::Selection;
1950    use std::mem;
1951
1952    #[test]
1953    fn semantic_selection_works() {
1954        let size = SizeInfo {
1955            width: 21.0,
1956            height: 51.0,
1957            cell_width: 3.0,
1958            cell_height: 3.0,
1959            padding_x: 0.0,
1960            padding_y: 0.0,
1961            dpr: 1.0,
1962        };
1963        let mut term = Term::new(size);
1964        let mut grid: Grid<Cell> = Grid::new(index::Line(3), index::Column(5), 0, Cell::default());
1965        for i in 0..5 {
1966            for j in 0..2 {
1967                grid[index::Line(j)][index::Column(i)].c = 'a';
1968            }
1969        }
1970        grid[index::Line(0)][index::Column(0)].c = '"';
1971        grid[index::Line(0)][index::Column(3)].c = '"';
1972        grid[index::Line(1)][index::Column(2)].c = '"';
1973        grid[index::Line(0)][index::Column(4)]
1974            .flags
1975            .insert(cell::Flags::WRAPLINE);
1976
1977        let mut escape_chars = String::from("\"");
1978
1979        mem::swap(&mut term.grid, &mut grid);
1980        mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
1981
1982        {
1983            *term.selection_mut() = Some(Selection::semantic(index::Point {
1984                line: 2,
1985                col: index::Column(1),
1986            }));
1987            assert_eq!(term.selection_to_string(), Some(String::from("aa")));
1988        }
1989
1990        {
1991            *term.selection_mut() = Some(Selection::semantic(index::Point {
1992                line: 2,
1993                col: index::Column(4),
1994            }));
1995            assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
1996        }
1997
1998        {
1999            *term.selection_mut() = Some(Selection::semantic(index::Point {
2000                line: 1,
2001                col: index::Column(1),
2002            }));
2003            assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
2004        }
2005    }
2006
2007    #[test]
2008    fn line_selection_works() {
2009        let size = SizeInfo {
2010            width: 21.0,
2011            height: 51.0,
2012            cell_width: 3.0,
2013            cell_height: 3.0,
2014            padding_x: 0.0,
2015            padding_y: 0.0,
2016            dpr: 1.0,
2017        };
2018        let mut term = Term::new(size);
2019        let mut grid: Grid<Cell> = Grid::new(index::Line(1), index::Column(5), 0, Cell::default());
2020        for i in 0..5 {
2021            grid[index::Line(0)][index::Column(i)].c = 'a';
2022        }
2023        grid[index::Line(0)][index::Column(0)].c = '"';
2024        grid[index::Line(0)][index::Column(3)].c = '"';
2025
2026        mem::swap(&mut term.grid, &mut grid);
2027
2028        *term.selection_mut() = Some(Selection::lines(index::Point {
2029            line: 0,
2030            col: index::Column(3),
2031        }));
2032        assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n")));
2033    }
2034
2035    #[test]
2036    fn selecting_empty_line() {
2037        let size = SizeInfo {
2038            width: 21.0,
2039            height: 51.0,
2040            cell_width: 3.0,
2041            cell_height: 3.0,
2042            padding_x: 0.0,
2043            padding_y: 0.0,
2044            dpr: 1.0,
2045        };
2046        let mut term = Term::new(size);
2047        let mut grid: Grid<Cell> = Grid::new(index::Line(3), index::Column(3), 0, Cell::default());
2048        for l in 0..3 {
2049            if l != 1 {
2050                for c in 0..3 {
2051                    grid[index::Line(l)][index::Column(c)].c = 'a';
2052                }
2053            }
2054        }
2055
2056        mem::swap(&mut term.grid, &mut grid);
2057
2058        let mut selection = Selection::simple(
2059            index::Point {
2060                line: 2,
2061                col: index::Column(0),
2062            },
2063            index::Side::Left,
2064        );
2065        selection.update(
2066            index::Point {
2067                line: 0,
2068                col: index::Column(2),
2069            },
2070            index::Side::Right,
2071        );
2072        *term.selection_mut() = Some(selection);
2073        assert_eq!(term.selection_to_string(), Some("aaa\n\naaa\n".into()));
2074    }
2075
2076    #[test]
2077    fn input_line_drawing_character() {
2078        let size = SizeInfo {
2079            width: 21.0,
2080            height: 51.0,
2081            cell_width: 3.0,
2082            cell_height: 3.0,
2083            padding_x: 0.0,
2084            padding_y: 0.0,
2085            dpr: 1.0,
2086        };
2087        let mut term = Term::new(size);
2088        let cursor = index::Point::new(index::Line(0), index::Column(0));
2089        term.configure_charset(
2090            CharsetIndex::G0,
2091            StandardCharset::SpecialCharacterAndLineDrawing,
2092        );
2093        term.input('a');
2094
2095        assert_eq!(term.grid()[&cursor].c, '▒');
2096    }
2097
2098    #[test]
2099    fn clear_saved_lines() {
2100        let size = SizeInfo {
2101            width: 21.0,
2102            height: 51.0,
2103            cell_width: 3.0,
2104            cell_height: 3.0,
2105            padding_x: 0.0,
2106            padding_y: 0.0,
2107            dpr: 1.0,
2108        };
2109        let mut term: Term = Term::new(size);
2110
2111        // Add one line of scrollback
2112        term.grid.scroll_up(
2113            &(index::Line(0)..index::Line(1)),
2114            index::Line(1),
2115            &Cell::default(),
2116        );
2117
2118        // Clear the history
2119        term.clear_screen(ansi::ClearMode::Saved);
2120
2121        // Make sure that scrolling does not change the grid
2122        let mut scrolled_grid = term.grid.clone();
2123        scrolled_grid.scroll_display(Scroll::Top);
2124        assert_eq!(term.grid, scrolled_grid);
2125    }
2126}
2127
2128#[cfg(all(test, feature = "bench"))]
2129mod benches {
2130    extern crate test;
2131
2132    use std::fs::File;
2133    use std::io::Read;
2134    use std::mem;
2135    use std::path::Path;
2136
2137    use crate::config::Config;
2138    use crate::grid::Grid;
2139    use crate::message_bar::MessageBuffer;
2140
2141    use super::cell::Cell;
2142    use super::{SizeInfo, Term};
2143
2144    fn read_string<P>(path: P) -> String
2145    where
2146        P: AsRef<Path>,
2147    {
2148        let mut res = String::new();
2149        File::open(path.as_ref())
2150            .unwrap()
2151            .read_to_string(&mut res)
2152            .unwrap();
2153
2154        res
2155    }
2156
2157    /// Benchmark for the renderable cells iterator
2158    ///
2159    /// The renderable cells iterator yields cells that require work to be
2160    /// displayed (that is, not a an empty background cell). This benchmark
2161    /// measures how long it takes to process the whole iterator.
2162    ///
2163    /// When this benchmark was first added, it averaged ~78usec on my macbook
2164    /// pro. The total render time for this grid is anywhere between ~1500 and
2165    /// ~2000usec (measured imprecisely with the visual meter).
2166    #[bench]
2167    fn render_iter(b: &mut test::Bencher) {
2168        // Need some realistic grid state; using one of the ref files.
2169        let serialized_grid = read_string(concat!(
2170            env!("CARGO_MANIFEST_DIR"),
2171            "/tests/ref/vim_large_window_scroll/grid.json"
2172        ));
2173        let serialized_size = read_string(concat!(
2174            env!("CARGO_MANIFEST_DIR"),
2175            "/tests/ref/vim_large_window_scroll/size.json"
2176        ));
2177
2178        let mut grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
2179        let size: SizeInfo = json::from_str(&serialized_size).unwrap();
2180
2181        let config = Config::default();
2182
2183        let mut terminal = Term::new(size);
2184        mem::swap(&mut terminal.grid, &mut grid);
2185
2186        b.iter(|| {
2187            let iter = terminal.renderable_cells(&config, false);
2188            for cell in iter {
2189                test::black_box(cell);
2190            }
2191        })
2192    }
2193}