Skip to main content

vt100/
cell.rs

1use unicode_width::UnicodeWidthChar as _;
2
3// chosen to make the size of the cell struct 32 bytes
4const CONTENT_BYTES: usize = 22;
5
6const IS_WIDE: u8 = 0b1000_0000;
7const IS_WIDE_CONTINUATION: u8 = 0b0100_0000;
8const LEN_BITS: u8 = 0b0001_1111;
9
10/// Represents a single terminal cell.
11#[derive(Clone, Debug, Eq)]
12pub struct Cell {
13    contents: [u8; CONTENT_BYTES],
14    len: u8,
15    attrs: crate::attrs::Attrs,
16}
17const _: () = assert!(std::mem::size_of::<Cell>() == 32);
18
19impl PartialEq<Self> for Cell {
20    fn eq(&self, other: &Self) -> bool {
21        if self.len != other.len {
22            return false;
23        }
24        if self.attrs != other.attrs {
25            return false;
26        }
27        let len = self.len();
28        self.contents[..len] == other.contents[..len]
29    }
30}
31
32impl Cell {
33    pub(crate) fn new() -> Self {
34        Self {
35            contents: Default::default(),
36            len: 0,
37            attrs: crate::attrs::Attrs::default(),
38        }
39    }
40
41    fn len(&self) -> usize {
42        usize::from(self.len & LEN_BITS)
43    }
44
45    pub(crate) fn set(&mut self, c: char, a: crate::attrs::Attrs) {
46        self.len = 0;
47        self.append_char(0, c);
48        // strings in this context should always be an arbitrary character
49        // followed by zero or more zero-width characters, so we should only
50        // have to look at the first character
51        self.set_wide(c.width().unwrap_or(1) > 1);
52        self.attrs = a;
53    }
54
55    pub(crate) fn append(&mut self, c: char) {
56        let len = self.len();
57        if len >= CONTENT_BYTES - 4 {
58            return;
59        }
60        if len == 0 {
61            self.contents[0] = b' ';
62            self.len += 1;
63        }
64
65        // we already checked that we have space for another codepoint
66        self.append_char(self.len(), c);
67    }
68
69    // Writes bytes representing c at start
70    // Requires caller to verify start <= CODEPOINTS_IN_CELL * 4
71    fn append_char(&mut self, start: usize, c: char) {
72        c.encode_utf8(&mut self.contents[start..]);
73        self.len += u8::try_from(c.len_utf8()).unwrap();
74    }
75
76    pub(crate) fn clear(&mut self, attrs: crate::attrs::Attrs) {
77        self.len = 0;
78        self.attrs = attrs;
79    }
80
81    /// Returns the text contents of the cell.
82    ///
83    /// Can include multiple unicode characters if combining characters are
84    /// used, but will contain at most one character with a non-zero character
85    /// width.
86    // Since contents has been constructed by appending chars encoded as UTF-8 it will be valid UTF-8
87    #[allow(clippy::missing_panics_doc)]
88    #[must_use]
89    pub fn contents(&self) -> &str {
90        std::str::from_utf8(&self.contents[..self.len()]).unwrap()
91    }
92
93    /// Returns whether the cell contains any text data.
94    #[must_use]
95    pub fn has_contents(&self) -> bool {
96        self.len() > 0
97    }
98
99    /// Returns whether the text data in the cell represents a wide character.
100    #[must_use]
101    pub fn is_wide(&self) -> bool {
102        self.len & IS_WIDE != 0
103    }
104
105    /// Returns whether the cell contains the second half of a wide character
106    /// (in other words, whether the previous cell in the row contains a wide
107    /// character)
108    #[must_use]
109    pub fn is_wide_continuation(&self) -> bool {
110        self.len & IS_WIDE_CONTINUATION != 0
111    }
112
113    fn set_wide(&mut self, wide: bool) {
114        if wide {
115            self.len |= IS_WIDE;
116        } else {
117            self.len &= !IS_WIDE;
118        }
119    }
120
121    pub(crate) fn set_wide_continuation(&mut self, wide: bool) {
122        if wide {
123            self.len |= IS_WIDE_CONTINUATION;
124        } else {
125            self.len &= !IS_WIDE_CONTINUATION;
126        }
127    }
128
129    pub(crate) fn attrs(&self) -> &crate::attrs::Attrs {
130        &self.attrs
131    }
132
133    /// Returns the foreground color of the cell.
134    #[must_use]
135    pub fn fgcolor(&self) -> crate::Color {
136        self.attrs.fgcolor
137    }
138
139    /// Returns the background color of the cell.
140    #[must_use]
141    pub fn bgcolor(&self) -> crate::Color {
142        self.attrs.bgcolor
143    }
144
145    /// Returns whether the cell should be rendered with the bold text
146    /// attribute.
147    #[must_use]
148    pub fn bold(&self) -> bool {
149        self.attrs.bold()
150    }
151
152    /// Returns whether the cell should be rendered with the dim text
153    /// attribute.
154    #[must_use]
155    pub fn dim(&self) -> bool {
156        self.attrs.dim()
157    }
158
159    /// Returns whether the cell should be rendered with the italic text
160    /// attribute.
161    #[must_use]
162    pub fn italic(&self) -> bool {
163        self.attrs.italic()
164    }
165
166    /// Returns whether the cell should be rendered with the underlined text
167    /// attribute.
168    #[must_use]
169    pub fn underline(&self) -> bool {
170        self.attrs.underline()
171    }
172
173    /// Returns whether the cell should be rendered with the inverse text
174    /// attribute.
175    #[must_use]
176    pub fn inverse(&self) -> bool {
177        self.attrs.inverse()
178    }
179}