2018-12-10 17:53:56 +00:00
|
|
|
use bitflags::bitflags;
|
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
use crate::ansi::{Color, NamedColor};
|
2019-03-13 18:55:18 +00:00
|
|
|
use crate::grid::{self, GridCell};
|
2018-12-10 17:53:56 +00:00
|
|
|
use crate::index::Column;
|
2016-11-28 22:30:08 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Maximum number of zerowidth characters which will be stored per cell.
|
2018-12-09 15:28:22 +00:00
|
|
|
pub const MAX_ZEROWIDTH_CHARS: usize = 5;
|
|
|
|
|
2016-11-28 22:30:08 +00:00
|
|
|
bitflags! {
|
|
|
|
#[derive(Serialize, Deserialize)]
|
2018-12-09 15:28:22 +00:00
|
|
|
pub struct Flags: u16 {
|
2018-12-22 17:16:54 +00:00
|
|
|
const INVERSE = 0b00_0000_0001;
|
|
|
|
const BOLD = 0b00_0000_0010;
|
|
|
|
const ITALIC = 0b00_0000_0100;
|
2019-09-21 17:54:32 +00:00
|
|
|
const BOLD_ITALIC = 0b00_0000_0110;
|
2018-12-22 17:16:54 +00:00
|
|
|
const UNDERLINE = 0b00_0000_1000;
|
|
|
|
const WRAPLINE = 0b00_0001_0000;
|
|
|
|
const WIDE_CHAR = 0b00_0010_0000;
|
|
|
|
const WIDE_CHAR_SPACER = 0b00_0100_0000;
|
|
|
|
const DIM = 0b00_1000_0000;
|
|
|
|
const DIM_BOLD = 0b00_1000_0010;
|
|
|
|
const HIDDEN = 0b01_0000_0000;
|
|
|
|
const STRIKEOUT = 0b10_0000_0000;
|
2016-11-28 22:30:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-09 15:28:22 +00:00
|
|
|
const fn default_extra() -> [char; MAX_ZEROWIDTH_CHARS] {
|
|
|
|
[' '; MAX_ZEROWIDTH_CHARS]
|
|
|
|
}
|
|
|
|
|
2016-12-30 02:51:17 +00:00
|
|
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
2016-11-28 22:30:08 +00:00
|
|
|
pub struct Cell {
|
|
|
|
pub c: char,
|
|
|
|
pub fg: Color,
|
|
|
|
pub bg: Color,
|
|
|
|
pub flags: Flags,
|
2019-03-30 16:48:36 +00:00
|
|
|
#[serde(default = "default_extra")]
|
2018-12-09 15:28:22 +00:00
|
|
|
pub extra: [char; MAX_ZEROWIDTH_CHARS],
|
2016-11-28 22:30:08 +00:00
|
|
|
}
|
|
|
|
|
2016-12-30 02:43:55 +00:00
|
|
|
impl Default for Cell {
|
|
|
|
fn default() -> Cell {
|
2019-03-30 16:48:36 +00:00
|
|
|
Cell::new(' ', Color::Named(NamedColor::Foreground), Color::Named(NamedColor::Background))
|
2016-12-30 02:43:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-13 18:55:18 +00:00
|
|
|
impl GridCell for Cell {
|
|
|
|
#[inline]
|
|
|
|
fn is_empty(&self) -> bool {
|
|
|
|
(self.c == ' ' || self.c == '\t')
|
|
|
|
&& self.extra[0] == ' '
|
|
|
|
&& self.bg == Color::Named(NamedColor::Background)
|
2019-07-10 21:17:20 +00:00
|
|
|
&& self.fg == Color::Named(NamedColor::Foreground)
|
2020-01-09 23:06:41 +00:00
|
|
|
&& !self.flags.intersects(
|
|
|
|
Flags::INVERSE
|
|
|
|
| Flags::UNDERLINE
|
|
|
|
| Flags::STRIKEOUT
|
|
|
|
| Flags::WRAPLINE
|
|
|
|
| Flags::WIDE_CHAR_SPACER,
|
|
|
|
)
|
2019-03-13 18:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-09 23:06:41 +00:00
|
|
|
fn flags(&self) -> &Flags {
|
|
|
|
&self.flags
|
2019-03-13 18:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-01-09 23:06:41 +00:00
|
|
|
fn flags_mut(&mut self) -> &mut Flags {
|
|
|
|
&mut self.flags
|
2019-03-13 18:55:18 +00:00
|
|
|
}
|
2019-12-09 23:35:13 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn fast_eq(&self, other: Self) -> bool {
|
|
|
|
self.bg == other.bg
|
|
|
|
}
|
2019-03-13 18:55:18 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Get the length of occupied cells in a line.
|
2016-12-27 03:52:37 +00:00
|
|
|
pub trait LineLength {
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Calculate the occupied line length.
|
2016-12-27 03:52:37 +00:00
|
|
|
fn line_length(&self) -> Column;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LineLength for grid::Row<Cell> {
|
|
|
|
fn line_length(&self) -> Column {
|
|
|
|
let mut length = Column(0);
|
|
|
|
|
2017-10-12 01:52:23 +00:00
|
|
|
if self[Column(self.len() - 1)].flags.contains(Flags::WRAPLINE) {
|
2016-12-29 15:43:58 +00:00
|
|
|
return Column(self.len());
|
|
|
|
}
|
|
|
|
|
2016-12-27 03:52:37 +00:00
|
|
|
for (index, cell) in self[..].iter().rev().enumerate() {
|
2018-12-09 15:28:22 +00:00
|
|
|
if cell.c != ' ' || cell.extra[0] != ' ' {
|
2016-12-27 03:52:37 +00:00
|
|
|
length = Column(self.len() - index);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
length
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-28 22:30:08 +00:00
|
|
|
impl Cell {
|
2017-06-23 17:01:53 +00:00
|
|
|
#[inline]
|
2016-11-28 22:30:08 +00:00
|
|
|
pub fn bold(&self) -> bool {
|
2017-10-12 01:52:23 +00:00
|
|
|
self.flags.contains(Flags::BOLD)
|
2016-11-28 22:30:08 +00:00
|
|
|
}
|
|
|
|
|
2017-06-23 17:01:53 +00:00
|
|
|
#[inline]
|
2017-06-19 00:04:16 +00:00
|
|
|
pub fn inverse(&self) -> bool {
|
2017-10-12 01:52:23 +00:00
|
|
|
self.flags.contains(Flags::INVERSE)
|
2017-06-19 00:04:16 +00:00
|
|
|
}
|
|
|
|
|
2017-06-23 17:01:53 +00:00
|
|
|
#[inline]
|
|
|
|
pub fn dim(&self) -> bool {
|
2017-10-12 01:52:23 +00:00
|
|
|
self.flags.contains(Flags::DIM)
|
2017-06-23 17:01:53 +00:00
|
|
|
}
|
|
|
|
|
2016-11-28 22:30:08 +00:00
|
|
|
pub fn new(c: char, fg: Color, bg: Color) -> Cell {
|
2019-03-30 16:48:36 +00:00
|
|
|
Cell { extra: [' '; MAX_ZEROWIDTH_CHARS], c, bg, fg, flags: Flags::empty() }
|
2016-11-28 22:30:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn reset(&mut self, template: &Cell) {
|
2020-05-05 22:50:23 +00:00
|
|
|
// memcpy template to self.
|
2019-11-17 00:04:16 +00:00
|
|
|
*self = Cell { c: template.c, bg: template.bg, ..Cell::default() };
|
2016-11-28 22:30:08 +00:00
|
|
|
}
|
2018-12-09 15:28:22 +00:00
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn chars(&self) -> [char; MAX_ZEROWIDTH_CHARS + 1] {
|
|
|
|
unsafe {
|
2019-09-10 16:08:01 +00:00
|
|
|
let mut chars = [std::mem::MaybeUninit::uninit(); MAX_ZEROWIDTH_CHARS + 1];
|
|
|
|
std::ptr::write(chars[0].as_mut_ptr(), self.c);
|
2018-12-09 15:28:22 +00:00
|
|
|
std::ptr::copy_nonoverlapping(
|
2019-09-10 16:08:01 +00:00
|
|
|
self.extra.as_ptr() as *mut std::mem::MaybeUninit<char>,
|
2018-12-09 15:28:22 +00:00
|
|
|
chars.as_mut_ptr().offset(1),
|
|
|
|
self.extra.len(),
|
|
|
|
);
|
2019-09-10 16:08:01 +00:00
|
|
|
std::mem::transmute(chars)
|
2018-12-09 15:28:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn push_extra(&mut self, c: char) {
|
|
|
|
for elem in self.extra.iter_mut() {
|
|
|
|
if elem == &' ' {
|
|
|
|
*elem = c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-11-28 22:30:08 +00:00
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::{Cell, LineLength};
|
|
|
|
|
2018-12-10 17:53:56 +00:00
|
|
|
use crate::grid::Row;
|
|
|
|
use crate::index::Column;
|
2016-12-27 03:52:37 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn line_length_works() {
|
2016-12-30 02:43:55 +00:00
|
|
|
let template = Cell::default();
|
2020-05-30 20:45:44 +00:00
|
|
|
let mut row = Row::new(Column(10), template);
|
2016-12-27 03:52:37 +00:00
|
|
|
row[Column(5)].c = 'a';
|
|
|
|
|
|
|
|
assert_eq!(row.line_length(), Column(6));
|
|
|
|
}
|
2016-12-29 15:43:58 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn line_length_works_with_wrapline() {
|
2016-12-30 02:43:55 +00:00
|
|
|
let template = Cell::default();
|
2020-05-30 20:45:44 +00:00
|
|
|
let mut row = Row::new(Column(10), template);
|
2017-10-12 01:52:23 +00:00
|
|
|
row[Column(9)].flags.insert(super::Flags::WRAPLINE);
|
2016-12-29 15:43:58 +00:00
|
|
|
|
|
|
|
assert_eq!(row.line_length(), Column(10));
|
|
|
|
}
|
2016-12-27 03:52:37 +00:00
|
|
|
}
|
2016-12-30 02:51:17 +00:00
|
|
|
|
2017-02-03 04:50:48 +00:00
|
|
|
#[cfg(all(test, feature = "bench"))]
|
2016-12-30 02:51:17 +00:00
|
|
|
mod benches {
|
|
|
|
extern crate test;
|
|
|
|
use super::Cell;
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
fn cell_reset(b: &mut test::Bencher) {
|
|
|
|
b.iter(|| {
|
|
|
|
let mut cell = Cell::default();
|
|
|
|
|
|
|
|
for _ in 0..100 {
|
|
|
|
cell.reset(test::black_box(&Cell::default()));
|
|
|
|
}
|
|
|
|
|
|
|
|
test::black_box(cell);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|