1 use std::boxed::Box;
2 
3 use bitflags::bitflags;
4 use serde::{Deserialize, Serialize};
5 
6 use crate::ansi::{Color, NamedColor};
7 use crate::grid::{self, GridCell};
8 use crate::index::Column;
9 
10 bitflags! {
11     #[derive(Serialize, Deserialize)]
12     pub struct Flags: u16 {
13         const INVERSE                   = 0b0000_0000_0000_0001;
14         const BOLD                      = 0b0000_0000_0000_0010;
15         const ITALIC                    = 0b0000_0000_0000_0100;
16         const BOLD_ITALIC               = 0b0000_0000_0000_0110;
17         const UNDERLINE                 = 0b0000_0000_0000_1000;
18         const WRAPLINE                  = 0b0000_0000_0001_0000;
19         const WIDE_CHAR                 = 0b0000_0000_0010_0000;
20         const WIDE_CHAR_SPACER          = 0b0000_0000_0100_0000;
21         const DIM                       = 0b0000_0000_1000_0000;
22         const DIM_BOLD                  = 0b0000_0000_1000_0010;
23         const HIDDEN                    = 0b0000_0001_0000_0000;
24         const STRIKEOUT                 = 0b0000_0010_0000_0000;
25         const LEADING_WIDE_CHAR_SPACER  = 0b0000_0100_0000_0000;
26         const DOUBLE_UNDERLINE          = 0b0000_1000_0000_0000;
27     }
28 }
29 
30 /// Trait for determining if a reset should be performed.
31 pub trait ResetDiscriminant<T> {
32     /// Value based on which equality for the reset will be determined.
33     fn discriminant(&self) -> T;
34 }
35 
36 impl<T: Copy> ResetDiscriminant<T> for T {
37     fn discriminant(&self) -> T {
38         *self
39     }
40 }
41 
42 impl ResetDiscriminant<Color> for Cell {
43     fn discriminant(&self) -> Color {
44         self.bg
45     }
46 }
47 
48 /// Dynamically allocated cell content.
49 ///
50 /// This storage is reserved for cell attributes which are rarely set. This allows reducing the
51 /// allocation required ahead of time for every cell, with some additional overhead when the extra
52 /// storage is actually required.
53 #[derive(Serialize, Deserialize, Default, Debug, Clone, Eq, PartialEq)]
54 struct CellExtra {
55     zerowidth: Vec<char>,
56 }
57 
58 /// Content and attributes of a single cell in the terminal grid.
59 #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
60 pub struct Cell {
61     pub c: char,
62     pub fg: Color,
63     pub bg: Color,
64     pub flags: Flags,
65     #[serde(default)]
66     extra: Option<Box<CellExtra>>,
67 }
68 
69 impl Default for Cell {
70     #[inline]
71     fn default() -> Cell {
72         Cell {
73             c: ' ',
74             bg: Color::Named(NamedColor::Background),
75             fg: Color::Named(NamedColor::Foreground),
76             flags: Flags::empty(),
77             extra: None,
78         }
79     }
80 }
81 
82 impl Cell {
83     /// Zerowidth characters stored in this cell.
84     #[inline]
85     pub fn zerowidth(&self) -> Option<&[char]> {
86         self.extra.as_ref().map(|extra| extra.zerowidth.as_slice())
87     }
88 
89     /// Write a new zerowidth character to this cell.
90     #[inline]
91     pub fn push_zerowidth(&mut self, c: char) {
92         self.extra.get_or_insert_with(Default::default).zerowidth.push(c);
93     }
94 
95     /// Free all dynamically allocated cell storage.
96     #[inline]
97     pub fn drop_extra(&mut self) {
98         if self.extra.is_some() {
99             self.extra = None;
100         }
101     }
102 
103     /// Remove all wide char data from a cell.
104     #[inline(never)]
105     pub fn clear_wide(&mut self) {
106         self.flags.remove(Flags::WIDE_CHAR);
107         self.drop_extra();
108         self.c = ' ';
109     }
110 }
111 
112 impl GridCell for Cell {
113     #[inline]
114     fn is_empty(&self) -> bool {
115         (self.c == ' ' || self.c == '\t')
116             && self.bg == Color::Named(NamedColor::Background)
117             && self.fg == Color::Named(NamedColor::Foreground)
118             && !self.flags.intersects(
119                 Flags::INVERSE
120                     | Flags::UNDERLINE
121                     | Flags::DOUBLE_UNDERLINE
122                     | Flags::STRIKEOUT
123                     | Flags::WRAPLINE
124                     | Flags::WIDE_CHAR_SPACER
125                     | Flags::LEADING_WIDE_CHAR_SPACER,
126             )
127             && self.extra.as_ref().map(|extra| extra.zerowidth.is_empty()) != Some(false)
128     }
129 
130     #[inline]
131     fn flags(&self) -> &Flags {
132         &self.flags
133     }
134 
135     #[inline]
136     fn flags_mut(&mut self) -> &mut Flags {
137         &mut self.flags
138     }
139 
140     #[inline]
141     fn reset(&mut self, template: &Self) {
142         *self = Cell { bg: template.bg, ..Cell::default() };
143     }
144 }
145 
146 impl From<Color> for Cell {
147     #[inline]
148     fn from(color: Color) -> Self {
149         Self { bg: color, ..Cell::default() }
150     }
151 }
152 
153 /// Get the length of occupied cells in a line.
154 pub trait LineLength {
155     /// Calculate the occupied line length.
156     fn line_length(&self) -> Column;
157 }
158 
159 impl LineLength for grid::Row<Cell> {
160     fn line_length(&self) -> Column {
161         let mut length = Column(0);
162 
163         if self[Column(self.len() - 1)].flags.contains(Flags::WRAPLINE) {
164             return Column(self.len());
165         }
166 
167         for (index, cell) in self[..].iter().rev().enumerate() {
168             if cell.c != ' '
169                 || cell.extra.as_ref().map(|extra| extra.zerowidth.is_empty()) == Some(false)
170             {
171                 length = Column(self.len() - index);
172                 break;
173             }
174         }
175 
176         length
177     }
178 }
179 
180 #[cfg(test)]
181 mod tests {
182     use super::{Cell, LineLength};
183 
184     use crate::grid::Row;
185     use crate::index::Column;
186 
187     #[test]
188     fn line_length_works() {
189         let mut row = Row::<Cell>::new(10);
190         row[Column(5)].c = 'a';
191 
192         assert_eq!(row.line_length(), Column(6));
193     }
194 
195     #[test]
196     fn line_length_works_with_wrapline() {
197         let mut row = Row::<Cell>::new(10);
198         row[Column(9)].flags.insert(super::Flags::WRAPLINE);
199 
200         assert_eq!(row.line_length(), Column(10));
201     }
202 }
203