terminal_emulator/term/
cell.rs1use bitflags::bitflags;
15
16use crate::ansi::{Color, NamedColor};
17use crate::grid;
18use crate::index::Column;
19
20pub 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
58pub trait LineLength {
60 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 *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}