use crate::cell::{Cell, CellAttributes};
use crate::surface::line::CellRef;
use finl_unicode::grapheme_clusters::Graphemes;
use fixedbitset::FixedBitSet;
#[cfg(feature = "use_serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::convert::TryInto;
use std::num::NonZeroU8;
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
struct Cluster {
cell_width: u16,
attrs: CellAttributes,
}
#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct ClusteredLine {
pub text: String,
#[cfg_attr(
feature = "use_serde",
serde(
deserialize_with = "deserialize_bitset",
serialize_with = "serialize_bitset"
)
)]
is_double_wide: Option<Box<FixedBitSet>>,
clusters: Vec<Cluster>,
len: u32,
last_cell_width: Option<NonZeroU8>,
}
#[cfg(feature = "use_serde")]
fn deserialize_bitset<'de, D>(deserializer: D) -> Result<Option<Box<FixedBitSet>>, D::Error>
where
D: Deserializer<'de>,
{
let wide_indices = <Vec<usize>>::deserialize(deserializer)?;
if wide_indices.is_empty() {
Ok(None)
} else {
let max_idx = wide_indices.iter().max().unwrap_or(&1);
let mut bitset = FixedBitSet::with_capacity(max_idx + 1);
for idx in wide_indices {
bitset.set(idx, true);
}
Ok(Some(Box::new(bitset)))
}
}
#[cfg(feature = "use_serde")]
fn serialize_bitset<S>(value: &Option<Box<FixedBitSet>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut wide_indices: Vec<usize> = vec![];
if let Some(bits) = value {
for idx in bits.ones() {
wide_indices.push(idx);
}
}
wide_indices.serialize(serializer)
}
impl ClusteredLine {
pub fn new() -> Self {
Self {
text: String::with_capacity(80),
is_double_wide: None,
clusters: vec![],
len: 0,
last_cell_width: None,
}
}
pub fn to_cell_vec(&self) -> Vec<Cell> {
let mut cells = vec![];
for c in self.iter() {
cells.push(c.as_cell());
for _ in 1..c.width() {
cells.push(Cell::blank_with_attrs(c.attrs().clone()));
}
}
cells
}
pub fn from_cell_vec<'a>(hint: usize, iter: impl Iterator<Item = CellRef<'a>>) -> Self {
let mut last_cluster: Option<Cluster> = None;
let mut is_double_wide = FixedBitSet::with_capacity(hint);
let mut text = String::new();
let mut clusters = vec![];
let mut any_double = false;
let mut len = 0;
let mut last_cell_width = None;
for cell in iter {
len += cell.width();
last_cell_width = NonZeroU8::new(1);
if cell.width() > 1 {
any_double = true;
is_double_wide.set(cell.cell_index(), true);
}
text.push_str(cell.str());
last_cluster = match last_cluster.take() {
None => Some(Cluster {
cell_width: cell.width() as u16,
attrs: cell.attrs().clone(),
}),
Some(cluster) if cluster.attrs != *cell.attrs() => {
clusters.push(cluster);
Some(Cluster {
cell_width: cell.width() as u16,
attrs: cell.attrs().clone(),
})
}
Some(mut cluster) => {
cluster.cell_width += cell.width() as u16;
Some(cluster)
}
};
}
if let Some(cluster) = last_cluster.take() {
clusters.push(cluster);
}
Self {
text,
is_double_wide: if any_double {
Some(Box::new(is_double_wide))
} else {
None
},
clusters,
len: len.try_into().unwrap(),
last_cell_width,
}
}
pub fn len(&self) -> usize {
self.len as usize
}
fn is_double_wide(&self, cell_index: usize) -> bool {
match &self.is_double_wide {
Some(bitset) => bitset.contains(cell_index),
None => false,
}
}
pub fn iter(&self) -> ClusterLineCellIter {
let mut clusters = self.clusters.iter();
let cluster = clusters.next();
ClusterLineCellIter {
graphemes: Graphemes::new(&self.text),
clusters,
cluster,
idx: 0,
cluster_total: 0,
line: self,
}
}
pub fn append_grapheme(&mut self, text: &str, cell_width: usize, attrs: CellAttributes) {
let cell_width = cell_width as u16;
let new_cluster = match self.clusters.last() {
Some(cluster) => {
if cluster.attrs != attrs {
true
} else {
let (_, did_overflow) = cluster.cell_width.overflowing_add(cell_width);
did_overflow
}
}
None => true,
};
let new_cell_index = self.len as usize;
if new_cluster {
self.clusters.push(Cluster { attrs, cell_width });
} else if let Some(cluster) = self.clusters.last_mut() {
cluster.cell_width += cell_width;
}
self.text.push_str(text);
if cell_width > 1 {
let bitset = match self.is_double_wide.take() {
Some(mut bitset) => {
bitset.grow(new_cell_index + 1);
bitset.set(new_cell_index, true);
bitset
}
None => {
let mut bitset = FixedBitSet::with_capacity(new_cell_index + 1);
bitset.set(new_cell_index, true);
Box::new(bitset)
}
};
self.is_double_wide.replace(bitset);
}
self.last_cell_width = NonZeroU8::new(cell_width as u8);
self.len += cell_width as u32;
}
pub fn append(&mut self, cell: Cell) {
let cell_width = cell.width() as u16;
let new_cluster = match self.clusters.last() {
Some(cluster) => {
if cluster.attrs != *cell.attrs() {
true
} else {
let (_, did_overflow) = cluster.cell_width.overflowing_add(cell_width);
did_overflow
}
}
None => true,
};
let new_cell_index = self.len as usize;
if new_cluster {
self.clusters.push(Cluster {
attrs: (*cell.attrs()).clone(),
cell_width,
});
} else if let Some(cluster) = self.clusters.last_mut() {
cluster.cell_width += cell_width;
}
self.text.push_str(cell.str());
if cell_width > 1 {
let bitset = match self.is_double_wide.take() {
Some(mut bitset) => {
bitset.grow(new_cell_index + 1);
bitset.set(new_cell_index, true);
bitset
}
None => {
let mut bitset = FixedBitSet::with_capacity(new_cell_index + 1);
bitset.set(new_cell_index, true);
Box::new(bitset)
}
};
self.is_double_wide.replace(bitset);
}
self.last_cell_width = NonZeroU8::new(cell_width as u8);
self.len += cell_width as u32;
}
pub fn prune_trailing_blanks(&mut self) -> bool {
let num_spaces = self.text.chars().rev().take_while(|&c| c == ' ').count();
if num_spaces == 0 {
return false;
}
let blank = CellAttributes::blank();
let mut pruned = false;
for _ in 0..num_spaces {
let mut need_pop = false;
if let Some(cluster) = self.clusters.last_mut() {
if cluster.attrs != blank {
break;
}
cluster.cell_width -= 1;
self.text.pop();
self.len -= 1;
self.last_cell_width.take();
pruned = true;
if cluster.cell_width == 0 {
need_pop = true;
}
}
if need_pop {
self.clusters.pop();
}
}
pruned
}
fn compute_last_cell_width(&mut self) -> Option<NonZeroU8> {
if self.last_cell_width.is_none() {
if let Some(last_cell) = self.iter().last() {
self.last_cell_width = NonZeroU8::new(last_cell.width() as u8);
}
}
self.last_cell_width
}
pub fn set_last_cell_was_wrapped(&mut self, wrapped: bool) {
if let Some(width) = self.compute_last_cell_width() {
let width = width.get() as u16;
if let Some(last_cluster) = self.clusters.last_mut() {
let mut attrs = last_cluster.attrs.clone();
attrs.set_wrapped(wrapped);
if last_cluster.cell_width == width {
last_cluster.attrs = attrs;
} else {
last_cluster.cell_width -= width;
self.clusters.push(Cluster {
cell_width: width,
attrs,
});
}
}
}
}
}
pub(crate) struct ClusterLineCellIter<'a> {
graphemes: Graphemes<'a>,
clusters: std::slice::Iter<'a, Cluster>,
cluster: Option<&'a Cluster>,
idx: usize,
cluster_total: usize,
line: &'a ClusteredLine,
}
impl<'a> Iterator for ClusterLineCellIter<'a> {
type Item = CellRef<'a>;
fn next(&mut self) -> Option<CellRef<'a>> {
let text = self.graphemes.next()?;
let cell_index = self.idx;
let width = if self.line.is_double_wide(cell_index) {
2
} else {
1
};
self.idx += width;
self.cluster_total += width;
let attrs = &self.cluster.as_ref()?.attrs;
if self.cluster_total >= self.cluster.as_ref()?.cell_width as usize {
self.cluster = self.clusters.next();
self.cluster_total = 0;
}
Some(CellRef::ClusterRef {
cell_index,
width,
text,
attrs,
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[cfg(target_pointer_width = "64")]
fn memory_usage() {
assert_eq!(std::mem::size_of::<ClusteredLine>(), 64);
assert_eq!(std::mem::size_of::<String>(), 24);
assert_eq!(std::mem::size_of::<Vec<Cluster>>(), 24);
assert_eq!(std::mem::size_of::<Option<Box<FixedBitSet>>>(), 8);
assert_eq!(std::mem::size_of::<Option<NonZeroU8>>(), 1);
}
}