..self
}
}
+
+ /// Should the item be shown?
+ ///
+ /// This always returns true for headings because their contents are always
+ /// shown (although headings can be collapsed in an outline view).
+ pub fn is_shown(&self) -> bool {
+ self.details.kind() == ItemKind::Heading || self.show
+ }
}
impl<T> From<T> for Item
}
}
+/// A group of output items.
+///
+/// The name "heading" is used in SPV files. There is only a visible
+/// heading if the output items inside the heading include a [Text] item.
+/// The grouping itself is only visible in the outline pane in a viewer
+/// window.
#[derive(Clone, Debug, Default, Serialize)]
pub struct Heading(pub Vec<Arc<Item>>);
}
}
+pub struct ItemRefIterator<'a> {
+ next: Option<&'a Item>,
+ stack: Vec<(&'a Item, usize)>,
+}
+
+impl<'a> ItemRefIterator<'a> {
+ pub fn without_hidden(start: &'a Item) -> impl Iterator<Item = &'a Item> {
+ Self::with_hidden(start).filter(|item| item.is_shown())
+ }
+
+ pub fn with_hidden(start: &'a Item) -> Self {
+ Self {
+ next: Some(start),
+ stack: Vec::new(),
+ }
+ }
+}
+
+impl<'a> Iterator for ItemRefIterator<'a> {
+ type Item = &'a Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let cur = self.next.take()?;
+ if let Some(first_child) = cur.details.children().first() {
+ self.next = Some(&*first_child);
+ self.stack.push((cur, 1));
+ } else {
+ while let Some((item, index)) = self.stack.pop() {
+ if let Some(child) = item.details.children().get(index) {
+ self.next = Some(&*child);
+ self.stack.push((item, index + 1));
+ return Some(cur);
+ }
+ }
+ }
+ Some(cur)
+ }
+}
+
pub struct ItemCursor {
cur: Option<Arc<Item>>,
stack: Vec<(Arc<Item>, usize)>,
+ include_hidden: bool,
}
impl ItemCursor {
pub fn new(start: Arc<Item>) -> Self {
+ Self {
+ cur: start.is_shown().then_some(start),
+ stack: Vec::new(),
+ include_hidden: false,
+ }
+ }
+
+ pub fn with_hidden(start: Arc<Item>) -> Self {
Self {
cur: Some(start),
stack: Vec::new(),
+ include_hidden: true,
}
}
use unicode_linebreak::{BreakOpportunity, linebreaks};
use unicode_width::UnicodeWidthStr;
-use crate::output::{render::Extreme, table::DrawCell};
+use crate::output::{ItemRefIterator, render::Extreme, table::DrawCell};
use crate::output::{
Details, Item,
where
W: FmtWrite,
{
- match &item.details {
- Details::Chart | Details::Image(_) => todo!(),
- Details::Heading(children) => {
- for (index, child) in children.0.iter().enumerate() {
- if index > 0 {
- writeln!(writer)?;
- }
- self.render(child, writer)?;
+ for (index, item) in ItemRefIterator::without_hidden(item)
+ .filter(|item| !item.details.is_heading())
+ .enumerate()
+ {
+ if index > 0 {
+ writeln!(writer)?;
+ }
+ match &item.details {
+ Details::Chart | Details::Image(_) => todo!(),
+ Details::Heading(_) => unreachable!(),
+ Details::Message(_diagnostic) => todo!(),
+ Details::PageBreak => (),
+ Details::Table(pivot_table) => self.render_table(pivot_table, writer)?,
+ Details::Text(text) => {
+ self.render_table(&PivotTable::from((**text).clone()), writer)?
}
- Ok(())
}
- Details::Message(_diagnostic) => todo!(),
- Details::PageBreak => Ok(()),
- Details::Table(pivot_table) => self.render_table(pivot_table, writer),
- Details::Text(text) => self.render_table(&PivotTable::from((**text).clone()), writer),
}
+ Ok(())
}
fn render_table<W>(&mut self, table: &PivotTable, writer: &mut W) -> FmtResult