use super::width::ch_width;
use crossterm::style::Color as CtColor;
pub(super) const RESET: &str = "\x1b[0m";
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub(super) struct Style {
pub bold: bool,
pub italic: bool,
pub underline: bool,
pub strike: bool,
pub code: bool,
pub color: Option<CtColor>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(super) struct Cell {
pub ch: char,
pub style: Style,
}
pub(super) fn sgr_fg(c: CtColor) -> String {
match c {
CtColor::Rgb { r, g, b } => format!("\x1b[38;2;{r};{g};{b}m"),
_ => String::new(),
}
}
pub(super) fn open(style: Style) -> String {
let mut s = String::new();
if style.bold {
s.push_str("\x1b[1m");
}
if style.italic {
s.push_str("\x1b[3m");
}
if style.underline {
s.push_str("\x1b[4m");
}
if style.strike {
s.push_str("\x1b[9m");
}
let color = if style.code {
Some(super::emitter::FADE)
} else {
style.color
};
if let Some(c) = color {
s.push_str(&sgr_fg(c));
}
s
}
pub(super) fn render_cells(cells: &[Cell]) -> String {
let mut out = String::new();
let mut i = 0;
while i < cells.len() {
let style = cells[i].style;
let mut text = String::new();
while i < cells.len() && cells[i].style == style {
text.push(cells[i].ch);
i += 1;
}
let o = open(style);
if o.is_empty() {
out.push_str(&text);
} else {
out.push_str(&o);
out.push_str(&text);
out.push_str(RESET);
}
}
out
}
fn words(cells: &[Cell]) -> Vec<Vec<Cell>> {
let mut words: Vec<Vec<Cell>> = Vec::new();
let mut cur: Vec<Cell> = Vec::new();
for &c in cells {
if c.ch == ' ' {
if !cur.is_empty() {
words.push(std::mem::take(&mut cur));
}
} else {
cur.push(c);
}
}
if !cur.is_empty() {
words.push(cur);
}
words
}
pub(super) fn wrap_cells(cells: &[Cell], budget: usize) -> Vec<Vec<Cell>> {
let budget = budget.max(1);
let words = words(cells);
if words.is_empty() {
return vec![Vec::new()];
}
let space = Cell {
ch: ' ',
style: Style::default(),
};
let mut lines: Vec<Vec<Cell>> = Vec::new();
let mut cur: Vec<Cell> = Vec::new();
let mut cur_w = 0usize;
for w in words {
let ww: usize = w.iter().map(|c| ch_width(c.ch)).sum();
if cur.is_empty() {
cur = w;
cur_w = ww;
} else if cur_w + 1 + ww <= budget {
cur.push(space);
cur.extend(w);
cur_w += 1 + ww;
} else {
lines.push(std::mem::take(&mut cur));
cur = w;
cur_w = ww;
}
}
if !cur.is_empty() {
lines.push(cur);
}
if lines.is_empty() {
lines.push(Vec::new());
}
lines
}