use std::iter::{empty, once, repeat_n}; use std::pin::pin; use std::vec::Vec; use dsb::Cell; use dsb::cell::Style; use itertools::Itertools; use markdown::mdast::{self, Node}; use ropey::Rope; use serde_derive::{Deserialize, Serialize}; const D: Cell = Cell { letter: None, style: Style::new(FG, BG) }; use crate::{FG, text}; struct Builder { c: usize, to: Vec, scratch: Vec, } enum Action { Put(Vec), Just(Vec), Finish, } impl Builder { fn finish(&mut self) { assert!(self.scratch.len() < self.c); self.to.extend(&self.scratch); self.to.extend(repeat_n(D, self.c - self.scratch.len())); self.scratch.clear(); assert!(self.to.len() % self.c == 0); } fn put_wrapping( &mut self, mut c: impl Iterator, mut l: usize, ) { self.to.extend(self.scratch.drain(..)); let fill = self.c - (self.to.len() % self.c); self.to.extend(c.by_ref().take(fill)); l -= fill; let n = l - (l % self.c); self.to.extend(c.by_ref().take(n)); c.collect_into(&mut self.scratch); assert!(self.to.len() % self.c == 0); assert!(self.scratch.len() < self.c); } fn put(&mut self, c: impl Iterator + Clone) { let mut c = c.map(|x| match x.letter { Some('\n') => x.style.empty(), _ => x, }); assert!(self.scratch.len() < self.c); loop { let n = c .clone() .take_while_inclusive(|x| { !x.letter.is_none_or(char::is_whitespace) }) .count(); let next = c.by_ref().take_while_inclusive(|x| { !x.letter.is_none_or(char::is_whitespace) }); if n == 0 { break; } if n + self.scratch.len() < self.c { self.scratch.extend(next); } else if n < self.c { self.finish(); self.scratch.extend(next); } else { self.put_wrapping(next.into_iter(), n); } } // assert!(self.scratch.len() == 0); // } assert!(self.scratch.len() < self.c); // self.scratch.extend(after); // self.scratch.extend(c); assert!(self.to.len() % self.c == 0); assert!(self.scratch.len() < self.c); } fn extend_( &self, n: &mdast::Node, i: impl IntoIterator, inherit: Style, ) -> Vec { n.children() .iter() .flat_map(|i| *i) .flat_map(|x| self.run(x, inherit)) .chain(i) .collect() } fn extend(&self, n: &Node, inherit: Style) -> Vec { self.extend_(n, empty(), inherit) } fn run(&self, node: &mdast::Node, mut inherit: Style) -> Vec { match node { Node::Break(_) => vec![Action::Finish], Node::InlineCode(x) => { inherit.bg = [21, 24, 30]; vec![Action::Put( x.value.chars().map(|x| inherit.basic(x)).collect(), )] } // Node::Html(Html { value: "code", .. }) => {} Node::Delete(_) => todo!(), Node::Emphasis(_) => { inherit.flags |= Style::ITALIC; self.extend(node, inherit | Style::ITALIC) } Node::Link(_) => { inherit.flags |= Style::UNDERLINE; inherit.fg = [244, 196, 98]; self.extend(node, inherit) } Node::LinkReference(_) => { inherit.flags |= Style::UNDERLINE; inherit.fg = [244, 196, 98]; self.extend(node, inherit) } Node::Heading(_) => { inherit.flags |= Style::BOLD; inherit.fg = [255, 255, 255]; self.extend_(node, [Action::Finish], inherit) } Node::Strong(_) => self.extend(node, inherit | Style::BOLD), Node::Text(text) => vec![Action::Put( text.value .chars() .map(|x| if x == '\n' { ' ' } else { x }) .map(|x| inherit.basic(x)) .collect(), )], Node::Code(code) => { let r = Rope::from_str(&code.value); let mut cell = vec![D; self.c * r.len_lines()]; for (l, y) in r.lines().zip(0..) { for (e, x) in l.chars().take(self.c).zip(0..) { if e != '\n' { cell[y * self.c + x].letter = Some(e); } } } for ((x1, y1), (x2, y2), s, txt) in std::iter::from_coroutine(pin!(text::hl( text::LOADER .language_for_name( code.lang.as_deref().unwrap_or("rust") ) .unwrap_or( text::LOADER .language_for_name("rust") .unwrap() ), &r, .., self.c, ))) { cell.get_mut(y1 * self.c + x1..y2 * self.c + x2).map( |x| { x.iter_mut().zip(txt.chars()).for_each( |(x, c)| { x.style |= s; x.letter = Some(c); }, ) }, ); } assert_eq!(self.to.len() % self.c, 0); vec![Action::Finish, Action::Just(cell)] } Node::ThematicBreak(_) => { vec![Action::Finish, Action::Finish] } Node::Paragraph(paragraph) => paragraph .children .iter() .flat_map(|c| self.run(&c, inherit)) .chain([Action::Finish, Action::Finish]) .collect(), n => self.extend(n, inherit), } } } pub fn p(x: &str) -> Option { markdown::to_mdast(x, &markdown::ParseOptions::gfm()).ok() } fn len(node: &mdast::Node) -> Vec { match node { Node::Break(_) => vec![usize::MAX], Node::InlineCode(x) => vec![x.value.chars().count()], Node::Code(x) => once(usize::MAX) .chain( Iterator::intersperse( x.value.lines().map(|l| l.chars().count()), usize::MAX, ) .chain([usize::MAX]), ) .collect(), // Node::Html(Html { value: "code", .. }) => {} Node::Text(text) => vec![text.value.chars().count()], Node::Paragraph(x) => x.children.iter().flat_map(len).chain([usize::MAX]).collect(), n => n.children().iter().flat_map(|x| *x).flat_map(len).collect(), } } pub fn l(node: &Node) -> Vec { len(node) .into_iter() .chunk_by(|&x| x != usize::MAX) .into_iter() .filter_map(|x| x.0.then(|| x.1.sum::())) .collect::>() } #[implicit_fn::implicit_fn] pub fn markdown2(c: usize, x: &Node) -> Vec { let mut r = Builder { c, to: vec![], scratch: vec![] }; for (is_put, act) in r .run(&x, Style::new(FG, BG)) .into_iter() .chunk_by(|x| matches!(x, Action::Put(_))) .into_iter() { if is_put { r.put( act.flat_map(|x| match x { Action::Put(x) => x, _ => panic!(), }) .collect::>() .into_iter(), ); } else { for act in act { match act { Action::Put(cells) => r.put(cells.into_iter()), Action::Just(cells) => r.to.extend(cells), Action::Finish => r.finish(), } } } } if r.to.iter().take(c).all(_.letter.is_none()) { r.to.drain(..c); } if r.to.iter().rev().take(c).all(_.letter.is_none()) { r.to.drain(r.to.len() - c..); } r.to } pub const BG: [u8; 3] = text::col!("#191E27"); #[test] fn t() { use std::time::Instant; use dsb::F; use fimg::Image; let ppem = 18.0; let lh = 10.0; let (w, h) = (400, 8000); let (c, r) = dsb::fit(&crate::FONT, ppem, lh, (w, h)); let cells = markdown2(c, &p(include_str!("vec.md")).unwrap()); dbg!(l(&p(include_str!("vec.md")).unwrap())); dbg!(cells.len() / c); dbg!(w, h); dbg!(c, r); let mut fonts = dsb::Fonts::new( F::FontRef(*crate::FONT, &[(2003265652, 550.0)]), F::FontRef(*crate::BFONT, &[]), F::FontRef(*crate::IFONT, &[(2003265652, 550.0)]), F::FontRef(*crate::IFONT, &[]), ); let now = Instant::now(); let mut x = Image::build(w as _, h as _).fill(BG); unsafe { dsb::render( &cells, (c, r), ppem, &mut fonts, lh, true, x.as_mut(), (0, 0), ) }; println!("{:?}", now.elapsed()); x.as_ref().save("x"); } #[derive(Debug, Serialize, Deserialize)] pub struct Hovr { pub(crate) span: Option<[(VisualX, usize); 2]>, pub(crate) item: crate::text::CellBuffer, #[serde(skip)] pub(crate) range: Option, } pub type VisualX = usize;