Skip to main content

tui_term/
widget.rs

1use std::borrow::Cow;
2
3use ratatui_core::{
4    buffer::Buffer,
5    layout::Rect,
6    style::{Color, Modifier, Style},
7    widgets::Widget,
8};
9use ratatui_widgets::{block::Block, clear::Clear};
10
11use crate::state;
12
13/// A trait representing a pseudo-terminal screen.
14///
15/// Implementing this trait allows for backends other than `vt100` to be used
16/// with the `PseudoTerminal` widget.
17///
18/// All row coordinates use a visible coordinate system where row 0 is the
19/// topmost row currently displayed. When scrollback is active, this may
20/// differ from the underlying terminal buffer's row numbering. Implementors
21/// must ensure that [`cell`](Self::cell) and [`cursor_position`](Self::cursor_position)
22/// use the same coordinate space.
23pub trait Screen {
24    /// The type of cell this screen contains
25    type C: Cell;
26
27    /// Returns the cell at the given location in visible coordinates.
28    ///
29    /// Row 0 is the topmost visible row, accounting for any scrollback offset.
30    fn cell(&self, row: u16, col: u16) -> Option<&Self::C>;
31    /// Returns whether the cursor should be hidden.
32    fn hide_cursor(&self) -> bool;
33    /// Returns the cursor position in visible coordinates.
34    ///
35    /// The return value is (row, column), where row 0 is the topmost visible
36    /// row. This must use the same coordinate space as [`cell`](Self::cell).
37    /// When scrollback is active, the cursor row must be shifted down by the
38    /// number of scrollback rows visible above the active screen content.
39    ///
40    /// If the cursor is not within the visible area (e.g., scrolled
41    /// off-screen), the returned row may exceed the screen dimensions.
42    /// The rendering layer handles bounds checking.
43    fn cursor_position(&self) -> (u16, u16);
44}
45
46/// A trait for representing a single cell on a screen.
47pub trait Cell {
48    /// Whether the cell has any contents that could be rendered to the screen.
49    fn has_contents(&self) -> bool;
50    /// Apply the contents and styling of this cell to the provided buffer cell.
51    fn apply(&self, cell: &mut ratatui_core::buffer::Cell);
52}
53
54/// A widget representing a pseudo-terminal screen.
55///
56/// The `PseudoTerminal` widget displays the contents of a pseudo-terminal screen,
57/// which is typically populated with text and control sequences from a terminal emulator.
58/// It provides a visual representation of the terminal output within a TUI application.
59///
60/// The contents of the pseudo-terminal screen are represented by a `vt100::Screen` object.
61/// The `vt100` library provides functionality for parsing and processing terminal control sequences
62/// and handling terminal state, allowing the `PseudoTerminal` widget to accurately render the
63/// terminal output.
64///
65/// # Examples
66///
67/// ```rust
68/// use ratatui_core::style::{Color, Modifier, Style};
69/// use ratatui_widgets::{block::Block, borders::Borders};
70/// use tui_term::widget::PseudoTerminal;
71/// use vt100::Parser;
72///
73/// let mut parser = vt100::Parser::new(24, 80, 0);
74/// let pseudo_term = PseudoTerminal::new(parser.screen())
75///     .block(Block::default().title("Terminal").borders(Borders::ALL))
76///     .style(
77///         Style::default()
78///             .fg(Color::White)
79///             .bg(Color::Black)
80///             .add_modifier(Modifier::BOLD),
81///     );
82/// ```
83#[non_exhaustive]
84pub struct PseudoTerminal<'a, S> {
85    screen: &'a S,
86    pub(crate) block: Option<Block<'a>>,
87    style: Option<Style>,
88    pub(crate) cursor: Cursor,
89}
90
91#[non_exhaustive]
92pub struct Cursor {
93    pub(crate) show: bool,
94    pub(crate) symbol: Cow<'static, str>,
95    pub(crate) style: Style,
96    pub(crate) overlay_style: Style,
97}
98
99impl Cursor {
100    /// Sets the symbol for the cursor.
101    ///
102    /// # Arguments
103    ///
104    /// * `symbol`: The symbol to set as the cursor.
105    ///
106    /// # Example
107    ///
108    /// ```
109    /// use ratatui_core::style::Style;
110    /// use tui_term::widget::Cursor;
111    ///
112    /// let cursor = Cursor::default().symbol("|");
113    /// ```
114    #[inline]
115    #[must_use]
116    pub fn symbol(mut self, symbol: impl Into<Cow<'static, str>>) -> Self {
117        self.symbol = symbol.into();
118        self
119    }
120
121    /// Sets the style for the cursor.
122    ///
123    /// # Arguments
124    ///
125    /// * `style`: The `Style` to set for the cursor.
126    ///
127    /// # Example
128    ///
129    /// ```
130    /// use ratatui_core::style::Style;
131    /// use tui_term::widget::Cursor;
132    ///
133    /// let cursor = Cursor::default().style(Style::default());
134    /// ```
135    #[inline]
136    #[must_use]
137    pub const fn style(mut self, style: Style) -> Self {
138        self.style = style;
139        self
140    }
141
142    /// Sets the overlay style for the cursor.
143    ///
144    /// The overlay style is used when the cursor overlaps with existing content on the screen.
145    ///
146    /// # Arguments
147    ///
148    /// * `overlay_style`: The `Style` to set as the overlay style for the cursor.
149    ///
150    /// # Example
151    ///
152    /// ```
153    /// use ratatui_core::style::Style;
154    /// use tui_term::widget::Cursor;
155    ///
156    /// let cursor = Cursor::default().overlay_style(Style::default());
157    /// ```
158    #[inline]
159    #[must_use]
160    pub const fn overlay_style(mut self, overlay_style: Style) -> Self {
161        self.overlay_style = overlay_style;
162        self
163    }
164
165    /// Set the visibility of the cursor (default = shown)
166    #[inline]
167    #[must_use]
168    pub const fn visibility(mut self, show: bool) -> Self {
169        self.show = show;
170        self
171    }
172
173    /// Show the cursor (default)
174    #[inline]
175    pub fn show(&mut self) {
176        self.show = true;
177    }
178
179    /// Hide the cursor
180    #[inline]
181    pub fn hide(&mut self) {
182        self.show = false;
183    }
184}
185
186impl Default for Cursor {
187    #[inline]
188    fn default() -> Self {
189        Self {
190            show: true,
191            symbol: Cow::Borrowed("\u{2588}"), //"█".
192            style: Style::default().fg(Color::Gray),
193            overlay_style: Style::default().add_modifier(Modifier::REVERSED),
194        }
195    }
196}
197
198impl<'a, S: Screen> PseudoTerminal<'a, S> {
199    /// Creates a new instance of `PseudoTerminal`.
200    ///
201    /// # Arguments
202    ///
203    /// * `screen`: The reference to the `Screen`.
204    ///
205    /// # Example
206    ///
207    /// ```
208    /// use tui_term::widget::PseudoTerminal;
209    /// use vt100::Parser;
210    ///
211    /// let mut parser = vt100::Parser::new(24, 80, 0);
212    /// let pseudo_term = PseudoTerminal::new(parser.screen());
213    /// ```
214    #[inline]
215    #[must_use]
216    pub fn new(screen: &'a S) -> Self {
217        PseudoTerminal {
218            screen,
219            block: None,
220            style: None,
221            cursor: Cursor::default(),
222        }
223    }
224
225    /// Sets the block for the `PseudoTerminal`.
226    ///
227    /// # Arguments
228    ///
229    /// * `block`: The `Block` to set.
230    ///
231    /// # Example
232    ///
233    /// ```
234    /// use ratatui_widgets::block::Block;
235    /// use tui_term::widget::PseudoTerminal;
236    /// use vt100::Parser;
237    ///
238    /// let mut parser = vt100::Parser::new(24, 80, 0);
239    /// let block = Block::default();
240    /// let pseudo_term = PseudoTerminal::new(parser.screen()).block(block);
241    /// ```
242    #[inline]
243    #[must_use]
244    pub fn block(mut self, block: Block<'a>) -> Self {
245        self.block = Some(block);
246        self
247    }
248
249    /// Sets the cursor configuration for the `PseudoTerminal`.
250    ///
251    /// The `cursor` method allows configuring the appearance of the cursor within the
252    /// `PseudoTerminal` widget.
253    ///
254    /// # Arguments
255    ///
256    /// * `cursor`: The `Cursor` configuration to set.
257    ///
258    /// # Example
259    ///
260    /// ```rust
261    /// use ratatui_core::style::Style;
262    /// use tui_term::widget::{Cursor, PseudoTerminal};
263    ///
264    /// let mut parser = vt100::Parser::new(24, 80, 0);
265    /// let cursor = Cursor::default().symbol("|").style(Style::default());
266    /// let pseudo_term = PseudoTerminal::new(parser.screen()).cursor(cursor);
267    /// ```
268    #[inline]
269    #[must_use]
270    pub fn cursor(mut self, cursor: Cursor) -> Self {
271        self.cursor = cursor;
272        self
273    }
274
275    /// Sets the style for `PseudoTerminal`.
276    ///
277    /// # Arguments
278    ///
279    /// * `style`: The `Style` to set.
280    ///
281    /// # Example
282    ///
283    /// ```
284    /// use ratatui_core::style::Style;
285    /// use tui_term::widget::PseudoTerminal;
286    ///
287    /// let mut parser = vt100::Parser::new(24, 80, 0);
288    /// let style = Style::default();
289    /// let pseudo_term = PseudoTerminal::new(parser.screen()).style(style);
290    /// ```
291    #[inline]
292    #[must_use]
293    pub const fn style(mut self, style: Style) -> Self {
294        self.style = Some(style);
295        self
296    }
297
298    #[inline]
299    #[must_use]
300    pub const fn screen(&self) -> &S {
301        self.screen
302    }
303}
304
305impl<S: Screen> Widget for &PseudoTerminal<'_, S> {
306    #[inline]
307    fn render(self, area: Rect, buf: &mut Buffer) {
308        Clear.render(area, buf);
309        let area = self.block.as_ref().map_or(area, |b| {
310            let inner_area = b.inner(area);
311            b.clone().render(area, buf);
312            inner_area
313        });
314        state::handle(self, area, buf);
315    }
316}
317
318impl<S: Screen> Widget for PseudoTerminal<'_, S> {
319    #[inline]
320    fn render(self, area: Rect, buf: &mut Buffer) {
321        (&self).render(area, buf);
322    }
323}
324
325#[cfg(all(test, feature = "vt100"))]
326mod tests {
327    use ratatui::Terminal;
328    use ratatui_core::backend::TestBackend;
329    use ratatui_widgets::borders::Borders;
330
331    use super::*;
332
333    fn snapshot_typescript(stream: &[u8]) -> String {
334        let backend = TestBackend::new(80, 24);
335        let mut terminal = Terminal::new(backend).unwrap();
336        let mut parser = vt100::Parser::new(24, 80, 0);
337        parser.process(stream);
338        let pseudo_term = PseudoTerminal::new(parser.screen());
339        terminal
340            .draw(|f| {
341                f.render_widget(pseudo_term, f.area());
342            })
343            .unwrap();
344        format!("{:?}", terminal.backend().buffer())
345    }
346
347    #[test]
348    fn scrollback_cursor_not_rendered_when_off_screen() {
349        let backend = TestBackend::new(40, 4);
350        let mut terminal = Terminal::new(backend).unwrap();
351        let mut parser = vt100::Parser::new(4, 40, 20);
352
353        // 10 lines on 4 rows: 6 scroll off. With set_scrollback(4) all
354        // visible rows are scrollback. Cursor maps to visible row 7, off-screen.
355        for i in 0..10 {
356            parser.process(format!("[scrollback line {i}]\r\n").as_bytes());
357        }
358        parser.screen_mut().set_scrollback(4);
359
360        let pseudo_term = PseudoTerminal::new(parser.screen());
361        terminal
362            .draw(|f| {
363                f.render_widget(pseudo_term, f.area());
364            })
365            .unwrap();
366
367        let buf = terminal.backend().buffer();
368        for row in 0..4 {
369            for col in 0..40 {
370                let cell = &buf[(col, row)];
371                assert!(
372                    !cell.modifier.contains(Modifier::REVERSED),
373                    "REVERSED cursor style should not appear at ({col}, {row}) \
374                     when cursor is off-screen due to scrollback"
375                );
376            }
377        }
378        let view = format!("{:?}", terminal.backend().buffer());
379        insta::assert_snapshot!(view);
380    }
381
382    #[test]
383    fn scrollback_partial_cursor_visible_at_adjusted_row() {
384        let backend = TestBackend::new(40, 6);
385        let mut terminal = Terminal::new(backend).unwrap();
386        let mut parser = vt100::Parser::new(6, 40, 20);
387
388        // 8 lines on 6 rows: 3 scroll off into scrollback.
389        // \x1b[H moves cursor to drawing row 0.
390        parser.process(b"[scrollback: hidden 1]\r\n");
391        parser.process(b"[scrollback: hidden 2]\r\n");
392        parser.process(b"[scrollback: shown at row 0]\r\n");
393        parser.process(b"[draw-0: cursor row]\r\n");
394        parser.process(b"[draw-1]\r\n");
395        parser.process(b"[draw-2]\r\n");
396        parser.process(b"[draw-3]\r\n");
397        parser.process(b"[draw-4]\r\n");
398        parser.process(b"\x1b[H");
399
400        // With scrollback=1, drawing row 0 shifts to visible row 1.
401        // Visible row 0 becomes the last scrollback line.
402        parser.screen_mut().set_scrollback(1);
403
404        let pseudo_term = PseudoTerminal::new(parser.screen());
405        terminal
406            .draw(|f| {
407                f.render_widget(pseudo_term, f.area());
408            })
409            .unwrap();
410
411        let buf = terminal.backend().buffer();
412        for col in 0..40 {
413            let cell = &buf[(col, 0)];
414            assert!(
415                !cell.modifier.contains(Modifier::REVERSED),
416                "scrollback row 0 at col {col} should not have cursor styling"
417            );
418        }
419        let cursor_cell = &buf[(0, 1)];
420        assert!(
421            cursor_cell.modifier.contains(Modifier::REVERSED),
422            "cursor should render at visible row 1 (adjusted from drawing row 0)"
423        );
424        let view = format!("{:?}", terminal.backend().buffer());
425        insta::assert_snapshot!(view);
426    }
427
428    #[test]
429    fn scrollback_hide_cursor_suppresses_rendering() {
430        let backend = TestBackend::new(40, 6);
431        let mut terminal = Terminal::new(backend).unwrap();
432        let mut parser = vt100::Parser::new(6, 40, 20);
433
434        for i in 0..8 {
435            parser.process(format!("line {i}\r\n").as_bytes());
436        }
437        parser.process(b"\x1b[H");
438        // Hide cursor via escape sequence
439        parser.process(b"\x1b[?25l");
440        parser.screen_mut().set_scrollback(1);
441
442        let pseudo_term = PseudoTerminal::new(parser.screen());
443        terminal
444            .draw(|f| {
445                f.render_widget(pseudo_term, f.area());
446            })
447            .unwrap();
448
449        let buf = terminal.backend().buffer();
450        for row in 0..6 {
451            for col in 0..40 {
452                let cell = &buf[(col, row)];
453                assert!(
454                    !cell.modifier.contains(Modifier::REVERSED),
455                    "hidden cursor should not style ({col}, {row}) even with scrollback"
456                );
457            }
458        }
459    }
460
461    #[test]
462    fn scrollback_cursor_with_block() {
463        let backend = TestBackend::new(42, 8);
464        let mut terminal = Terminal::new(backend).unwrap();
465        let mut parser = vt100::Parser::new(6, 40, 20);
466
467        for i in 0..8 {
468            parser.process(format!("line {i}\r\n").as_bytes());
469        }
470        parser.process(b"\x1b[H");
471        parser.screen_mut().set_scrollback(1);
472
473        let block = Block::default().borders(Borders::ALL).title("pty");
474        let pseudo_term = PseudoTerminal::new(parser.screen()).block(block);
475        terminal
476            .draw(|f| {
477                f.render_widget(pseudo_term, f.area());
478            })
479            .unwrap();
480
481        // Border takes 1 row/col on each side, so inner area starts at (1,1).
482        // Visible row 0 is scrollback (inner buf row 1), no cursor.
483        // Cursor at drawing row 0 + scrollback 1 = visible row 1 (inner buf row 2).
484        let buf = terminal.backend().buffer();
485        let scrollback_cell = &buf[(1, 1)];
486        assert!(
487            !scrollback_cell.modifier.contains(Modifier::REVERSED),
488            "scrollback row inside block should not have cursor styling"
489        );
490        let cursor_cell = &buf[(1, 2)];
491        assert!(
492            cursor_cell.modifier.contains(Modifier::REVERSED),
493            "cursor should render at inner row 1 (buf row 2) with block border"
494        );
495    }
496
497    #[test]
498    fn empty_actions() {
499        let backend = TestBackend::new(80, 24);
500        let mut terminal = Terminal::new(backend).unwrap();
501        let mut parser = vt100::Parser::new(24, 80, 0);
502        parser.process(b" ");
503        let pseudo_term = PseudoTerminal::new(parser.screen());
504        terminal
505            .draw(|f| {
506                f.render_widget(pseudo_term, f.area());
507            })
508            .unwrap();
509        let view = format!("{:?}", terminal.backend().buffer());
510        insta::assert_snapshot!(view);
511    }
512    #[test]
513    fn boundary_rows_overshot_no_panic() {
514        let stream = include_bytes!("../test/typescript/simple_ls.typescript");
515        // Make the backend on purpose much smaller
516        let backend = TestBackend::new(80, 4);
517        let mut terminal = Terminal::new(backend).unwrap();
518        let mut parser = vt100::Parser::new(24, 80, 0);
519        parser.process(stream);
520        let pseudo_term = PseudoTerminal::new(parser.screen());
521        terminal
522            .draw(|f| {
523                f.render_widget(pseudo_term, f.area());
524            })
525            .unwrap();
526        let view = format!("{:?}", terminal.backend().buffer());
527        insta::assert_snapshot!(view);
528    }
529
530    #[test]
531    fn simple_ls() {
532        let stream = include_bytes!("../test/typescript/simple_ls.typescript");
533        let view = snapshot_typescript(stream);
534        insta::assert_snapshot!(view);
535    }
536    #[test]
537    fn simple_cursor_alternate_symbol() {
538        let stream = include_bytes!("../test/typescript/simple_ls.typescript");
539        let backend = TestBackend::new(80, 24);
540        let mut terminal = Terminal::new(backend).unwrap();
541        let mut parser = vt100::Parser::new(24, 80, 0);
542        let cursor = Cursor::default().symbol("|");
543        parser.process(stream);
544        let pseudo_term = PseudoTerminal::new(parser.screen()).cursor(cursor);
545        terminal
546            .draw(|f| {
547                f.render_widget(pseudo_term, f.area());
548            })
549            .unwrap();
550        let view = format!("{:?}", terminal.backend().buffer());
551        insta::assert_snapshot!(view);
552    }
553    #[test]
554    fn simple_cursor_styled() {
555        let stream = include_bytes!("../test/typescript/simple_ls.typescript");
556        let backend = TestBackend::new(80, 24);
557        let mut terminal = Terminal::new(backend).unwrap();
558        let mut parser = vt100::Parser::new(24, 80, 0);
559        let style = Style::default().bg(Color::Cyan).fg(Color::LightRed);
560        let cursor = Cursor::default().symbol("|").style(style);
561        parser.process(stream);
562        let pseudo_term = PseudoTerminal::new(parser.screen()).cursor(cursor);
563        terminal
564            .draw(|f| {
565                f.render_widget(pseudo_term, f.area());
566            })
567            .unwrap();
568        let view = format!("{:?}", terminal.backend().buffer());
569        insta::assert_snapshot!(view);
570    }
571    #[test]
572    fn simple_cursor_hide() {
573        let stream = include_bytes!("../test/typescript/simple_ls.typescript");
574        let backend = TestBackend::new(80, 24);
575        let mut terminal = Terminal::new(backend).unwrap();
576        let mut parser = vt100::Parser::new(24, 80, 0);
577        let cursor = Cursor::default().visibility(false);
578        parser.process(stream);
579        let pseudo_term = PseudoTerminal::new(parser.screen()).cursor(cursor);
580        terminal
581            .draw(|f| {
582                f.render_widget(pseudo_term, f.area());
583            })
584            .unwrap();
585        let view = format!("{:?}", terminal.backend().buffer());
586        insta::assert_snapshot!(view);
587    }
588    #[test]
589    fn simple_cursor_hide_alt() {
590        let stream = include_bytes!("../test/typescript/simple_ls.typescript");
591        let backend = TestBackend::new(80, 24);
592        let mut terminal = Terminal::new(backend).unwrap();
593        let mut parser = vt100::Parser::new(24, 80, 0);
594        let mut cursor = Cursor::default();
595        cursor.hide();
596        parser.process(stream);
597        let pseudo_term = PseudoTerminal::new(parser.screen()).cursor(cursor);
598        terminal
599            .draw(|f| {
600                f.render_widget(pseudo_term, f.area());
601            })
602            .unwrap();
603        let view = format!("{:?}", terminal.backend().buffer());
604        insta::assert_snapshot!(view);
605    }
606    #[test]
607    fn overlapping_cursor() {
608        let stream = include_bytes!("../test/typescript/overlapping_cursor.typescript");
609        let view = snapshot_typescript(stream);
610        insta::assert_snapshot!(view);
611    }
612    #[test]
613    fn overlapping_cursor_alternate_style() {
614        let stream = include_bytes!("../test/typescript/overlapping_cursor.typescript");
615        let backend = TestBackend::new(80, 24);
616        let mut terminal = Terminal::new(backend).unwrap();
617        let mut parser = vt100::Parser::new(24, 80, 0);
618        let style = Style::default().bg(Color::Cyan).fg(Color::LightRed);
619        let cursor = Cursor::default().overlay_style(style);
620        parser.process(stream);
621        let pseudo_term = PseudoTerminal::new(parser.screen()).cursor(cursor);
622        terminal
623            .draw(|f| {
624                f.render_widget(pseudo_term, f.area());
625            })
626            .unwrap();
627        let view = format!("{:?}", terminal.backend().buffer());
628        insta::assert_snapshot!(view);
629    }
630    #[test]
631    fn simple_ls_with_block() {
632        let stream = include_bytes!("../test/typescript/simple_ls.typescript");
633        let backend = TestBackend::new(100, 24);
634        let mut terminal = Terminal::new(backend).unwrap();
635        let mut parser = vt100::Parser::new(24, 80, 0);
636        parser.process(stream);
637        let block = Block::default().borders(Borders::ALL).title("ls");
638        let pseudo_term = PseudoTerminal::new(parser.screen()).block(block);
639        terminal
640            .draw(|f| {
641                f.render_widget(pseudo_term, f.area());
642            })
643            .unwrap();
644        let view = format!("{:?}", terminal.backend().buffer());
645        insta::assert_snapshot!(view);
646    }
647    #[test]
648    fn simple_ls_no_style_from_block() {
649        let stream = include_bytes!("../test/typescript/simple_ls.typescript");
650        let backend = TestBackend::new(100, 24);
651        let mut terminal = Terminal::new(backend).unwrap();
652        let mut parser = vt100::Parser::new(24, 80, 0);
653        parser.process(stream);
654        let block = Block::default()
655            .borders(Borders::ALL)
656            .style(Style::default().add_modifier(Modifier::BOLD))
657            .title("ls");
658        let pseudo_term = PseudoTerminal::new(parser.screen()).block(block);
659        terminal
660            .draw(|f| {
661                f.render_widget(pseudo_term, f.area());
662            })
663            .unwrap();
664        let view = format!("{:?}", terminal.backend().buffer());
665        insta::assert_snapshot!(view);
666    }
667    #[test]
668    fn italic_text() {
669        let stream = b"This line will be displayed in italic. This should have no style.";
670        let view = snapshot_typescript(stream);
671        insta::assert_snapshot!(view);
672    }
673    #[test]
674    fn underlined_text() {
675        let stream =
676            b"This line will be displayed with an underline. This should have no style.";
677        let view = snapshot_typescript(stream);
678        insta::assert_snapshot!(view);
679    }
680    #[test]
681    fn bold_text() {
682        let stream = b"This line will be displayed bold. This should have no style.";
683        let view = snapshot_typescript(stream);
684        insta::assert_snapshot!(view);
685    }
686    #[test]
687    fn inverse_text() {
688        let stream = b"This line will be displayed inversed. This should have no style.";
689        let view = snapshot_typescript(stream);
690        insta::assert_snapshot!(view);
691    }
692    #[test]
693    fn dim_text() {
694        let stream =
695            b"\x1b[2mThis line will be displayed dim/faint.\x1b[0m This should have no style.";
696        let view = snapshot_typescript(stream);
697        insta::assert_snapshot!(view);
698    }
699    #[test]
700    fn combined_modifier_text() {
701        let stream =
702            b"This line will be displayed in italic and underlined. This should have no style.";
703        let view = snapshot_typescript(stream);
704        insta::assert_snapshot!(view);
705    }
706    #[test]
707    fn dim_bold_text() {
708        let stream = b"\x1b[2m\x1b[1mThis is dim and bold. Bold takes precedence.\x1b[0m Normal.";
709        let view = snapshot_typescript(stream);
710        insta::assert_snapshot!(view);
711    }
712
713    #[test]
714    fn vttest_02_01() {
715        let stream = include_bytes!("../test/typescript/vttest_02_01.typescript");
716        let view = snapshot_typescript(stream);
717        insta::assert_snapshot!(view);
718    }
719    #[test]
720    fn vttest_02_02() {
721        let stream = include_bytes!("../test/typescript/vttest_02_02.typescript");
722        let view = snapshot_typescript(stream);
723        insta::assert_snapshot!(view);
724    }
725    #[test]
726    fn vttest_02_03() {
727        let stream = include_bytes!("../test/typescript/vttest_02_03.typescript");
728        let view = snapshot_typescript(stream);
729        insta::assert_snapshot!(view);
730    }
731    #[test]
732    fn vttest_02_04() {
733        let stream = include_bytes!("../test/typescript/vttest_02_04.typescript");
734        let view = snapshot_typescript(stream);
735        insta::assert_snapshot!(view);
736    }
737    #[test]
738    fn vttest_02_05() {
739        let stream = include_bytes!("../test/typescript/vttest_02_05.typescript");
740        let view = snapshot_typescript(stream);
741        insta::assert_snapshot!(view);
742    }
743    #[test]
744    fn vttest_02_06() {
745        let stream = include_bytes!("../test/typescript/vttest_02_06.typescript");
746        let view = snapshot_typescript(stream);
747        insta::assert_snapshot!(view);
748    }
749    #[test]
750    fn vttest_02_07() {
751        let stream = include_bytes!("../test/typescript/vttest_02_07.typescript");
752        let view = snapshot_typescript(stream);
753        insta::assert_snapshot!(view);
754    }
755    #[test]
756    fn vttest_02_08() {
757        let stream = include_bytes!("../test/typescript/vttest_02_08.typescript");
758        let view = snapshot_typescript(stream);
759        insta::assert_snapshot!(view);
760    }
761    #[test]
762    fn vttest_02_09() {
763        let stream = include_bytes!("../test/typescript/vttest_02_09.typescript");
764        let view = snapshot_typescript(stream);
765        insta::assert_snapshot!(view);
766    }
767    #[test]
768    fn vttest_02_10() {
769        let stream = include_bytes!("../test/typescript/vttest_02_10.typescript");
770        let view = snapshot_typescript(stream);
771        insta::assert_snapshot!(view);
772    }
773    #[test]
774    fn vttest_02_11() {
775        let stream = include_bytes!("../test/typescript/vttest_02_11.typescript");
776        let view = snapshot_typescript(stream);
777        insta::assert_snapshot!(view);
778    }
779    #[test]
780    fn vttest_02_12() {
781        let stream = include_bytes!("../test/typescript/vttest_02_12.typescript");
782        let view = snapshot_typescript(stream);
783        insta::assert_snapshot!(view);
784    }
785    #[test]
786    fn vttest_02_13() {
787        let stream = include_bytes!("../test/typescript/vttest_02_13.typescript");
788        let view = snapshot_typescript(stream);
789        insta::assert_snapshot!(view);
790    }
791    #[test]
792    fn vttest_02_14() {
793        let stream = include_bytes!("../test/typescript/vttest_02_14.typescript");
794        let view = snapshot_typescript(stream);
795        insta::assert_snapshot!(view);
796    }
797    #[test]
798    fn vttest_02_15() {
799        let stream = include_bytes!("../test/typescript/vttest_02_15.typescript");
800        let view = snapshot_typescript(stream);
801        insta::assert_snapshot!(view);
802    }
803
804    #[test]
805    fn vttest_03_01() {
806        let stream = include_bytes!("../test/typescript/vttest_03_01.typescript");
807        let view = snapshot_typescript(stream);
808        insta::assert_snapshot!(view);
809    }
810
811    #[test]
812    fn vttest_01_01() {
813        let stream = include_bytes!("../test/typescript/vttest_01_01.typescript");
814        let view = snapshot_typescript(stream);
815        insta::assert_snapshot!(view);
816    }
817
818    #[test]
819    fn vttest_01_02() {
820        let stream = include_bytes!("../test/typescript/vttest_01_02.typescript");
821        let view = snapshot_typescript(stream);
822        insta::assert_snapshot!(view);
823    }
824
825    #[test]
826    fn vttest_01_03() {
827        let stream = include_bytes!("../test/typescript/vttest_01_03.typescript");
828        let view = snapshot_typescript(stream);
829        insta::assert_snapshot!(view);
830    }
831
832    #[test]
833    fn vttest_01_04() {
834        let stream = include_bytes!("../test/typescript/vttest_01_04.typescript");
835        let view = snapshot_typescript(stream);
836        insta::assert_snapshot!(view);
837    }
838
839    #[test]
840    fn vttest_01_05() {
841        let stream = include_bytes!("../test/typescript/vttest_01_05.typescript");
842        let view = snapshot_typescript(stream);
843        insta::assert_snapshot!(view);
844    }
845
846    #[test]
847    fn vttest_menu() {
848        let stream = include_bytes!("../test/typescript/vttest_menu.typescript");
849        let view = snapshot_typescript(stream);
850        insta::assert_snapshot!(view);
851    }
852
853    #[test]
854    fn vttest_08_01() {
855        let stream = include_bytes!("../test/typescript/vttest_08_01.typescript");
856        let view = snapshot_typescript(stream);
857        insta::assert_snapshot!(view);
858    }
859
860    #[test]
861    fn vttest_08_02() {
862        let stream = include_bytes!("../test/typescript/vttest_08_02.typescript");
863        let view = snapshot_typescript(stream);
864        insta::assert_snapshot!(view);
865    }
866
867    #[test]
868    fn vttest_08_03() {
869        let stream = include_bytes!("../test/typescript/vttest_08_03.typescript");
870        let view = snapshot_typescript(stream);
871        insta::assert_snapshot!(view);
872    }
873
874    #[test]
875    fn vttest_08_04() {
876        let stream = include_bytes!("../test/typescript/vttest_08_04.typescript");
877        let view = snapshot_typescript(stream);
878        insta::assert_snapshot!(view);
879    }
880
881    #[test]
882    fn vttest_08_05() {
883        let stream = include_bytes!("../test/typescript/vttest_08_05.typescript");
884        let view = snapshot_typescript(stream);
885        insta::assert_snapshot!(view);
886    }
887
888    #[test]
889    fn vttest_08_06() {
890        let stream = include_bytes!("../test/typescript/vttest_08_06.typescript");
891        let view = snapshot_typescript(stream);
892        insta::assert_snapshot!(view);
893    }
894
895    #[test]
896    fn vttest_08_07() {
897        let stream = include_bytes!("../test/typescript/vttest_08_07.typescript");
898        let view = snapshot_typescript(stream);
899        insta::assert_snapshot!(view);
900    }
901
902    #[test]
903    fn vttest_08_08() {
904        let stream = include_bytes!("../test/typescript/vttest_08_08.typescript");
905        let view = snapshot_typescript(stream);
906        insta::assert_snapshot!(view);
907    }
908
909    #[test]
910    fn vttest_08_09() {
911        let stream = include_bytes!("../test/typescript/vttest_08_09.typescript");
912        let view = snapshot_typescript(stream);
913        insta::assert_snapshot!(view);
914    }
915
916    #[test]
917    fn vttest_08_10() {
918        let stream = include_bytes!("../test/typescript/vttest_08_10.typescript");
919        let view = snapshot_typescript(stream);
920        insta::assert_snapshot!(view);
921    }
922
923    #[test]
924    fn vttest_08_11() {
925        let stream = include_bytes!("../test/typescript/vttest_08_11.typescript");
926        let view = snapshot_typescript(stream);
927        insta::assert_snapshot!(view);
928    }
929
930    #[test]
931    fn vttest_08_12() {
932        let stream = include_bytes!("../test/typescript/vttest_08_12.typescript");
933        let view = snapshot_typescript(stream);
934        insta::assert_snapshot!(view);
935    }
936
937    #[test]
938    fn vttest_08_13() {
939        let stream = include_bytes!("../test/typescript/vttest_08_13.typescript");
940        let view = snapshot_typescript(stream);
941        insta::assert_snapshot!(view);
942    }
943
944    #[test]
945    fn vttest_08_14() {
946        let stream = include_bytes!("../test/typescript/vttest_08_14.typescript");
947        let view = snapshot_typescript(stream);
948        insta::assert_snapshot!(view);
949    }
950
951    #[test]
952    fn vttest_11_06_02() {
953        let stream = include_bytes!("../test/typescript/vttest_11_06_02.typescript");
954        let view = snapshot_typescript(stream);
955        insta::assert_snapshot!(view);
956    }
957
958    #[test]
959    fn vttest_11_06_03() {
960        let stream = include_bytes!("../test/typescript/vttest_11_06_03.typescript");
961        let view = snapshot_typescript(stream);
962        insta::assert_snapshot!(view);
963    }
964
965    #[test]
966    fn vttest_11_06_04() {
967        let stream = include_bytes!("../test/typescript/vttest_11_06_04.typescript");
968        let view = snapshot_typescript(stream);
969        insta::assert_snapshot!(view);
970    }
971
972    #[test]
973    fn vttest_11_06_05() {
974        let stream = include_bytes!("../test/typescript/vttest_11_06_05.typescript");
975        let view = snapshot_typescript(stream);
976        insta::assert_snapshot!(view);
977    }
978
979    #[test]
980    fn vttest_09_01() {
981        let stream = include_bytes!("../test/typescript/vttest_09_01.typescript");
982        let view = snapshot_typescript(stream);
983        insta::assert_snapshot!(view);
984    }
985
986    #[test]
987    fn vttest_09_02() {
988        let stream = include_bytes!("../test/typescript/vttest_09_02.typescript");
989        let view = snapshot_typescript(stream);
990        insta::assert_snapshot!(view);
991    }
992
993    #[test]
994    fn vttest_09_03() {
995        let stream = include_bytes!("../test/typescript/vttest_09_03.typescript");
996        let view = snapshot_typescript(stream);
997        insta::assert_snapshot!(view);
998    }
999
1000    #[test]
1001    fn vttest_09_04() {
1002        let stream = include_bytes!("../test/typescript/vttest_09_04.typescript");
1003        let view = snapshot_typescript(stream);
1004        insta::assert_snapshot!(view);
1005    }
1006
1007    #[test]
1008    fn vttest_09_05() {
1009        let stream = include_bytes!("../test/typescript/vttest_09_05.typescript");
1010        let view = snapshot_typescript(stream);
1011        insta::assert_snapshot!(view);
1012    }
1013
1014    #[test]
1015    fn vttest_09_06() {
1016        let stream = include_bytes!("../test/typescript/vttest_09_06.typescript");
1017        let view = snapshot_typescript(stream);
1018        insta::assert_snapshot!(view);
1019    }
1020
1021    #[test]
1022    fn vttest_09_07() {
1023        let stream = include_bytes!("../test/typescript/vttest_09_07.typescript");
1024        let view = snapshot_typescript(stream);
1025        insta::assert_snapshot!(view);
1026    }
1027
1028    #[test]
1029    fn vttest_09_08() {
1030        let stream = include_bytes!("../test/typescript/vttest_09_08.typescript");
1031        let view = snapshot_typescript(stream);
1032        insta::assert_snapshot!(view);
1033    }
1034
1035    #[test]
1036    fn vttest_09_09() {
1037        let stream = include_bytes!("../test/typescript/vttest_09_09.typescript");
1038        let view = snapshot_typescript(stream);
1039        insta::assert_snapshot!(view);
1040    }
1041
1042    #[test]
1043    fn vttest_11_05_01() {
1044        let stream = include_bytes!("../test/typescript/vttest_11_05_01.typescript");
1045        let view = snapshot_typescript(stream);
1046        insta::assert_snapshot!(view);
1047    }
1048
1049    #[test]
1050    fn vttest_11_05_02() {
1051        let stream = include_bytes!("../test/typescript/vttest_11_05_02.typescript");
1052        let view = snapshot_typescript(stream);
1053        insta::assert_snapshot!(view);
1054    }
1055
1056    #[test]
1057    fn vttest_11_05_03() {
1058        let stream = include_bytes!("../test/typescript/vttest_11_05_03.typescript");
1059        let view = snapshot_typescript(stream);
1060        insta::assert_snapshot!(view);
1061    }
1062
1063    #[test]
1064    fn vttest_11_05_04() {
1065        let stream = include_bytes!("../test/typescript/vttest_11_05_04.typescript");
1066        let view = snapshot_typescript(stream);
1067        insta::assert_snapshot!(view);
1068    }
1069
1070    #[test]
1071    fn vttest_11_05_05() {
1072        let stream = include_bytes!("../test/typescript/vttest_11_05_05.typescript");
1073        let view = snapshot_typescript(stream);
1074        insta::assert_snapshot!(view);
1075    }
1076
1077    #[test]
1078    fn vttest_11_05_06() {
1079        let stream = include_bytes!("../test/typescript/vttest_11_05_06.typescript");
1080        let view = snapshot_typescript(stream);
1081        insta::assert_snapshot!(view);
1082    }
1083
1084    #[test]
1085    fn vttest_11_05_07() {
1086        let stream = include_bytes!("../test/typescript/vttest_11_05_07.typescript");
1087        let view = snapshot_typescript(stream);
1088        insta::assert_snapshot!(view);
1089    }
1090
1091    #[test]
1092    fn vttest_11_05_08() {
1093        let stream = include_bytes!("../test/typescript/vttest_11_05_08.typescript");
1094        let view = snapshot_typescript(stream);
1095        insta::assert_snapshot!(view);
1096    }
1097
1098    #[test]
1099    fn vttest_11_05_09() {
1100        let stream = include_bytes!("../test/typescript/vttest_11_05_09.typescript");
1101        let view = snapshot_typescript(stream);
1102        insta::assert_snapshot!(view);
1103    }
1104
1105    #[test]
1106    fn vttest_11_07_02() {
1107        let stream = include_bytes!("../test/typescript/vttest_11_07_02.typescript");
1108        let view = snapshot_typescript(stream);
1109        insta::assert_snapshot!(view);
1110    }
1111}