1use std::cmp::{max, min};
17use std::ops::{Index, IndexMut, Range};
18use std::time::{Duration, Instant};
19use std::{io, mem, ptr, str};
20
21use log::{debug, trace};
22use serde::{Deserialize, Serialize};
23use unicode_width::UnicodeWidthChar;
24
25use crate::ansi::{
26 self, Attr, CharsetIndex, Color, CursorStyle, Handler, NamedColor, StandardCharset, TermInfo,
27};
28use crate::clipboard::{Clipboard, ClipboardType};
29use crate::config::{Config, VisualBellAnimation, DEFAULT_NAME};
30use crate::event::{Event, EventListener};
31use crate::grid::{
32 BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll,
33};
34use crate::index::{self, Column, IndexRange, Line, Point};
35use crate::selection::{Selection, SelectionRange};
36use crate::term::cell::{Cell, Flags, LineLength};
37use crate::term::color::Rgb;
38
39pub mod cell;
40pub mod color;
41
42const BRACKET_PAIRS: [(char, char); 4] = [('(', ')'), ('[', ']'), ('{', '}'), ('<', '>')];
44
45const TITLE_STACK_MAX_DEPTH: usize = 4096;
47
48pub trait Search {
53 fn semantic_search_left(&self, _: Point<usize>) -> Point<usize>;
55 fn semantic_search_right(&self, _: Point<usize>) -> Point<usize>;
57 fn line_search_left(&self, _: Point<usize>) -> Point<usize>;
59 fn line_search_right(&self, _: Point<usize>) -> Point<usize>;
61 fn bracket_search(&self, _: Point<usize>) -> Option<Point<usize>>;
63}
64
65impl<T> Search for Term<T> {
66 fn semantic_search_left(&self, mut point: Point<usize>) -> Point<usize> {
67 point.line = min(point.line, self.grid.len() - 1);
69
70 let mut iter = self.grid.iter_from(point);
71 let last_col = self.grid.num_cols() - Column(1);
72
73 while let Some(cell) = iter.prev() {
74 if !cell.flags.intersects(Flags::WIDE_CHAR | Flags::WIDE_CHAR_SPACER)
75 && self.semantic_escape_chars.contains(cell.c)
76 {
77 break;
78 }
79
80 if iter.point().col == last_col && !cell.flags.contains(Flags::WRAPLINE) {
81 break; }
83
84 point = iter.point();
85 }
86
87 point
88 }
89
90 fn semantic_search_right(&self, mut point: Point<usize>) -> Point<usize> {
91 point.line = min(point.line, self.grid.len() - 1);
93
94 let mut iter = self.grid.iter_from(point);
95 let last_col = self.grid.num_cols() - 1;
96
97 while let Some(cell) = iter.next() {
98 if !cell.flags.intersects(Flags::WIDE_CHAR | Flags::WIDE_CHAR_SPACER)
99 && self.semantic_escape_chars.contains(cell.c)
100 {
101 break;
102 }
103
104 point = iter.point();
105
106 if point.col == last_col && !cell.flags.contains(Flags::WRAPLINE) {
107 break; }
109 }
110
111 point
112 }
113
114 fn line_search_left(&self, mut point: Point<usize>) -> Point<usize> {
115 while point.line + 1 < self.grid.len()
116 && self.grid[point.line + 1][self.grid.num_cols() - 1].flags.contains(Flags::WRAPLINE)
117 {
118 point.line += 1;
119 }
120
121 point.col = Column(0);
122
123 point
124 }
125
126 fn line_search_right(&self, mut point: Point<usize>) -> Point<usize> {
127 while self.grid[point.line][self.grid.num_cols() - 1].flags.contains(Flags::WRAPLINE) {
128 point.line -= 1;
129 }
130
131 point.col = self.grid.num_cols() - 1;
132
133 point
134 }
135
136 fn bracket_search(&self, point: Point<usize>) -> Option<Point<usize>> {
137 let start_char = self.grid[point.line][point.col].c;
138
139 let (forwards, end_char) = BRACKET_PAIRS.iter().find_map(|(open, close)| {
141 if open == &start_char {
142 Some((true, *close))
143 } else if close == &start_char {
144 Some((false, *open))
145 } else {
146 None
147 }
148 })?;
149
150 let mut iter = self.grid.iter_from(point);
151
152 let mut skip_pairs = 0;
155
156 loop {
157 let cell = if forwards { iter.next() } else { iter.prev() };
159
160 let c = match cell {
162 Some(cell) => cell.c,
163 None => break,
164 };
165
166 if c == end_char && skip_pairs == 0 {
168 return Some(iter.point());
169 } else if c == start_char {
170 skip_pairs += 1;
171 } else if c == end_char {
172 skip_pairs -= 1;
173 }
174 }
175
176 None
177 }
178}
179
180#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, Deserialize)]
182pub struct CursorKey {
183 pub style: CursorStyle,
184 pub is_wide: bool,
185}
186
187pub struct RenderableCellsIter<'a, C> {
196 inner: DisplayIter<'a, Cell>,
197 grid: &'a Grid<Cell>,
198 cursor: &'a Point,
199 cursor_offset: usize,
200 cursor_key: Option<CursorKey>,
201 cursor_style: CursorStyle,
202 config: &'a Config<C>,
203 colors: &'a color::List,
204 selection: Option<SelectionRange<Line>>,
205}
206
207impl<'a, C> RenderableCellsIter<'a, C> {
208 fn new<'b, T>(
213 term: &'b Term<T>,
214 config: &'b Config<C>,
215 selection: Option<SelectionRange>,
216 mut cursor_style: CursorStyle,
217 ) -> RenderableCellsIter<'b, C> {
218 let grid = &term.grid;
219 let num_cols = grid.num_cols();
220 let num_lines = grid.num_lines();
221
222 let cursor_offset = grid.line_to_offset(term.cursor.point.line);
223 let inner = grid.display_iter();
224
225 let selection_range = selection.and_then(|span| {
226 let (limit_start, limit_end) = if span.is_block {
227 (span.start.col, span.end.col)
228 } else {
229 (Column(0), num_cols - 1)
230 };
231
232 let start = term.buffer_to_visible(span.start);
234 let end = term.buffer_to_visible(span.end);
235
236 let (mut start, mut end) = match (start, end) {
238 (Some(start), Some(end)) => (start, end),
239 (Some(start), None) => {
240 let end = Point::new(num_lines.0 - 1, num_cols - 1);
241 (start, end)
242 },
243 (None, Some(end)) => {
244 let start = Point::new(0, Column(0));
245 (start, end)
246 },
247 (None, None) => return None,
248 };
249
250 start.col = max(limit_start, start.col);
252 end.col = min(limit_end, end.col);
253
254 Some(SelectionRange::new(start.into(), end.into(), span.is_block))
255 });
256
257 let cursor = &term.cursor.point;
259 let cursor_visible = term.mode.contains(TermMode::SHOW_CURSOR) && grid.contains(cursor);
260 let cursor_key = if cursor_visible {
261 let is_wide =
262 grid[cursor].flags.contains(Flags::WIDE_CHAR) && (cursor.col + 1) < num_cols;
263 Some(CursorKey { style: cursor_style, is_wide })
264 } else {
265 cursor_style = CursorStyle::Hidden;
267 None
268 };
269
270 RenderableCellsIter {
271 cursor,
272 cursor_offset,
273 grid,
274 inner,
275 selection: selection_range,
276 config,
277 colors: &term.colors,
278 cursor_key,
279 cursor_style,
280 }
281 }
282}
283
284#[derive(Copy, Clone, Debug)]
285pub enum RenderableCellContent {
286 Chars([char; cell::MAX_ZEROWIDTH_CHARS + 1]),
287 Cursor(CursorKey),
288}
289
290#[derive(Copy, Clone, Debug)]
291pub struct RenderableCell {
292 pub line: Line,
294 pub column: Column,
295 pub inner: RenderableCellContent,
296 pub fg: Rgb,
297 pub bg: Rgb,
298 pub bg_alpha: f32,
299 pub flags: Flags,
300}
301
302impl RenderableCell {
303 fn new<C>(
304 config: &Config<C>,
305 colors: &color::List,
306 cell: Indexed<Cell>,
307 selected: bool,
308 ) -> Self {
309 let mut fg_rgb = Self::compute_fg_rgb(config, colors, cell.fg, cell.flags);
311 let mut bg_rgb = Self::compute_bg_rgb(colors, cell.bg);
312 let mut bg_alpha = Self::compute_bg_alpha(cell.bg);
313
314 let selection_background = config.colors.selection.background;
315 if let (true, Some(col)) = (selected, selection_background) {
316 bg_rgb = col;
318 bg_alpha = 1.0;
319 } else if selected ^ cell.inverse() {
320 if fg_rgb == bg_rgb && !cell.flags.contains(Flags::HIDDEN) {
321 fg_rgb = colors[NamedColor::Background];
323 bg_rgb = colors[NamedColor::Foreground];
324 } else {
325 mem::swap(&mut fg_rgb, &mut bg_rgb);
327 }
328
329 bg_alpha = 1.0;
330 }
331
332 if let (true, Some(col)) = (selected, config.colors.selection.text) {
334 fg_rgb = col;
335 }
336
337 RenderableCell {
338 line: cell.line,
339 column: cell.column,
340 inner: RenderableCellContent::Chars(cell.chars()),
341 fg: fg_rgb,
342 bg: bg_rgb,
343 bg_alpha,
344 flags: cell.flags,
345 }
346 }
347
348 fn compute_fg_rgb<C>(config: &Config<C>, colors: &color::List, fg: Color, flags: Flags) -> Rgb {
349 match fg {
350 Color::Spec(rgb) => rgb,
351 Color::Named(ansi) => {
352 match (config.draw_bold_text_with_bright_colors(), flags & Flags::DIM_BOLD) {
353 (_, Flags::DIM_BOLD)
355 if ansi == NamedColor::Foreground
356 && config.colors.primary.bright_foreground.is_none() =>
357 {
358 colors[NamedColor::DimForeground]
359 },
360 (true, Flags::BOLD) => colors[ansi.to_bright()],
362 (_, Flags::DIM) | (false, Flags::DIM_BOLD) => colors[ansi.to_dim()],
364 _ => colors[ansi],
366 }
367 },
368 Color::Indexed(idx) => {
369 let idx = match (
370 config.draw_bold_text_with_bright_colors(),
371 flags & Flags::DIM_BOLD,
372 idx,
373 ) {
374 (true, Flags::BOLD, 0..=7) => idx as usize + 8,
375 (false, Flags::DIM, 8..=15) => idx as usize - 8,
376 (false, Flags::DIM, 0..=7) => idx as usize + 260,
377 _ => idx as usize,
378 };
379
380 colors[idx]
381 },
382 }
383 }
384
385 #[inline]
386 fn compute_bg_alpha(bg: Color) -> f32 {
387 if bg == Color::Named(NamedColor::Background) {
388 0.
389 } else {
390 1.
391 }
392 }
393
394 #[inline]
395 fn compute_bg_rgb(colors: &color::List, bg: Color) -> Rgb {
396 match bg {
397 Color::Spec(rgb) => rgb,
398 Color::Named(ansi) => colors[ansi],
399 Color::Indexed(idx) => colors[idx],
400 }
401 }
402}
403
404impl<'a, C> Iterator for RenderableCellsIter<'a, C> {
405 type Item = RenderableCell;
406
407 #[inline]
412 fn next(&mut self) -> Option<Self::Item> {
413 loop {
414 if self.cursor_offset == self.inner.offset() && self.inner.column() == self.cursor.col {
415 let selected = self
416 .selection
417 .as_ref()
418 .map(|range| range.contains(self.cursor.col, self.cursor.line))
419 .unwrap_or(false);
420
421 if let Some(cursor_key) = self.cursor_key.take() {
423 let cell = Indexed {
424 inner: self.grid[self.cursor],
425 column: self.cursor.col,
426 line: self.inner.line(),
430 };
431
432 let mut renderable_cell =
433 RenderableCell::new(self.config, self.colors, cell, selected);
434
435 renderable_cell.inner = RenderableCellContent::Cursor(cursor_key);
436
437 if let Some(color) = self.config.cursor_cursor_color() {
438 renderable_cell.fg = RenderableCell::compute_bg_rgb(self.colors, color);
439 }
440
441 return Some(renderable_cell);
442 } else {
443 let mut cell =
444 RenderableCell::new(self.config, self.colors, self.inner.next()?, selected);
445
446 if self.cursor_style == CursorStyle::Block {
447 std::mem::swap(&mut cell.bg, &mut cell.fg);
448
449 if let Some(color) = self.config.cursor_text_color() {
450 cell.fg = color;
451 }
452 }
453
454 return Some(cell);
455 }
456 } else {
457 let cell = self.inner.next()?;
458
459 let selected = self
460 .selection
461 .as_ref()
462 .map(|range| range.contains(cell.column, cell.line))
463 .unwrap_or(false);
464
465 if !cell.is_empty() || selected {
466 return Some(RenderableCell::new(self.config, self.colors, cell, selected));
467 }
468 }
469 }
470 }
471}
472
473pub mod mode {
474 #![allow(clippy::bad_bit_mask)] use bitflags::bitflags;
478
479 bitflags! {
480 pub struct TermMode: u16 {
481 const SHOW_CURSOR = 0b0000_0000_0000_0001;
482 const APP_CURSOR = 0b0000_0000_0000_0010;
483 const APP_KEYPAD = 0b0000_0000_0000_0100;
484 const MOUSE_REPORT_CLICK = 0b0000_0000_0000_1000;
485 const BRACKETED_PASTE = 0b0000_0000_0001_0000;
486 const SGR_MOUSE = 0b0000_0000_0010_0000;
487 const MOUSE_MOTION = 0b0000_0000_0100_0000;
488 const LINE_WRAP = 0b0000_0000_1000_0000;
489 const LINE_FEED_NEW_LINE = 0b0000_0001_0000_0000;
490 const ORIGIN = 0b0000_0010_0000_0000;
491 const INSERT = 0b0000_0100_0000_0000;
492 const FOCUS_IN_OUT = 0b0000_1000_0000_0000;
493 const ALT_SCREEN = 0b0001_0000_0000_0000;
494 const MOUSE_DRAG = 0b0010_0000_0000_0000;
495 const MOUSE_MODE = 0b0010_0000_0100_1000;
496 const UTF8_MOUSE = 0b0100_0000_0000_0000;
497 const ALTERNATE_SCROLL = 0b1000_0000_0000_0000;
498 const ANY = 0b1111_1111_1111_1111;
499 const NONE = 0;
500 }
501 }
502
503 impl Default for TermMode {
504 fn default() -> TermMode {
505 TermMode::SHOW_CURSOR | TermMode::LINE_WRAP | TermMode::ALTERNATE_SCROLL
506 }
507 }
508}
509
510pub use crate::term::mode::TermMode;
511
512trait CharsetMapping {
513 fn map(&self, c: char) -> char {
514 c
515 }
516}
517
518impl CharsetMapping for StandardCharset {
519 #[inline]
522 fn map(&self, c: char) -> char {
523 match *self {
524 StandardCharset::Ascii => c,
525 StandardCharset::SpecialCharacterAndLineDrawing => match c {
526 '`' => '◆',
527 'a' => '▒',
528 'b' => '\t',
529 'c' => '\u{000c}',
530 'd' => '\r',
531 'e' => '\n',
532 'f' => '°',
533 'g' => '±',
534 'h' => '\u{2424}',
535 'i' => '\u{000b}',
536 'j' => '┘',
537 'k' => '┐',
538 'l' => '┌',
539 'm' => '└',
540 'n' => '┼',
541 'o' => '⎺',
542 'p' => '⎻',
543 'q' => '─',
544 'r' => '⎼',
545 's' => '⎽',
546 't' => '├',
547 'u' => '┤',
548 'v' => '┴',
549 'w' => '┬',
550 'x' => '│',
551 'y' => '≤',
552 'z' => '≥',
553 '{' => 'π',
554 '|' => '≠',
555 '}' => '£',
556 '~' => '·',
557 _ => c,
558 },
559 }
560 }
561}
562
563#[derive(Default, Copy, Clone)]
564struct Charsets([StandardCharset; 4]);
565
566impl Index<CharsetIndex> for Charsets {
567 type Output = StandardCharset;
568
569 fn index(&self, index: CharsetIndex) -> &StandardCharset {
570 &self.0[index as usize]
571 }
572}
573
574impl IndexMut<CharsetIndex> for Charsets {
575 fn index_mut(&mut self, index: CharsetIndex) -> &mut StandardCharset {
576 &mut self.0[index as usize]
577 }
578}
579
580#[derive(Default, Copy, Clone)]
581pub struct Cursor {
582 pub point: Point,
584
585 template: Cell,
587
588 charsets: Charsets,
590}
591
592pub struct VisualBell {
593 animation: VisualBellAnimation,
595
596 duration: Duration,
598
599 start_time: Option<Instant>,
601}
602
603fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 {
604 (1.0 - x).powi(3) * p0
605 + 3.0 * (1.0 - x).powi(2) * x * p1
606 + 3.0 * (1.0 - x) * x.powi(2) * p2
607 + x.powi(3) * p3
608}
609
610impl VisualBell {
611 pub fn new<C>(config: &Config<C>) -> VisualBell {
612 let visual_bell_config = &config.visual_bell;
613 VisualBell {
614 animation: visual_bell_config.animation,
615 duration: visual_bell_config.duration(),
616 start_time: None,
617 }
618 }
619
620 pub fn ring(&mut self) -> f64 {
622 let now = Instant::now();
623 self.start_time = Some(now);
624 self.intensity_at_instant(now)
625 }
626
627 pub fn intensity(&self) -> f64 {
630 self.intensity_at_instant(Instant::now())
631 }
632
633 pub fn completed(&mut self) -> bool {
635 match self.start_time {
636 Some(earlier) => {
637 if Instant::now().duration_since(earlier) >= self.duration {
638 self.start_time = None;
639 }
640 false
641 },
642 None => true,
643 }
644 }
645
646 pub fn intensity_at_instant(&self, instant: Instant) -> f64 {
650 if self.duration == Duration::from_secs(0) {
653 return 0.0;
654 }
655
656 match self.start_time {
657 None => 0.0,
660
661 Some(earlier) => {
662 if instant < earlier {
666 return 0.0;
667 }
668
669 let elapsed = instant.duration_since(earlier);
670 let elapsed_f =
671 elapsed.as_secs() as f64 + f64::from(elapsed.subsec_nanos()) / 1e9f64;
672 let duration_f = self.duration.as_secs() as f64
673 + f64::from(self.duration.subsec_nanos()) / 1e9f64;
674
675 let time = (elapsed_f / duration_f).min(1.0);
679
680 let inverse_intensity = match self.animation {
684 VisualBellAnimation::Ease | VisualBellAnimation::EaseOut => {
685 cubic_bezier(0.25, 0.1, 0.25, 1.0, time)
686 },
687 VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time),
688 VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time),
689 VisualBellAnimation::EaseOutCubic => {
690 cubic_bezier(0.215, 0.61, 0.355, 1.0, time)
691 },
692 VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time),
693 VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time),
694 VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time),
695 VisualBellAnimation::EaseOutCirc => cubic_bezier(0.075, 0.82, 0.165, 1.0, time),
696 VisualBellAnimation::Linear => time,
697 };
698
699 1.0 - inverse_intensity
702 },
703 }
704 }
705
706 pub fn update_config<C>(&mut self, config: &Config<C>) {
707 let visual_bell_config = &config.visual_bell;
708 self.animation = visual_bell_config.animation;
709 self.duration = visual_bell_config.duration();
710 }
711}
712
713pub struct Term<T> {
714 pub is_focused: bool,
716
717 grid: Grid<Cell>,
719
720 input_needs_wrap: bool,
726
727 alt_grid: Grid<Cell>,
729
730 alt: bool,
732
733 cursor: Cursor,
735
736 active_charset: CharsetIndex,
739
740 tabs: TabStops,
742
743 mode: TermMode,
745
746 scroll_region: Range<Line>,
750
751 pub dirty: bool,
752
753 pub visual_bell: VisualBell,
754
755 cursor_save: Cursor,
757
758 cursor_save_alt: Cursor,
760
761 semantic_escape_chars: String,
762
763 colors: color::List,
765
766 color_modified: [bool; color::COUNT],
768
769 original_colors: color::List,
771
772 cursor_style: Option<CursorStyle>,
774
775 default_cursor_style: CursorStyle,
777
778 dynamic_title: bool,
780
781 tabspaces: usize,
783
784 clipboard: Clipboard,
786
787 event_proxy: T,
789
790 title: String,
792
793 title_stack: Vec<String>,
796}
797
798#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
800pub struct SizeInfo {
801 pub width: f32,
803
804 pub height: f32,
806
807 pub cell_width: f32,
809
810 pub cell_height: f32,
812
813 pub padding_x: f32,
815
816 pub padding_y: f32,
818
819 #[serde(default)]
821 pub dpr: f64,
822}
823
824impl SizeInfo {
825 #[inline]
826 pub fn lines(&self) -> Line {
827 Line(((self.height - 2. * self.padding_y) / self.cell_height) as usize)
828 }
829
830 #[inline]
831 pub fn cols(&self) -> Column {
832 Column(((self.width - 2. * self.padding_x) / self.cell_width) as usize)
833 }
834
835 pub fn contains_point(&self, x: usize, y: usize) -> bool {
839 x < (self.width - self.padding_x) as usize
840 && x >= self.padding_x as usize
841 && y < (self.height - self.padding_y) as usize
842 && y >= self.padding_y as usize
843 }
844
845 pub fn pixels_to_coords(&self, x: usize, y: usize) -> Point {
846 let col = Column(x.saturating_sub(self.padding_x as usize) / (self.cell_width as usize));
847 let line = Line(y.saturating_sub(self.padding_y as usize) / (self.cell_height as usize));
848
849 Point {
850 line: min(line, Line(self.lines().saturating_sub(1))),
851 col: min(col, Column(self.cols().saturating_sub(1))),
852 }
853 }
854}
855
856impl<T> Term<T> {
857 pub fn selection(&self) -> &Option<Selection> {
858 &self.grid.selection
859 }
860
861 pub fn selection_mut(&mut self) -> &mut Option<Selection> {
862 &mut self.grid.selection
863 }
864
865 #[inline]
866 pub fn scroll_display(&mut self, scroll: Scroll)
867 where
868 T: EventListener,
869 {
870 self.event_proxy.send_event(Event::MouseCursorDirty);
871 self.grid.scroll_display(scroll);
872 self.dirty = true;
873 }
874
875 pub fn new<C>(
876 config: &Config<C>,
877 size: &SizeInfo,
878 clipboard: Clipboard,
879 event_proxy: T,
880 ) -> Term<T> {
881 let num_cols = size.cols();
882 let num_lines = size.lines();
883
884 let history_size = config.scrolling.history() as usize;
885 let grid = Grid::new(num_lines, num_cols, history_size, Cell::default());
886 let alt = Grid::new(num_lines, num_cols, 0 , Cell::default());
887
888 let tabspaces = config.tabspaces();
889 let tabs = TabStops::new(grid.num_cols(), tabspaces);
890
891 let scroll_region = Line(0)..grid.num_lines();
892
893 let colors = color::List::from(&config.colors);
894
895 Term {
896 dirty: false,
897 visual_bell: VisualBell::new(config),
898 input_needs_wrap: false,
899 grid,
900 alt_grid: alt,
901 alt: false,
902 active_charset: Default::default(),
903 cursor: Default::default(),
904 cursor_save: Default::default(),
905 cursor_save_alt: Default::default(),
906 tabs,
907 mode: Default::default(),
908 scroll_region,
909 colors,
910 color_modified: [false; color::COUNT],
911 original_colors: colors,
912 semantic_escape_chars: config.selection.semantic_escape_chars().to_owned(),
913 cursor_style: None,
914 default_cursor_style: config.cursor.style,
915 dynamic_title: config.dynamic_title(),
916 tabspaces,
917 clipboard,
918 event_proxy,
919 is_focused: true,
920 title: config.window.title.clone(),
921 title_stack: Vec::new(),
922 }
923 }
924
925 pub fn update_config<C>(&mut self, config: &Config<C>) {
926 self.semantic_escape_chars = config.selection.semantic_escape_chars().to_owned();
927 self.original_colors.fill_named(&config.colors);
928 self.original_colors.fill_cube(&config.colors);
929 self.original_colors.fill_gray_ramp(&config.colors);
930 for i in 0..color::COUNT {
931 if !self.color_modified[i] {
932 self.colors[i] = self.original_colors[i];
933 }
934 }
935 self.visual_bell.update_config(config);
936 if let Some(0) = config.scrolling.faux_multiplier() {
937 self.mode.remove(TermMode::ALTERNATE_SCROLL);
938 }
939 self.default_cursor_style = config.cursor.style;
940 self.dynamic_title = config.dynamic_title();
941 self.grid.update_history(config.scrolling.history() as usize);
942 }
943
944 pub fn selection_to_string(&self) -> Option<String> {
946 let selection = self.grid.selection.clone()?;
947 let SelectionRange { start, end, is_block } = selection.to_range(self)?;
948
949 let mut res = String::new();
950
951 if is_block {
952 for line in (end.line + 1..=start.line).rev() {
953 res += &(self.line_to_string(line, start.col..end.col) + "\n");
954 }
955 res += &self.line_to_string(end.line, start.col..end.col);
956 } else {
957 res = self.bounds_to_string(start, end);
958 }
959
960 Some(res)
961 }
962
963 pub fn bounds_to_string(&self, start: Point<usize>, end: Point<usize>) -> String {
965 let mut res = String::new();
966
967 for line in (end.line..=start.line).rev() {
968 let start_col = if line == start.line { start.col } else { Column(0) };
969 let end_col = if line == end.line { end.col } else { self.cols() - 1 };
970
971 res += &self.line_to_string(line, start_col..end_col);
972 }
973
974 res
975 }
976
977 fn line_to_string(&self, line: usize, cols: Range<Column>) -> String {
979 let mut text = String::new();
980
981 let grid_line = &self.grid[line];
982 let line_length = grid_line.line_length();
983 let line_end = min(line_length, cols.end + 1);
984
985 let mut tab_mode = false;
986
987 for col in IndexRange::from(cols.start..line_end) {
988 let cell = grid_line[col];
989
990 if tab_mode {
992 if self.tabs[col] {
993 tab_mode = false;
994 } else {
995 continue;
996 }
997 }
998
999 if cell.c == '\t' {
1000 tab_mode = true;
1001 }
1002
1003 if !cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
1004 text.push(cell.c);
1006
1007 for c in (&cell.chars()[1..]).iter().take_while(|c| **c != ' ') {
1009 text.push(*c);
1010 }
1011 }
1012 }
1013
1014 if cols.end >= self.cols() - 1
1015 && (line_end == Column(0)
1016 || !self.grid[line][line_end - 1].flags.contains(Flags::WRAPLINE))
1017 {
1018 text.push('\n');
1019 }
1020
1021 text
1022 }
1023
1024 pub fn visible_to_buffer(&self, point: Point) -> Point<usize> {
1025 self.grid.visible_to_buffer(point)
1026 }
1027
1028 pub fn buffer_to_visible(&self, point: impl Into<Point<usize>>) -> Option<Point<usize>> {
1029 self.grid.buffer_to_visible(point)
1030 }
1031
1032 pub fn grid(&self) -> &Grid<Cell> {
1037 &self.grid
1038 }
1039
1040 #[cfg(test)]
1042 pub fn grid_mut(&mut self) -> &mut Grid<Cell> {
1043 &mut self.grid
1044 }
1045
1046 pub fn renderable_cells<'b, C>(&'b self, config: &'b Config<C>) -> RenderableCellsIter<'_, C> {
1052 let selection = self.grid.selection.as_ref().and_then(|s| s.to_range(self));
1053
1054 let cursor = if self.is_focused || !config.cursor.unfocused_hollow() {
1055 self.cursor_style.unwrap_or(self.default_cursor_style)
1056 } else {
1057 CursorStyle::HollowBlock
1058 };
1059
1060 RenderableCellsIter::new(&self, config, selection, cursor)
1061 }
1062
1063 pub fn resize(&mut self, size: &SizeInfo) {
1065 let old_cols = self.grid.num_cols();
1066 let old_lines = self.grid.num_lines();
1067 let mut num_cols = size.cols();
1068 let mut num_lines = size.lines();
1069
1070 if old_cols == num_cols && old_lines == num_lines {
1071 debug!("Term::resize dimensions unchanged");
1072 return;
1073 }
1074
1075 self.grid.selection = None;
1076 self.alt_grid.selection = None;
1077
1078 if num_cols <= Column(1) {
1080 num_cols = Column(2);
1081 }
1082
1083 if num_lines <= Line(1) {
1085 num_lines = Line(2);
1086 }
1087
1088 if self.cursor.point.line >= num_lines {
1090 let lines = self.cursor.point.line - num_lines + 1;
1091 let template = Cell { bg: self.cursor.template.bg, ..Cell::default() };
1092 self.grid.scroll_up(&(Line(0)..old_lines), lines, &template);
1093 }
1094
1095 if self.cursor_save_alt.point.line >= num_lines {
1097 let lines = self.cursor_save_alt.point.line - num_lines + 1;
1098 let template = Cell { bg: self.cursor_save_alt.template.bg, ..Cell::default() };
1099 self.alt_grid.scroll_up(&(Line(0)..old_lines), lines, &template);
1100 }
1101
1102 if num_lines > old_lines && !self.mode.contains(TermMode::ALT_SCREEN) {
1104 let growage = min(num_lines - old_lines, Line(self.grid.history_size()));
1105 self.cursor.point.line += growage;
1106 }
1107
1108 debug!("New num_cols is {} and num_lines is {}", num_cols, num_lines);
1109
1110 let is_alt = self.mode.contains(TermMode::ALT_SCREEN);
1112 let alt_cursor_point =
1113 if is_alt { &mut self.cursor_save.point } else { &mut self.cursor_save_alt.point };
1114 self.grid.resize(!is_alt, num_lines, num_cols, &mut self.cursor.point, &Cell::default());
1115 self.alt_grid.resize(is_alt, num_lines, num_cols, alt_cursor_point, &Cell::default());
1116
1117 self.scroll_region = Line(0)..self.grid.num_lines();
1119
1120 self.cursor.point.col = min(self.cursor.point.col, num_cols - 1);
1122 self.cursor.point.line = min(self.cursor.point.line, num_lines - 1);
1123 self.cursor_save.point.col = min(self.cursor_save.point.col, num_cols - 1);
1124 self.cursor_save.point.line = min(self.cursor_save.point.line, num_lines - 1);
1125 self.cursor_save_alt.point.col = min(self.cursor_save_alt.point.col, num_cols - 1);
1126 self.cursor_save_alt.point.line = min(self.cursor_save_alt.point.line, num_lines - 1);
1127
1128 self.tabs = TabStops::new(self.grid.num_cols(), self.tabspaces);
1130 }
1131
1132 #[inline]
1133 pub fn mode(&self) -> &TermMode {
1134 &self.mode
1135 }
1136
1137 #[inline]
1138 pub fn cursor(&self) -> &Cursor {
1139 &self.cursor
1140 }
1141
1142 pub fn swap_alt(&mut self) {
1143 if self.alt {
1144 let template = self.cursor.template;
1145 self.grid.region_mut(..).each(|c| c.reset(&template));
1146 }
1147
1148 self.alt = !self.alt;
1149 std::mem::swap(&mut self.grid, &mut self.alt_grid);
1150 }
1151
1152 #[inline]
1157 fn scroll_down_relative(&mut self, origin: Line, mut lines: Line) {
1158 trace!("Scrolling down relative: origin={}, lines={}", origin, lines);
1159 lines = min(lines, self.scroll_region.end - self.scroll_region.start);
1160 lines = min(lines, self.scroll_region.end - origin);
1161
1162 let template = Cell { bg: self.cursor.template.bg, ..Cell::default() };
1164 self.grid.scroll_down(&(origin..self.scroll_region.end), lines, &template);
1165 }
1166
1167 #[inline]
1172 fn scroll_up_relative(&mut self, origin: Line, lines: Line) {
1173 trace!("Scrolling up relative: origin={}, lines={}", origin, lines);
1174 let lines = min(lines, self.scroll_region.end - self.scroll_region.start);
1175
1176 let template = Cell { bg: self.cursor.template.bg, ..Cell::default() };
1178 self.grid.scroll_up(&(origin..self.scroll_region.end), lines, &template);
1179 }
1180
1181 fn deccolm(&mut self)
1182 where
1183 T: EventListener,
1184 {
1185 self.set_scrolling_region(1, self.grid.num_lines().0);
1188
1189 let template = self.cursor.template;
1191 self.grid.region_mut(..).each(|c| c.reset(&template));
1192 }
1193
1194 #[inline]
1195 pub fn background_color(&self) -> Rgb {
1196 self.colors[NamedColor::Background]
1197 }
1198
1199 #[inline]
1200 pub fn exit(&mut self)
1201 where
1202 T: EventListener,
1203 {
1204 self.event_proxy.send_event(Event::Exit);
1205 }
1206
1207 pub fn clipboard(&mut self) -> &mut Clipboard {
1208 &mut self.clipboard
1209 }
1210
1211 #[inline]
1213 fn wrapline(&mut self)
1214 where
1215 T: EventListener,
1216 {
1217 if !self.mode.contains(TermMode::LINE_WRAP) {
1218 return;
1219 }
1220
1221 trace!("Wrapping input");
1222
1223 self.grid[&self.cursor.point].flags.insert(Flags::WRAPLINE);
1224
1225 if (self.cursor.point.line + 1) >= self.scroll_region.end {
1226 self.linefeed();
1227 } else {
1228 self.cursor.point.line += 1;
1229 }
1230
1231 self.cursor.point.col = Column(0);
1232 self.input_needs_wrap = false;
1233 }
1234
1235 #[inline]
1237 fn write_at_cursor(&mut self, c: char) -> &mut Cell
1238 where
1239 T: EventListener,
1240 {
1241 let cell = &mut self.grid[&self.cursor.point];
1242 *cell = self.cursor.template;
1243 cell.c = self.cursor.charsets[self.active_charset].map(c);
1244 cell
1245 }
1246}
1247
1248impl<T> TermInfo for Term<T> {
1249 #[inline]
1250 fn lines(&self) -> Line {
1251 self.grid.num_lines()
1252 }
1253
1254 #[inline]
1255 fn cols(&self) -> Column {
1256 self.grid.num_cols()
1257 }
1258}
1259
1260impl<T: EventListener> Handler for Term<T> {
1261 #[inline]
1262 #[cfg(not(windows))]
1263 fn set_title(&mut self, title: &str) {
1264 if self.dynamic_title {
1265 trace!("Setting window title to '{}'", title);
1266
1267 self.title = title.into();
1268 self.event_proxy.send_event(Event::Title(title.to_owned()));
1269 }
1270 }
1271
1272 #[inline]
1273 #[cfg(windows)]
1274 fn set_title(&mut self, title: &str) {
1275 if self.dynamic_title {
1276 trace!("Setting window title to '{}'", title);
1283
1284 let title = if !tty::is_conpty() && title.starts_with(' ') {
1285 format!("Alacritty {}", title.trim())
1286 } else {
1287 title.to_owned()
1288 };
1289
1290 self.title = title.clone();
1291 self.event_proxy.send_event(Event::Title(title));
1292 }
1293 }
1294
1295 #[inline]
1297 fn input(&mut self, c: char) {
1298 let width = match c.width() {
1300 Some(width) => width,
1301 None => return,
1302 };
1303
1304 if width == 0 {
1306 let mut col = self.cursor.point.col.0.saturating_sub(1);
1307 let line = self.cursor.point.line;
1308 if self.grid[line][Column(col)].flags.contains(Flags::WIDE_CHAR_SPACER) {
1309 col = col.saturating_sub(1);
1310 }
1311 self.grid[line][Column(col)].push_extra(c);
1312 return;
1313 }
1314
1315 if self.input_needs_wrap {
1317 self.wrapline();
1318 }
1319
1320 let num_cols = self.grid.num_cols();
1321
1322 if self.mode.contains(TermMode::INSERT) && self.cursor.point.col + width < num_cols {
1324 let line = self.cursor.point.line;
1325 let col = self.cursor.point.col;
1326 let line = &mut self.grid[line];
1327
1328 let src = line[col..].as_ptr();
1329 let dst = line[(col + width)..].as_mut_ptr();
1330 unsafe {
1331 ptr::copy(src, dst, (num_cols - col - width).0);
1332 }
1333 }
1334
1335 if width == 1 {
1336 self.write_at_cursor(c);
1337 } else {
1338 if self.cursor.point.col + 1 >= num_cols {
1340 self.write_at_cursor(' ').flags.insert(Flags::WIDE_CHAR_SPACER);
1341 self.wrapline();
1342 }
1343
1344 self.write_at_cursor(c).flags.insert(Flags::WIDE_CHAR);
1346
1347 self.cursor.point.col += 1;
1349 self.write_at_cursor(' ').flags.insert(Flags::WIDE_CHAR_SPACER);
1350 }
1351
1352 if self.cursor.point.col + 1 < num_cols {
1353 self.cursor.point.col += 1;
1354 } else {
1355 self.input_needs_wrap = true;
1356 }
1357 }
1358
1359 #[inline]
1360 fn decaln(&mut self) {
1361 trace!("Decalnning");
1362
1363 let template = Cell { c: 'E', ..Cell::default() };
1364 self.grid.region_mut(..).each(|c| c.reset(&template));
1365 }
1366
1367 #[inline]
1368 fn goto(&mut self, line: Line, col: Column) {
1369 trace!("Going to: line={}, col={}", line, col);
1370 let (y_offset, max_y) = if self.mode.contains(TermMode::ORIGIN) {
1371 (self.scroll_region.start, self.scroll_region.end - 1)
1372 } else {
1373 (Line(0), self.grid.num_lines() - 1)
1374 };
1375
1376 self.cursor.point.line = min(line + y_offset, max_y);
1377 self.cursor.point.col = min(col, self.grid.num_cols() - 1);
1378 self.input_needs_wrap = false;
1379 }
1380
1381 #[inline]
1382 fn goto_line(&mut self, line: Line) {
1383 trace!("Going to line: {}", line);
1384 self.goto(line, self.cursor.point.col)
1385 }
1386
1387 #[inline]
1388 fn goto_col(&mut self, col: Column) {
1389 trace!("Going to column: {}", col);
1390 self.goto(self.cursor.point.line, col)
1391 }
1392
1393 #[inline]
1394 fn insert_blank(&mut self, count: Column) {
1395 let count = min(count, self.grid.num_cols() - self.cursor.point.col);
1398
1399 let source = self.cursor.point.col;
1400 let destination = self.cursor.point.col + count;
1401 let num_cells = (self.grid.num_cols() - destination).0;
1402
1403 let line = &mut self.grid[self.cursor.point.line];
1404
1405 unsafe {
1406 let src = line[source..].as_ptr();
1407 let dst = line[destination..].as_mut_ptr();
1408
1409 ptr::copy(src, dst, num_cells);
1410 }
1411
1412 for c in &mut line[source..destination] {
1415 c.reset(&self.cursor.template);
1416 }
1417 }
1418
1419 #[inline]
1420 fn move_up(&mut self, lines: Line) {
1421 trace!("Moving up: {}", lines);
1422 let move_to = Line(self.cursor.point.line.0.saturating_sub(lines.0));
1423 self.goto(move_to, self.cursor.point.col)
1424 }
1425
1426 #[inline]
1427 fn move_down(&mut self, lines: Line) {
1428 trace!("Moving down: {}", lines);
1429 let move_to = self.cursor.point.line + lines;
1430 self.goto(move_to, self.cursor.point.col)
1431 }
1432
1433 #[inline]
1434 fn move_forward(&mut self, cols: Column) {
1435 trace!("Moving forward: {}", cols);
1436 self.cursor.point.col = min(self.cursor.point.col + cols, self.grid.num_cols() - 1);
1437 self.input_needs_wrap = false;
1438 }
1439
1440 #[inline]
1441 fn move_backward(&mut self, cols: Column) {
1442 trace!("Moving backward: {}", cols);
1443 self.cursor.point.col -= min(self.cursor.point.col, cols);
1444 self.input_needs_wrap = false;
1445 }
1446
1447 #[inline]
1448 fn identify_terminal<W: io::Write>(&mut self, writer: &mut W) {
1449 trace!("Reporting terminal identity");
1450 let _ = writer.write_all(b"\x1b[?6c");
1451 }
1452
1453 #[inline]
1454 fn device_status<W: io::Write>(&mut self, writer: &mut W, arg: usize) {
1455 trace!("Reporting device status: {}", arg);
1456 match arg {
1457 5 => {
1458 let _ = writer.write_all(b"\x1b[0n");
1459 },
1460 6 => {
1461 let pos = self.cursor.point;
1462 let response = format!("\x1b[{};{}R", pos.line + 1, pos.col + 1);
1463 let _ = writer.write_all(response.as_bytes());
1464 },
1465 _ => debug!("unknown device status query: {}", arg),
1466 };
1467 }
1468
1469 #[inline]
1470 fn move_down_and_cr(&mut self, lines: Line) {
1471 trace!("Moving down and cr: {}", lines);
1472 let move_to = self.cursor.point.line + lines;
1473 self.goto(move_to, Column(0))
1474 }
1475
1476 #[inline]
1477 fn move_up_and_cr(&mut self, lines: Line) {
1478 trace!("Moving up and cr: {}", lines);
1479 let move_to = Line(self.cursor.point.line.0.saturating_sub(lines.0));
1480 self.goto(move_to, Column(0))
1481 }
1482
1483 #[inline]
1484 fn put_tab(&mut self, mut count: i64) {
1485 trace!("Putting tab: {}", count);
1486
1487 while self.cursor.point.col < self.grid.num_cols() && count != 0 {
1488 count -= 1;
1489
1490 let cell = &mut self.grid[&self.cursor.point];
1491 if cell.c == ' ' {
1492 cell.c = self.cursor.charsets[self.active_charset].map('\t');
1493 }
1494
1495 loop {
1496 if (self.cursor.point.col + 1) == self.grid.num_cols() {
1497 break;
1498 }
1499
1500 self.cursor.point.col += 1;
1501
1502 if self.tabs[self.cursor.point.col] {
1503 break;
1504 }
1505 }
1506 }
1507
1508 self.input_needs_wrap = false;
1509 }
1510
1511 #[inline]
1513 fn backspace(&mut self) {
1514 trace!("Backspace");
1515 if self.cursor.point.col > Column(0) {
1516 self.cursor.point.col -= 1;
1517 self.input_needs_wrap = false;
1518 }
1519 }
1520
1521 #[inline]
1523 fn carriage_return(&mut self) {
1524 trace!("Carriage return");
1525 self.cursor.point.col = Column(0);
1526 self.input_needs_wrap = false;
1527 }
1528
1529 #[inline]
1531 fn linefeed(&mut self) {
1532 trace!("Linefeed");
1533 let next = self.cursor.point.line + 1;
1534 if next == self.scroll_region.end {
1535 self.scroll_up(Line(1));
1536 } else if next < self.grid.num_lines() {
1537 self.cursor.point.line += 1;
1538 }
1539 }
1540
1541 #[inline]
1543 fn bell(&mut self) {
1544 trace!("Bell");
1545 self.visual_bell.ring();
1546 self.event_proxy.send_event(Event::Urgent);
1547 }
1548
1549 #[inline]
1550 fn substitute(&mut self) {
1551 trace!("[unimplemented] Substitute");
1552 }
1553
1554 #[inline]
1577 fn newline(&mut self) {
1578 self.linefeed();
1579
1580 if self.mode.contains(TermMode::LINE_FEED_NEW_LINE) {
1581 self.carriage_return();
1582 }
1583 }
1584
1585 #[inline]
1586 fn set_horizontal_tabstop(&mut self) {
1587 trace!("Setting horizontal tabstop");
1588 let column = self.cursor.point.col;
1589 self.tabs[column] = true;
1590 }
1591
1592 #[inline]
1593 fn scroll_up(&mut self, lines: Line) {
1594 let origin = self.scroll_region.start;
1595 self.scroll_up_relative(origin, lines);
1596 }
1597
1598 #[inline]
1599 fn scroll_down(&mut self, lines: Line) {
1600 let origin = self.scroll_region.start;
1601 self.scroll_down_relative(origin, lines);
1602 }
1603
1604 #[inline]
1605 fn insert_blank_lines(&mut self, lines: Line) {
1606 trace!("Inserting blank {} lines", lines);
1607 if self.scroll_region.contains(&self.cursor.point.line) {
1608 let origin = self.cursor.point.line;
1609 self.scroll_down_relative(origin, lines);
1610 }
1611 }
1612
1613 #[inline]
1614 fn delete_lines(&mut self, lines: Line) {
1615 let origin = self.cursor.point.line;
1616 let lines = min(self.lines() - origin, lines);
1617
1618 trace!("Deleting {} lines", lines);
1619
1620 if lines.0 > 0 && self.scroll_region.contains(&self.cursor.point.line) {
1621 self.scroll_up_relative(origin, lines);
1622 }
1623 }
1624
1625 #[inline]
1626 fn erase_chars(&mut self, count: Column) {
1627 trace!("Erasing chars: count={}, col={}", count, self.cursor.point.col);
1628 let start = self.cursor.point.col;
1629 let end = min(start + count, self.grid.num_cols());
1630
1631 let row = &mut self.grid[self.cursor.point.line];
1632 for c in &mut row[start..end] {
1634 c.reset(&self.cursor.template);
1635 }
1636 }
1637
1638 #[inline]
1639 fn delete_chars(&mut self, count: Column) {
1640 let cols = self.grid.num_cols();
1641
1642 let count = min(count, cols);
1644
1645 let start = self.cursor.point.col;
1646 let end = min(start + count, cols - 1);
1647 let n = (cols - end).0;
1648
1649 let line = &mut self.grid[self.cursor.point.line];
1650
1651 unsafe {
1652 let src = line[end..].as_ptr();
1653 let dst = line[start..].as_mut_ptr();
1654
1655 ptr::copy(src, dst, n);
1656 }
1657
1658 let end = cols - count;
1661 for c in &mut line[end..] {
1662 c.reset(&self.cursor.template);
1663 }
1664 }
1665
1666 #[inline]
1667 fn move_backward_tabs(&mut self, count: i64) {
1668 trace!("Moving backward {} tabs", count);
1669
1670 for _ in 0..count {
1671 let mut col = self.cursor.point.col;
1672 for i in (0..(col.0)).rev() {
1673 if self.tabs[index::Column(i)] {
1674 col = index::Column(i);
1675 break;
1676 }
1677 }
1678 self.cursor.point.col = col;
1679 }
1680 }
1681
1682 #[inline]
1683 fn move_forward_tabs(&mut self, count: i64) {
1684 trace!("[unimplemented] Moving forward {} tabs", count);
1685 }
1686
1687 #[inline]
1688 fn save_cursor_position(&mut self) {
1689 trace!("Saving cursor position");
1690 let cursor = if self.alt { &mut self.cursor_save_alt } else { &mut self.cursor_save };
1691
1692 *cursor = self.cursor;
1693 }
1694
1695 #[inline]
1696 fn restore_cursor_position(&mut self) {
1697 trace!("Restoring cursor position");
1698 let source = if self.alt { &self.cursor_save_alt } else { &self.cursor_save };
1699
1700 self.cursor = *source;
1701 self.cursor.point.line = min(self.cursor.point.line, self.grid.num_lines() - 1);
1702 self.cursor.point.col = min(self.cursor.point.col, self.grid.num_cols() - 1);
1703 }
1704
1705 #[inline]
1706 fn clear_line(&mut self, mode: ansi::LineClearMode) {
1707 trace!("Clearing line: {:?}", mode);
1708
1709 let col = self.cursor.point.col;
1710
1711 match mode {
1712 ansi::LineClearMode::Right => {
1713 let row = &mut self.grid[self.cursor.point.line];
1714 for cell in &mut row[col..] {
1715 cell.reset(&self.cursor.template);
1716 }
1717 },
1718 ansi::LineClearMode::Left => {
1719 let row = &mut self.grid[self.cursor.point.line];
1720 for cell in &mut row[..=col] {
1721 cell.reset(&self.cursor.template);
1722 }
1723 },
1724 ansi::LineClearMode::All => {
1725 let row = &mut self.grid[self.cursor.point.line];
1726 for cell in &mut row[..] {
1727 cell.reset(&self.cursor.template);
1728 }
1729 },
1730 }
1731 }
1732
1733 #[inline]
1735 fn set_color(&mut self, index: usize, color: Rgb) {
1736 trace!("Setting color[{}] = {:?}", index, color);
1737 self.colors[index] = color;
1738 self.color_modified[index] = true;
1739 }
1740
1741 #[inline]
1743 fn dynamic_color_sequence<W: io::Write>(&mut self, writer: &mut W, code: u8, index: usize) {
1744 trace!("Writing escape sequence for dynamic color code {}: color[{}]", code, index);
1745 let color = self.colors[index];
1746 let response = format!(
1747 "\x1b]{};rgb:{1:02x}{1:02x}/{2:02x}{2:02x}/{3:02x}{3:02x}\x07",
1748 code, color.r, color.g, color.b
1749 );
1750 let _ = writer.write_all(response.as_bytes());
1751 }
1752
1753 #[inline]
1755 fn reset_color(&mut self, index: usize) {
1756 trace!("Resetting color[{}]", index);
1757 self.colors[index] = self.original_colors[index];
1758 self.color_modified[index] = false;
1759 }
1760
1761 #[inline]
1763 fn set_clipboard(&mut self, clipboard: u8, base64: &[u8]) {
1764 let clipboard_type = match clipboard {
1765 b'c' => ClipboardType::Clipboard,
1766 b'p' | b's' => ClipboardType::Selection,
1767 _ => return,
1768 };
1769
1770 if let Ok(bytes) = base64::decode(base64) {
1771 if let Ok(text) = str::from_utf8(&bytes) {
1772 self.clipboard.store(clipboard_type, text);
1773 }
1774 }
1775 }
1776
1777 #[inline]
1779 fn write_clipboard<W: io::Write>(&mut self, clipboard: u8, writer: &mut W) {
1780 let clipboard_type = match clipboard {
1781 b'c' => ClipboardType::Clipboard,
1782 b'p' | b's' => ClipboardType::Selection,
1783 _ => return,
1784 };
1785
1786 let text = self.clipboard.load(clipboard_type);
1787 let base64 = base64::encode(&text);
1788 let escape = format!("\x1b]52;{};{}\x07", clipboard as char, base64);
1789 let _ = writer.write_all(escape.as_bytes());
1790 }
1791
1792 #[inline]
1793 fn clear_screen(&mut self, mode: ansi::ClearMode) {
1794 trace!("Clearing screen: {:?}", mode);
1795 let template = self.cursor.template;
1796
1797 self.grid.selection = None;
1799
1800 match mode {
1801 ansi::ClearMode::Above => {
1802 if self.cursor.point.line > Line(1) {
1804 self.grid
1806 .region_mut(..self.cursor.point.line)
1807 .each(|cell| cell.reset(&template));
1808 }
1809 let end = min(self.cursor.point.col + 1, self.grid.num_cols());
1811 for cell in &mut self.grid[self.cursor.point.line][..end] {
1812 cell.reset(&template);
1813 }
1814 },
1815 ansi::ClearMode::Below => {
1816 for cell in &mut self.grid[self.cursor.point.line][self.cursor.point.col..] {
1817 cell.reset(&template);
1818 }
1819 if self.cursor.point.line < self.grid.num_lines() - 1 {
1820 self.grid
1821 .region_mut((self.cursor.point.line + 1)..)
1822 .each(|cell| cell.reset(&template));
1823 }
1824 },
1825 ansi::ClearMode::All => {
1826 if self.mode.contains(TermMode::ALT_SCREEN) {
1827 self.grid.region_mut(..).each(|c| c.reset(&template));
1828 } else {
1829 let template = Cell { bg: template.bg, ..Cell::default() };
1830 self.grid.clear_viewport(&template);
1831 }
1832 },
1833 ansi::ClearMode::Saved => self.grid.clear_history(),
1834 }
1835 }
1836
1837 #[inline]
1838 fn clear_tabs(&mut self, mode: ansi::TabulationClearMode) {
1839 trace!("Clearing tabs: {:?}", mode);
1840 match mode {
1841 ansi::TabulationClearMode::Current => {
1842 let column = self.cursor.point.col;
1843 self.tabs[column] = false;
1844 },
1845 ansi::TabulationClearMode::All => {
1846 self.tabs.clear_all();
1847 },
1848 }
1849 }
1850
1851 #[inline]
1853 fn reset_state(&mut self) {
1854 if self.alt {
1855 self.swap_alt();
1856 }
1857 self.input_needs_wrap = false;
1858 self.cursor = Default::default();
1859 self.active_charset = Default::default();
1860 self.mode = Default::default();
1861 self.cursor_save = Default::default();
1862 self.cursor_save_alt = Default::default();
1863 self.colors = self.original_colors;
1864 self.color_modified = [false; color::COUNT];
1865 self.cursor_style = None;
1866 self.grid.reset(&Cell::default());
1867 self.alt_grid.reset(&Cell::default());
1868 self.scroll_region = Line(0)..self.grid.num_lines();
1869 self.title = DEFAULT_NAME.to_string();
1870 self.title_stack.clear();
1871 }
1872
1873 #[inline]
1874 fn reverse_index(&mut self) {
1875 trace!("Reversing index");
1876 if self.cursor.point.line == self.scroll_region.start {
1878 self.scroll_down(Line(1));
1879 } else {
1880 self.cursor.point.line -= min(self.cursor.point.line, Line(1));
1881 }
1882 }
1883
1884 #[inline]
1886 fn terminal_attribute(&mut self, attr: Attr) {
1887 trace!("Setting attribute: {:?}", attr);
1888 match attr {
1889 Attr::Foreground(color) => self.cursor.template.fg = color,
1890 Attr::Background(color) => self.cursor.template.bg = color,
1891 Attr::Reset => {
1892 self.cursor.template.fg = Color::Named(NamedColor::Foreground);
1893 self.cursor.template.bg = Color::Named(NamedColor::Background);
1894 self.cursor.template.flags = Flags::empty();
1895 },
1896 Attr::Reverse => self.cursor.template.flags.insert(Flags::INVERSE),
1897 Attr::CancelReverse => self.cursor.template.flags.remove(Flags::INVERSE),
1898 Attr::Bold => self.cursor.template.flags.insert(Flags::BOLD),
1899 Attr::CancelBold => self.cursor.template.flags.remove(Flags::BOLD),
1900 Attr::Dim => self.cursor.template.flags.insert(Flags::DIM),
1901 Attr::CancelBoldDim => self.cursor.template.flags.remove(Flags::BOLD | Flags::DIM),
1902 Attr::Italic => self.cursor.template.flags.insert(Flags::ITALIC),
1903 Attr::CancelItalic => self.cursor.template.flags.remove(Flags::ITALIC),
1904 Attr::Underline => self.cursor.template.flags.insert(Flags::UNDERLINE),
1905 Attr::CancelUnderline => self.cursor.template.flags.remove(Flags::UNDERLINE),
1906 Attr::Hidden => self.cursor.template.flags.insert(Flags::HIDDEN),
1907 Attr::CancelHidden => self.cursor.template.flags.remove(Flags::HIDDEN),
1908 Attr::Strike => self.cursor.template.flags.insert(Flags::STRIKEOUT),
1909 Attr::CancelStrike => self.cursor.template.flags.remove(Flags::STRIKEOUT),
1910 _ => {
1911 debug!("Term got unhandled attr: {:?}", attr);
1912 },
1913 }
1914 }
1915
1916 #[inline]
1917 fn set_mode(&mut self, mode: ansi::Mode) {
1918 trace!("Setting mode: {:?}", mode);
1919 match mode {
1920 ansi::Mode::SwapScreenAndSetRestoreCursor => {
1921 if !self.alt {
1922 self.mode.insert(TermMode::ALT_SCREEN);
1923 self.save_cursor_position();
1924 self.swap_alt();
1925 self.save_cursor_position();
1926 }
1927 },
1928 ansi::Mode::ShowCursor => self.mode.insert(TermMode::SHOW_CURSOR),
1929 ansi::Mode::CursorKeys => self.mode.insert(TermMode::APP_CURSOR),
1930 ansi::Mode::ReportMouseClicks => {
1932 self.mode.remove(TermMode::MOUSE_MODE);
1933 self.mode.insert(TermMode::MOUSE_REPORT_CLICK);
1934 self.event_proxy.send_event(Event::MouseCursorDirty);
1935 },
1936 ansi::Mode::ReportCellMouseMotion => {
1937 self.mode.remove(TermMode::MOUSE_MODE);
1938 self.mode.insert(TermMode::MOUSE_DRAG);
1939 self.event_proxy.send_event(Event::MouseCursorDirty);
1940 },
1941 ansi::Mode::ReportAllMouseMotion => {
1942 self.mode.remove(TermMode::MOUSE_MODE);
1943 self.mode.insert(TermMode::MOUSE_MOTION);
1944 self.event_proxy.send_event(Event::MouseCursorDirty);
1945 },
1946 ansi::Mode::ReportFocusInOut => self.mode.insert(TermMode::FOCUS_IN_OUT),
1947 ansi::Mode::BracketedPaste => self.mode.insert(TermMode::BRACKETED_PASTE),
1948 ansi::Mode::SgrMouse => {
1950 self.mode.remove(TermMode::UTF8_MOUSE);
1951 self.mode.insert(TermMode::SGR_MOUSE);
1952 },
1953 ansi::Mode::Utf8Mouse => {
1954 self.mode.remove(TermMode::SGR_MOUSE);
1955 self.mode.insert(TermMode::UTF8_MOUSE);
1956 },
1957 ansi::Mode::AlternateScroll => self.mode.insert(TermMode::ALTERNATE_SCROLL),
1958 ansi::Mode::LineWrap => self.mode.insert(TermMode::LINE_WRAP),
1959 ansi::Mode::LineFeedNewLine => self.mode.insert(TermMode::LINE_FEED_NEW_LINE),
1960 ansi::Mode::Origin => self.mode.insert(TermMode::ORIGIN),
1961 ansi::Mode::DECCOLM => self.deccolm(),
1962 ansi::Mode::Insert => self.mode.insert(TermMode::INSERT), ansi::Mode::BlinkingCursor => {
1964 trace!("... unimplemented mode");
1965 },
1966 }
1967 }
1968
1969 #[inline]
1970 fn unset_mode(&mut self, mode: ansi::Mode) {
1971 trace!("Unsetting mode: {:?}", mode);
1972 match mode {
1973 ansi::Mode::SwapScreenAndSetRestoreCursor => {
1974 if self.alt {
1975 self.mode.remove(TermMode::ALT_SCREEN);
1976 self.restore_cursor_position();
1977 self.swap_alt();
1978 self.restore_cursor_position();
1979 }
1980 },
1981 ansi::Mode::ShowCursor => self.mode.remove(TermMode::SHOW_CURSOR),
1982 ansi::Mode::CursorKeys => self.mode.remove(TermMode::APP_CURSOR),
1983 ansi::Mode::ReportMouseClicks => {
1984 self.mode.remove(TermMode::MOUSE_REPORT_CLICK);
1985 self.event_proxy.send_event(Event::MouseCursorDirty);
1986 },
1987 ansi::Mode::ReportCellMouseMotion => {
1988 self.mode.remove(TermMode::MOUSE_DRAG);
1989 self.event_proxy.send_event(Event::MouseCursorDirty);
1990 },
1991 ansi::Mode::ReportAllMouseMotion => {
1992 self.mode.remove(TermMode::MOUSE_MOTION);
1993 self.event_proxy.send_event(Event::MouseCursorDirty);
1994 },
1995 ansi::Mode::ReportFocusInOut => self.mode.remove(TermMode::FOCUS_IN_OUT),
1996 ansi::Mode::BracketedPaste => self.mode.remove(TermMode::BRACKETED_PASTE),
1997 ansi::Mode::SgrMouse => self.mode.remove(TermMode::SGR_MOUSE),
1998 ansi::Mode::Utf8Mouse => self.mode.remove(TermMode::UTF8_MOUSE),
1999 ansi::Mode::AlternateScroll => self.mode.remove(TermMode::ALTERNATE_SCROLL),
2000 ansi::Mode::LineWrap => self.mode.remove(TermMode::LINE_WRAP),
2001 ansi::Mode::LineFeedNewLine => self.mode.remove(TermMode::LINE_FEED_NEW_LINE),
2002 ansi::Mode::Origin => self.mode.remove(TermMode::ORIGIN),
2003 ansi::Mode::DECCOLM => self.deccolm(),
2004 ansi::Mode::Insert => self.mode.remove(TermMode::INSERT),
2005 ansi::Mode::BlinkingCursor => {
2006 trace!("... unimplemented mode");
2007 },
2008 }
2009 }
2010
2011 #[inline]
2012 fn set_scrolling_region(&mut self, top: usize, bottom: usize) {
2013 if top >= bottom {
2014 debug!("Invalid scrolling region: ({};{})", top, bottom);
2015 return;
2016 }
2017
2018 let start = Line(top - 1);
2023 let end = Line(bottom);
2024
2025 trace!("Setting scrolling region: ({};{})", start, end);
2026
2027 self.scroll_region.start = min(start, self.grid.num_lines());
2028 self.scroll_region.end = min(end, self.grid.num_lines());
2029 self.goto(Line(0), Column(0));
2030 }
2031
2032 #[inline]
2033 fn set_keypad_application_mode(&mut self) {
2034 trace!("Setting keypad application mode");
2035 self.mode.insert(TermMode::APP_KEYPAD);
2036 }
2037
2038 #[inline]
2039 fn unset_keypad_application_mode(&mut self) {
2040 trace!("Unsetting keypad application mode");
2041 self.mode.remove(TermMode::APP_KEYPAD);
2042 }
2043
2044 #[inline]
2045 fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
2046 trace!("Configuring charset {:?} as {:?}", index, charset);
2047 self.cursor.charsets[index] = charset;
2048 }
2049
2050 #[inline]
2051 fn set_active_charset(&mut self, index: CharsetIndex) {
2052 trace!("Setting active charset {:?}", index);
2053 self.active_charset = index;
2054 }
2055
2056 #[inline]
2057 fn set_cursor_style(&mut self, style: Option<CursorStyle>) {
2058 trace!("Setting cursor style {:?}", style);
2059 self.cursor_style = style;
2060 }
2061
2062 #[inline]
2063 fn push_title(&mut self) {
2064 trace!("Pushing '{}' onto title stack", self.title);
2065
2066 if self.title_stack.len() >= TITLE_STACK_MAX_DEPTH {
2067 let removed = self.title_stack.remove(0);
2068 trace!(
2069 "Removing '{}' from bottom of title stack that exceeds its maximum depth",
2070 removed
2071 );
2072 }
2073
2074 self.title_stack.push(self.title.clone());
2075 }
2076
2077 #[inline]
2078 fn pop_title(&mut self) {
2079 trace!("Attempting to pop title from stack...");
2080
2081 if let Some(popped) = self.title_stack.pop() {
2082 trace!("Title '{}' popped from stack", popped);
2083 self.set_title(&popped);
2084 }
2085 }
2086}
2087
2088struct TabStops {
2089 tabs: Vec<bool>,
2090}
2091
2092impl TabStops {
2093 fn new(num_cols: Column, tabspaces: usize) -> TabStops {
2094 TabStops {
2095 tabs: IndexRange::from(Column(0)..num_cols)
2096 .map(|i| (*i as usize) % tabspaces == 0)
2097 .collect::<Vec<bool>>(),
2098 }
2099 }
2100
2101 fn clear_all(&mut self) {
2102 unsafe {
2103 ptr::write_bytes(self.tabs.as_mut_ptr(), 0, self.tabs.len());
2104 }
2105 }
2106}
2107
2108impl Index<Column> for TabStops {
2109 type Output = bool;
2110
2111 fn index(&self, index: Column) -> &bool {
2112 &self.tabs[index.0]
2113 }
2114}
2115
2116impl IndexMut<Column> for TabStops {
2117 fn index_mut(&mut self, index: Column) -> &mut bool {
2118 self.tabs.index_mut(index.0)
2119 }
2120}
2121
2122#[cfg(test)]
2123mod tests {
2124 use std::mem;
2125
2126 use crate::ansi::{self, CharsetIndex, Handler, StandardCharset};
2127 use crate::clipboard::Clipboard;
2128 use crate::config::MockConfig;
2129 use crate::event::{Event, EventListener};
2130 use crate::grid::{Grid, Scroll};
2131 use crate::index::{Column, Line, Point, Side};
2132 use crate::selection::Selection;
2133 use crate::term::cell::{Cell, Flags};
2134 use crate::term::{SizeInfo, Term};
2135
2136 struct Mock;
2137 impl EventListener for Mock {
2138 fn send_event(&self, _event: Event) {}
2139 }
2140
2141 #[test]
2142 fn semantic_selection_works() {
2143 let size = SizeInfo {
2144 width: 21.0,
2145 height: 51.0,
2146 cell_width: 3.0,
2147 cell_height: 3.0,
2148 padding_x: 0.0,
2149 padding_y: 0.0,
2150 dpr: 1.0,
2151 };
2152 let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2153 let mut grid: Grid<Cell> = Grid::new(Line(3), Column(5), 0, Cell::default());
2154 for i in 0..5 {
2155 for j in 0..2 {
2156 grid[Line(j)][Column(i)].c = 'a';
2157 }
2158 }
2159 grid[Line(0)][Column(0)].c = '"';
2160 grid[Line(0)][Column(3)].c = '"';
2161 grid[Line(1)][Column(2)].c = '"';
2162 grid[Line(0)][Column(4)].flags.insert(Flags::WRAPLINE);
2163
2164 let mut escape_chars = String::from("\"");
2165
2166 mem::swap(&mut term.grid, &mut grid);
2167 mem::swap(&mut term.semantic_escape_chars, &mut escape_chars);
2168
2169 {
2170 *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) }));
2171 assert_eq!(term.selection_to_string(), Some(String::from("aa")));
2172 }
2173
2174 {
2175 *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) }));
2176 assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
2177 }
2178
2179 {
2180 *term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) }));
2181 assert_eq!(term.selection_to_string(), Some(String::from("aaa")));
2182 }
2183 }
2184
2185 #[test]
2186 fn line_selection_works() {
2187 let size = SizeInfo {
2188 width: 21.0,
2189 height: 51.0,
2190 cell_width: 3.0,
2191 cell_height: 3.0,
2192 padding_x: 0.0,
2193 padding_y: 0.0,
2194 dpr: 1.0,
2195 };
2196 let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2197 let mut grid: Grid<Cell> = Grid::new(Line(1), Column(5), 0, Cell::default());
2198 for i in 0..5 {
2199 grid[Line(0)][Column(i)].c = 'a';
2200 }
2201 grid[Line(0)][Column(0)].c = '"';
2202 grid[Line(0)][Column(3)].c = '"';
2203
2204 mem::swap(&mut term.grid, &mut grid);
2205
2206 *term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) }));
2207 assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n")));
2208 }
2209
2210 #[test]
2211 fn selecting_empty_line() {
2212 let size = SizeInfo {
2213 width: 21.0,
2214 height: 51.0,
2215 cell_width: 3.0,
2216 cell_height: 3.0,
2217 padding_x: 0.0,
2218 padding_y: 0.0,
2219 dpr: 1.0,
2220 };
2221 let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2222 let mut grid: Grid<Cell> = Grid::new(Line(3), Column(3), 0, Cell::default());
2223 for l in 0..3 {
2224 if l != 1 {
2225 for c in 0..3 {
2226 grid[Line(l)][Column(c)].c = 'a';
2227 }
2228 }
2229 }
2230
2231 mem::swap(&mut term.grid, &mut grid);
2232
2233 let mut selection = Selection::simple(Point { line: 2, col: Column(0) }, Side::Left);
2234 selection.update(Point { line: 0, col: Column(2) }, Side::Right);
2235 *term.selection_mut() = Some(selection);
2236 assert_eq!(term.selection_to_string(), Some("aaa\n\naaa\n".into()));
2237 }
2238
2239 #[test]
2244 fn grid_serde() {
2245 let template = Cell::default();
2246
2247 let grid: Grid<Cell> = Grid::new(Line(24), Column(80), 0, template);
2248 let serialized = serde_json::to_string(&grid).expect("ser");
2249 let deserialized = serde_json::from_str::<Grid<Cell>>(&serialized).expect("de");
2250
2251 assert_eq!(deserialized, grid);
2252 }
2253
2254 #[test]
2255 fn input_line_drawing_character() {
2256 let size = SizeInfo {
2257 width: 21.0,
2258 height: 51.0,
2259 cell_width: 3.0,
2260 cell_height: 3.0,
2261 padding_x: 0.0,
2262 padding_y: 0.0,
2263 dpr: 1.0,
2264 };
2265 let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2266 let cursor = Point::new(Line(0), Column(0));
2267 term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing);
2268 term.input('a');
2269
2270 assert_eq!(term.grid()[&cursor].c, '▒');
2271 }
2272
2273 #[test]
2274 fn clear_saved_lines() {
2275 let size = SizeInfo {
2276 width: 21.0,
2277 height: 51.0,
2278 cell_width: 3.0,
2279 cell_height: 3.0,
2280 padding_x: 0.0,
2281 padding_y: 0.0,
2282 dpr: 1.0,
2283 };
2284 let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2285
2286 term.grid.scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default());
2288
2289 term.clear_screen(ansi::ClearMode::Saved);
2291
2292 let mut scrolled_grid = term.grid.clone();
2294 scrolled_grid.scroll_display(Scroll::Top);
2295
2296 scrolled_grid.truncate();
2298 term.grid.truncate();
2299
2300 assert_eq!(term.grid, scrolled_grid);
2301 }
2302
2303 #[test]
2304 fn window_title() {
2305 let size = SizeInfo {
2306 width: 21.0,
2307 height: 51.0,
2308 cell_width: 3.0,
2309 cell_height: 3.0,
2310 padding_x: 0.0,
2311 padding_y: 0.0,
2312 dpr: 1.0,
2313 };
2314 let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock);
2315
2316 {
2318 term.title = "Test".to_string();
2319 assert_eq!(term.title, "Test");
2320 }
2321
2322 {
2324 term.push_title();
2325 term.title = "Next".to_string();
2326 assert_eq!(term.title, "Next");
2327 assert_eq!(term.title_stack.get(0).unwrap(), "Test");
2328 }
2329
2330 {
2332 term.pop_title();
2333 assert_eq!(term.title, "Test");
2334 assert!(term.title_stack.is_empty());
2335 }
2336
2337 {
2339 for _ in 0..4097 {
2340 term.push_title();
2341 }
2342 assert_eq!(term.title_stack.len(), 4096);
2343 }
2344
2345 {
2347 term.push_title();
2348 term.reset_state();
2349 assert_eq!(term.title, "Alacritty");
2350 assert!(term.title_stack.is_empty());
2351 }
2352 }
2353}
2354
2355#[cfg(all(test, feature = "bench"))]
2356mod benches {
2357 extern crate serde_json as json;
2358 extern crate test;
2359
2360 use std::fs::File;
2361 use std::io::Read;
2362 use std::mem;
2363 use std::path::Path;
2364
2365 use crate::clipboard::Clipboard;
2366 use crate::config::MockConfig;
2367 use crate::event::{Event, EventListener};
2368 use crate::grid::Grid;
2369
2370 use super::cell::Cell;
2371 use super::{SizeInfo, Term};
2372
2373 struct Mock;
2374 impl EventListener for Mock {
2375 fn send_event(&self, _event: Event) {}
2376 }
2377
2378 fn read_string<P>(path: P) -> String
2379 where
2380 P: AsRef<Path>,
2381 {
2382 let mut res = String::new();
2383 File::open(path.as_ref()).unwrap().read_to_string(&mut res).unwrap();
2384
2385 res
2386 }
2387
2388 #[bench]
2398 fn render_iter(b: &mut test::Bencher) {
2399 let serialized_grid = read_string(concat!(
2401 env!("CARGO_MANIFEST_DIR"),
2402 "/tests/ref/vim_large_window_scroll/grid.json"
2403 ));
2404 let serialized_size = read_string(concat!(
2405 env!("CARGO_MANIFEST_DIR"),
2406 "/tests/ref/vim_large_window_scroll/size.json"
2407 ));
2408
2409 let mut grid: Grid<Cell> = json::from_str(&serialized_grid).unwrap();
2410 let size: SizeInfo = json::from_str(&serialized_size).unwrap();
2411
2412 let config = MockConfig::default();
2413
2414 let mut terminal = Term::new(&config, &size, Clipboard::new_nop(), Mock);
2415 mem::swap(&mut terminal.grid, &mut grid);
2416
2417 b.iter(|| {
2418 let iter = terminal.renderable_cells(&config);
2419 for cell in iter {
2420 test::black_box(cell);
2421 }
2422 })
2423 }
2424}