Skip to main content

forge_terminal/
parser.rs

1//! The parser's view of the screen, with the one count the grid cannot give.
2//!
3//! **`Term` keeps only the lines the scrollback still holds.** Its line count
4//! stops at the cap, so a read cursor measured in buffer positions stops
5//! advancing once the buffer fills. This wrapper forwards every handler to the
6//! term and counts the linefeeds that scroll a full screen, which are exactly
7//! the lines the buffer drops.
8
9use alacritty_terminal::event::EventListener;
10use alacritty_terminal::grid::Dimensions;
11use alacritty_terminal::index::Line;
12use alacritty_terminal::term::Term;
13use alacritty_terminal::vte::ansi::{
14    Attr, CharsetIndex, ClearMode, CursorShape, CursorStyle, Handler, Hyperlink, KeyboardModes,
15    KeyboardModesApplyBehavior, LineClearMode, Mode, ModifyOtherKeys, PrivateMode, Processor, Rgb,
16    ScpCharPath, ScpUpdateMode, StandardCharset, TabulationClearMode,
17};
18
19/// Drive `parser` over `bytes`, then add the lines that entered the buffer to
20/// `appended` - the lifetime count a read cursor is measured in.
21pub(crate) fn advance<T: EventListener>(
22    parser: &mut Processor,
23    term: &mut Term<T>,
24    bytes: &[u8],
25    history: usize,
26    appended: &mut u64,
27) {
28    let mut counted = Counted::new(term, history);
29    parser.advance(&mut counted, bytes);
30    *appended = appended.saturating_add(counted.finish());
31}
32
33/// One parse's worth of counting.
34struct Counted<'a, T: EventListener> {
35    term: &'a mut Term<T>,
36    /// The buffer's line count when the parse started.
37    total: usize,
38    /// The configured scrollback cap, in lines.
39    history: usize,
40    /// Lines this parse pushed out of a full buffer.
41    scrolled: u64,
42}
43
44impl<'a, T: EventListener> Counted<'a, T> {
45    fn new(term: &'a mut Term<T>, history: usize) -> Self {
46        let total = term.grid().total_lines();
47        Self { term, total, history, scrolled: 0 }
48    }
49
50    /// The lines that entered the buffer: the growth it shows, plus the lines
51    /// it dropped once it was full.
52    fn finish(self) -> u64 {
53        let growth = self.term.grid().total_lines().saturating_sub(self.total);
54        u64::try_from(growth).unwrap_or(u64::MAX).saturating_add(self.scrolled)
55    }
56
57    /// Forward one call, and count it when it scrolled a full screen: the
58    /// cursor line is unchanged only when the linefeed scrolled the region,
59    /// and only a region ending at the screen's last line reaches the history.
60    fn scrolled_linefeed(&mut self, scrolls: impl FnOnce(&mut Term<T>)) {
61        let before = self.term.grid().cursor.point.line;
62        scrolls(self.term);
63        let grid = self.term.grid();
64        let last = Line(i32::try_from(grid.screen_lines().saturating_sub(1)).unwrap_or(i32::MAX));
65        let full = grid.total_lines() >= grid.screen_lines() + self.history;
66        if grid.cursor.point.line == before && before == last && full {
67            self.scrolled = self.scrolled.saturating_add(1);
68        }
69    }
70}
71
72macro_rules! forward {
73    ($($name:ident($($arg:ident: $ty:ty),*);)*) => {
74        $(
75            fn $name(&mut self, $($arg: $ty),*) {
76                self.term.$name($($arg),*);
77            }
78        )*
79    };
80}
81
82impl<T: EventListener> Handler for Counted<'_, T> {
83    fn linefeed(&mut self) {
84        self.scrolled_linefeed(Handler::linefeed);
85    }
86
87    fn newline(&mut self) {
88        self.scrolled_linefeed(Handler::newline);
89    }
90
91    forward!(
92        set_title(a0: Option<String>);
93        set_cursor_style(a0: Option<CursorStyle>);
94        set_cursor_shape(a0: CursorShape);
95        input(a0: char);
96        goto(a0: i32, a1: usize);
97        goto_line(a0: i32);
98        goto_col(a0: usize);
99        insert_blank(a0: usize);
100        move_up(a0: usize);
101        move_down(a0: usize);
102        identify_terminal(a0: Option<char>);
103        device_status(a0: usize);
104        move_forward(a0: usize);
105        move_backward(a0: usize);
106        move_down_and_cr(a0: usize);
107        move_up_and_cr(a0: usize);
108        put_tab(a0: u16);
109        backspace();
110        carriage_return();
111        bell();
112        substitute();
113        set_horizontal_tabstop();
114        scroll_up(a0: usize);
115        scroll_down(a0: usize);
116        insert_blank_lines(a0: usize);
117        delete_lines(a0: usize);
118        erase_chars(a0: usize);
119        delete_chars(a0: usize);
120        move_backward_tabs(a0: u16);
121        move_forward_tabs(a0: u16);
122        save_cursor_position();
123        restore_cursor_position();
124        clear_line(a0: LineClearMode);
125        clear_screen(a0: ClearMode);
126        clear_tabs(a0: TabulationClearMode);
127        set_tabs(a0: u16);
128        reset_state();
129        reverse_index();
130        terminal_attribute(a0: Attr);
131        set_mode(a0: Mode);
132        unset_mode(a0: Mode);
133        report_mode(a0: Mode);
134        set_private_mode(a0: PrivateMode);
135        unset_private_mode(a0: PrivateMode);
136        report_private_mode(a0: PrivateMode);
137        set_scrolling_region(a0: usize, a1: Option<usize>);
138        set_keypad_application_mode();
139        unset_keypad_application_mode();
140        set_active_charset(a0: CharsetIndex);
141        configure_charset(a0: CharsetIndex, a1: StandardCharset);
142        set_color(a0: usize, a1: Rgb);
143        dynamic_color_sequence(a0: String, a1: usize, a2: &str);
144        reset_color(a0: usize);
145        clipboard_store(a0: u8, a1: &[u8]);
146        clipboard_load(a0: u8, a1: &str);
147        decaln();
148        push_title();
149        pop_title();
150        text_area_size_pixels();
151        text_area_size_chars();
152        set_hyperlink(a0: Option<Hyperlink>);
153        report_keyboard_mode();
154        push_keyboard_mode(a0: KeyboardModes);
155        pop_keyboard_modes(a0: u16);
156        set_keyboard_mode(a0: KeyboardModes, a1: KeyboardModesApplyBehavior);
157        set_modify_other_keys(a0: ModifyOtherKeys);
158        report_modify_other_keys();
159        set_scp(a0: ScpCharPath, a1: ScpUpdateMode);
160    );
161}