terminal_emulator/term/
cell.rs

1// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14use bitflags::bitflags;
15
16use crate::ansi::{Color, NamedColor};
17use crate::grid;
18use crate::index::Column;
19
20// Maximum number of zerowidth characters which will be stored per cell.
21pub const MAX_ZEROWIDTH_CHARS: usize = 5;
22
23bitflags! {
24    pub struct Flags: u16 {
25        const INVERSE           = 0b00_0000_0001;
26        const BOLD              = 0b00_0000_0010;
27        const ITALIC            = 0b00_0000_0100;
28        const UNDERLINE         = 0b00_0000_1000;
29        const WRAPLINE          = 0b00_0001_0000;
30        const WIDE_CHAR         = 0b00_0010_0000;
31        const WIDE_CHAR_SPACER  = 0b00_0100_0000;
32        const DIM               = 0b00_1000_0000;
33        const DIM_BOLD          = 0b00_1000_0010;
34        const HIDDEN            = 0b01_0000_0000;
35        const STRIKEOUT         = 0b10_0000_0000;
36    }
37}
38
39#[derive(Copy, Clone, Debug, Eq, PartialEq)]
40pub struct Cell {
41    pub c: char,
42    pub fg: Color,
43    pub bg: Color,
44    pub flags: Flags,
45    pub extra: [char; MAX_ZEROWIDTH_CHARS],
46}
47
48impl Default for Cell {
49    fn default() -> Cell {
50        Cell::new(
51            ' ',
52            Color::Named(NamedColor::Foreground),
53            Color::Named(NamedColor::Background),
54        )
55    }
56}
57
58/// Get the length of occupied cells in a line
59pub trait LineLength {
60    /// Calculate the occupied line length
61    fn line_length(&self) -> Column;
62}
63
64impl LineLength for grid::Row<Cell> {
65    fn line_length(&self) -> Column {
66        let mut length = Column(0);
67
68        if self[Column(self.len() - 1)].flags.contains(Flags::WRAPLINE) {
69            return Column(self.len());
70        }
71
72        for (index, cell) in self[..].iter().rev().enumerate() {
73            if cell.c != ' ' || cell.extra[0] != ' ' {
74                length = Column(self.len() - index);
75                break;
76            }
77        }
78
79        length
80    }
81}
82
83impl Cell {
84    #[inline]
85    pub fn bold(&self) -> bool {
86        self.flags.contains(Flags::BOLD)
87    }
88
89    #[inline]
90    pub fn inverse(&self) -> bool {
91        self.flags.contains(Flags::INVERSE)
92    }
93
94    #[inline]
95    pub fn dim(&self) -> bool {
96        self.flags.contains(Flags::DIM)
97    }
98
99    pub fn new(c: char, fg: Color, bg: Color) -> Cell {
100        Cell {
101            extra: [' '; MAX_ZEROWIDTH_CHARS],
102            c,
103            bg,
104            fg,
105            flags: Flags::empty(),
106        }
107    }
108
109    #[inline]
110    pub fn is_empty(&self) -> bool {
111        (self.c == ' ' || self.c == '\t')
112            && self.extra[0] == ' '
113            && self.bg == Color::Named(NamedColor::Background)
114            && !self
115                .flags
116                .intersects(Flags::INVERSE | Flags::UNDERLINE | Flags::STRIKEOUT)
117    }
118
119    #[inline]
120    pub fn reset(&mut self, template: &Cell) {
121        // memcpy template to self
122        *self = *template;
123    }
124
125    #[inline]
126    pub fn chars(&self) -> [char; MAX_ZEROWIDTH_CHARS + 1] {
127        unsafe {
128            let mut chars = [std::mem::uninitialized(); MAX_ZEROWIDTH_CHARS + 1];
129            std::ptr::write(&mut chars[0], self.c);
130            std::ptr::copy_nonoverlapping(
131                self.extra.as_ptr(),
132                chars.as_mut_ptr().offset(1),
133                self.extra.len(),
134            );
135            chars
136        }
137    }
138
139    #[inline]
140    pub fn push_extra(&mut self, c: char) {
141        for elem in self.extra.iter_mut() {
142            if elem == &' ' {
143                *elem = c;
144                break;
145            }
146        }
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::{Cell, LineLength};
153
154    use crate::grid::Row;
155    use crate::index::Column;
156
157    #[test]
158    fn line_length_works() {
159        let template = Cell::default();
160        let mut row = Row::new(Column(10), &template);
161        row[Column(5)].c = 'a';
162
163        assert_eq!(row.line_length(), Column(6));
164    }
165
166    #[test]
167    fn line_length_works_with_wrapline() {
168        let template = Cell::default();
169        let mut row = Row::new(Column(10), &template);
170        row[Column(9)].flags.insert(super::Flags::WRAPLINE);
171
172        assert_eq!(row.line_length(), Column(10));
173    }
174}
175
176#[cfg(all(test, feature = "bench"))]
177mod benches {
178    extern crate test;
179    use super::Cell;
180
181    #[bench]
182    fn cell_reset(b: &mut test::Bencher) {
183        b.iter(|| {
184            let mut cell = Cell::default();
185
186            for _ in 0..100 {
187                cell.reset(test::black_box(&Cell::default()));
188            }
189
190            test::black_box(cell);
191        });
192    }
193}