use std::fmt; use sum_tree::SumTree; use super::{BlockItem, LayoutSummary, RenderState}; /// Extension trait for types with verbose descriptive formatting. pub trait Describe { /// Describe this item into the given formatter. fn describe_to(&self, f: &mut fmt::Formatter) -> fmt::Result; /// Describe this item. fn describe(&self) -> Description<'_, Self> { Description(self) } } pub struct Description<'a, T: ?Sized>(&'a T); impl fmt::Display for Description<'_, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.describe_to(f) } } impl Describe for RenderState { fn describe_to(&self, f: &mut fmt::Formatter) -> fmt::Result { let viewport_start = self.viewport.scroll_top().as_f32() as f64; let viewport_end = viewport_start + self.viewport.height().as_f32() as f64; writeln!(f, "Viewport: {viewport_start:.2}px to {viewport_end:.2}px")?; writeln!(f, "Selection: {}", self.selections())?; let mut in_viewport = false; let content = self.content.borrow(); let mut cursor = content.cursor::<(), LayoutSummary>(); cursor.descend_to_first_item(&content, |_| true); while let Some(item) = cursor.item() { let start_summary = cursor.start(); let item_start = start_summary.height; let item_end = item_start + item.height().as_f32() as f64; // Is this the end of the viewport? if item_start > viewport_end && in_viewport { in_viewport = false; writeln!(f, "============> VIEWPORT END <============")?; } // Is this the start of the viewport? if item_end >= viewport_start && item_start <= viewport_end && !in_viewport { in_viewport = true; writeln!(f, "============> VIEWPORT START <============")?; } writeln!( f, "-------- {:.2}px / {} characters --------", start_summary.height, start_summary.content_length )?; writeln!(f, " {}", item.describe())?; cursor.next(); } if in_viewport { writeln!(f, "============> VIEWPORT END <============")?; } Ok(()) } } impl Describe for SumTree { fn describe_to(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut cursor = self.cursor::<(), LayoutSummary>(); cursor.descend_to_first_item(self, |_| true); while let Some(item) = cursor.item() { let summary = cursor.start(); writeln!( f, "-------- {:.2}px / {} characters --------\n{}", summary.height, summary.content_length, item.describe() )?; cursor.next(); } Ok(()) } } impl Describe for BlockItem { fn describe_to(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { BlockItem::Paragraph(_) => f.write_str("Paragraph")?, BlockItem::TextBlock { .. } => f.write_str("Text Block")?, BlockItem::RunnableCodeBlock { code_block_type, .. } => write!(f, "Code Block - {code_block_type}",)?, BlockItem::MermaidDiagram { .. } => f.write_str("Mermaid Diagram")?, BlockItem::TemporaryBlock { .. } => f.write_str("Temporary Paragraph")?, BlockItem::TaskList { indent_level, complete, .. } => write!( f, "Task List @ {indent_level} [{}]", if *complete { "X" } else { " " } )?, BlockItem::UnorderedList { indent_level, .. } => { write!(f, "Unordered List @ {indent_level}")? } BlockItem::OrderedList { indent_level, .. } => { write!(f, "Ordered List @ {indent_level}")? } BlockItem::Header { header_size, .. } => write!(f, "{header_size:?}")?, BlockItem::HorizontalRule(_) => f.write_str("Horizontal Rule")?, BlockItem::Image { alt_text, .. } => write!(f, "Image: {alt_text}")?, BlockItem::Table(laid_out_table) => write!( f, "Table: {}x{}", laid_out_table.table.rows.len() + 1, laid_out_table.table.headers.len() )?, BlockItem::TrailingNewLine(_) => f.write_str("Trailing Newline")?, BlockItem::Embedded(_) => f.write_str("Embedded Item")?, BlockItem::Hidden { .. } => f.write_str("Hidden")?, } write!( f, " ({} characters, {} lines, {:.2}px tall)", self.content_length(), self.lines(), self.height() ) } }