use alloc::string::String;
use alloc::{vec, vec::Vec};
use core::fmt::{self, Write};
use crate::Level;
use crate::renderer::ElementStyle;
use crate::renderer::stylesheet::Stylesheet;
#[derive(Debug)]
pub(crate) struct StyledBuffer {
lines: Vec<Vec<StyledChar>>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct StyledChar {
ch: char,
style: ElementStyle,
}
impl StyledChar {
pub(crate) const SPACE: Self = StyledChar::new(' ', ElementStyle::NoStyle);
pub(crate) const fn new(ch: char, style: ElementStyle) -> StyledChar {
StyledChar { ch, style }
}
}
impl StyledBuffer {
pub(crate) fn new() -> StyledBuffer {
StyledBuffer { lines: vec![] }
}
fn ensure_lines(&mut self, line: usize) {
if line >= self.lines.len() {
self.lines.resize(line + 1, Vec::new());
}
}
pub(crate) fn render(
&self,
level: &Level<'_>,
stylesheet: &Stylesheet,
str: &mut String,
) -> Result<(), fmt::Error> {
for (i, line) in self.lines.iter().enumerate() {
let mut current_style = stylesheet.none;
for StyledChar { ch, style } in line {
let ch_style = style.color_spec(level, stylesheet);
if ch_style != current_style {
if !line.is_empty() {
write!(str, "{current_style:#}")?;
}
current_style = ch_style;
write!(str, "{current_style}")?;
}
write!(str, "{ch}")?;
}
write!(str, "{current_style:#}")?;
if i != self.lines.len() - 1 {
writeln!(str)?;
}
}
Ok(())
}
pub(crate) fn putc(&mut self, line: usize, col: usize, chr: char, style: ElementStyle) {
self.ensure_lines(line);
if col >= self.lines[line].len() {
self.lines[line].resize(col + 1, StyledChar::SPACE);
}
self.lines[line][col] = StyledChar::new(chr, style);
}
pub(crate) fn puts(&mut self, line: usize, col: usize, string: &str, style: ElementStyle) {
for (offset, c) in string.chars().enumerate() {
self.putc(line, col + offset, c, style);
}
}
pub(crate) fn append(&mut self, line: usize, string: &str, style: ElementStyle) {
if line >= self.lines.len() {
self.puts(line, 0, string, style);
} else {
let col = self.lines[line].len();
self.puts(line, col, string, style);
}
}
pub(crate) fn replace(&mut self, line: usize, start: usize, end: usize, string: &str) {
if start == end {
return;
}
if start > self.lines[line].len() || end > self.lines[line].len() {
return;
};
self.lines[line].splice(
start..end,
string
.chars()
.map(|c| StyledChar::new(c, ElementStyle::LineNumber)),
);
}
pub(crate) fn prepend(&mut self, line: usize, string: &str, style: ElementStyle) {
self.ensure_lines(line);
let string_len = string.chars().count();
if !self.lines[line].is_empty() {
for _ in 0..string_len {
self.lines[line].insert(0, StyledChar::SPACE);
}
}
self.puts(line, 0, string, style);
}
pub(crate) fn num_lines(&self) -> usize {
self.lines.len()
}
pub(crate) fn set_style_range(
&mut self,
line: usize,
col_start: usize,
col_end: usize,
style: ElementStyle,
overwrite: bool,
) {
for col in col_start..col_end {
self.set_style(line, col, style, overwrite);
}
}
pub(crate) fn set_style(
&mut self,
line: usize,
col: usize,
style: ElementStyle,
overwrite: bool,
) {
if let Some(ref mut line) = self.lines.get_mut(line) {
if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
if overwrite || matches!(s, ElementStyle::NoStyle | ElementStyle::Quotation) {
*s = style;
}
}
}
}
}