use crate::fragment::CellText;
use crate::Settings;
use crate::{
buffer::{fragment_buffer::FragmentTree, Fragment, StringBuffer},
util::parser,
};
pub use cell::{Cell, CellGrid};
pub use contacts::Contacts;
use itertools::Itertools;
use sauron::{
html,
html::{attributes::*, *},
svg::{attributes::*, *},
Node,
};
pub use span::Span;
use std::{
collections::BTreeMap,
fmt,
ops::{Deref, DerefMut},
};
use unicode_width::UnicodeWidthStr;
mod cell;
mod contacts;
mod endorse;
mod span;
/// The simplest buffer.
/// This is maps which char belong to which cell skipping the whitespaces
#[derive(Debug)]
pub struct CellBuffer {
map: BTreeMap<Cell, char>,
/// class, <style>
/// assemble into
///
/// ```css
/// .class { styles }
/// ```
css_styles: Vec<(String, String)>,
escaped_text: Vec<(Cell, String)>,
}
impl Deref for CellBuffer {
type Target = BTreeMap<Cell, char>;
fn deref(&self) -> &Self::Target {
&self.map
}
}
impl DerefMut for CellBuffer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.map
}
}
impl CellBuffer {
pub fn new() -> Self {
CellBuffer {
map: BTreeMap::new(),
css_styles: vec![],
escaped_text: vec![],
}
}
pub fn add_css_styles(&mut self, css_styles: Vec<(String, String)>) {
self.css_styles.extend(css_styles);
}
/// Groups cell that are adjacents (cells that are next to each other, horizontally or
/// vertically)
/// Note: using .rev() since this has a high change that the last cell is adjacent with the
/// current cell tested
pub fn group_adjacents(&self) -> Vec<Span> {
let mut adjacents: Vec<Span> = vec![];
for (cell, ch) in self.iter() {
let belongs_to_adjacents =
adjacents.iter_mut().rev().any(|contacts| {
if contacts.is_adjacent(cell) {
contacts.push((*cell, *ch));
true
} else {
false
}
});
if !belongs_to_adjacents {
adjacents.push(Span::new(*cell, *ch));
}
}
Self::merge_recursive(adjacents)
}
/// merge span recursively until it hasn't changed the number of spans
fn merge_recursive(adjacents: Vec<Span>) -> Vec<Span> {
let original_len = adjacents.len();
let merged = Self::second_pass_merge(adjacents);
// if has merged continue merging until nothing can be merged
if merged.len() < original_len {
Self::merge_recursive(merged)
} else {
merged
}
}
/// second pass merge is operating on span comparing to other spans
fn second_pass_merge(adjacents: Vec<Span>) -> Vec<Span> {
let mut new_groups: Vec<Span> = vec![];
for span in adjacents.into_iter() {
let is_merged = new_groups.iter_mut().rev().any(|new_group| {
if new_group.can_merge(&span) {
new_group.merge(&span);
true
} else {
false
}
});
if !is_merged {
new_groups.push(span);
}
}
new_groups
}
pub fn bounds(&self) -> Option<(Cell, Cell)> {
let xlimits =
self.iter().map(|(cell, _)| cell.x).minmax().into_option();
let ylimits =
self.iter().map(|(cell, _)| cell.y).minmax().into_option();
match (xlimits, ylimits) {
(Some((min_x, max_x)), Some((min_y, max_y))) => {
Some((Cell::new(min_x, min_y), Cell::new(max_x, max_y)))
}
_ => None,
}
}
/// get the svg node of this cell buffer, using the default settings for the sizes
pub fn get_node<MSG>(&self) -> Node<MSG> {
let (node, _w, _h) = self.get_node_with_size(&Settings::default());
node
}
/// calculate the appropriate size (w,h) in pixels for the whole cell buffer to fit
/// appropriately
pub(crate) fn get_size(&self, settings: &Settings) -> (f32, f32) {
let (_top_left, bottom_right) =
self.bounds().unwrap_or((Cell::new(0, 0), Cell::new(0, 0)));
let w = settings.scale * (bottom_right.x + 2) as f32 * Cell::width();
let h = settings.scale * (bottom_right.y + 2) as f32 * Cell::height();
(w, h)
}
/// get all nodes of this cell buffer
pub fn get_node_with_size<MSG>(
&self,
settings: &Settings,
) -> (