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