use smallvec::{smallvec, SmallVec};
use std::sync::OnceLock;
use unicode_width::UnicodeWidthChar;
use crate::term::{self, AsTermInput};
static EMPTY_CELL: OnceLock<Cell> = OnceLock::new();
pub fn empty() -> &'static Cell {
EMPTY_CELL.get_or_init(|| Cell::empty())
}
#[derive(Clone, Eq, PartialEq)]
pub struct Cell {
grapheme_cluster: SmallVec<[char; 2]>,
width: u8,
empty: bool,
wide_padding: bool,
attrs: term::Attrs,
}
static_assertions::const_assert!(
(std::mem::size_of::<SmallVec<[char; 2]>>() == std::mem::size_of::<SmallVec<[char; 1]>>())
|| std::mem::size_of::<usize>() != 8
);
#[allow(dead_code)]
impl Cell {
pub fn new(c: char, attrs: term::Attrs) -> Self {
let width = match UnicodeWidthChar::width(c) {
None => panic!("control chars cannot create cells"),
Some(0) => panic!("zero width chars cannot create cells"),
Some(w) => w,
};
Cell {
grapheme_cluster: smallvec![c],
width: width as u8,
empty: false,
wide_padding: false,
attrs,
}
}
pub fn empty() -> Self {
Cell {
grapheme_cluster: smallvec![],
width: 0,
empty: true,
wide_padding: false,
attrs: term::Attrs::default(),
}
}
pub fn empty_with_attrs(attrs: term::Attrs) -> Self {
Cell { grapheme_cluster: smallvec![], width: 0, empty: true, wide_padding: false, attrs }
}
pub fn wide_pad() -> Self {
Cell {
grapheme_cluster: smallvec![],
width: 0,
empty: true,
wide_padding: true,
attrs: term::Attrs::default(),
}
}
pub fn add_char(&mut self, c: char) {
assert!(UnicodeWidthChar::width(c).unwrap_or(0) > 0, "non-zero width char added to cell");
self.grapheme_cluster.push(c);
}
pub fn width(&self) -> u8 {
self.width
}
pub fn is_empty(&self) -> bool {
self.empty
}
pub fn attrs(&self) -> &term::Attrs {
&self.attrs
}
}
impl AsTermInput for Cell {
fn term_input_into(&self, buf: &mut Vec<u8>) {
let mut utf8_buf = [0u8; 4];
for c in self.grapheme_cluster.iter() {
let utf8_slice = c.encode_utf8(&mut utf8_buf);
buf.extend(utf8_slice.as_bytes());
}
if self.empty && !self.wide_padding {
assert!(self.grapheme_cluster.is_empty());
buf.push(b' ');
}
}
}
impl std::fmt::Display for Cell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for c in &self.grapheme_cluster {
write!(f, "{}", c)?;
}
if self.wide_padding {
write!(f, "-")?;
} else if self.empty {
write!(f, "*")?;
}
Ok(())
}
}
impl std::fmt::Debug for Cell {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<")?;
for c in &self.grapheme_cluster {
write!(f, "{}", c)?;
}
if self.wide_padding {
write!(f, "-")?;
} else if self.empty {
write!(f, "☐")?;
}
if self.attrs.has_attrs() {
write!(f, "/{}", self.attrs)?;
}
write!(f, ">")?;
Ok(())
}
}