use super::super::chapters;
use super::super::rows::{Row, WrappedLines, wrap_word_aware};
use super::super::{Block, BlockKind, Transcript};
impl Transcript {
pub(crate) fn render(&self, width: usize) -> (WrappedLines, Vec<usize>) {
let eff = width.max(1);
let mut lines = Vec::new();
let mut starts: Vec<usize> = Vec::with_capacity(self.blocks.len());
let mut skip_until = 0;
let mut fold_start = 0;
for (i, block) in self.blocks.iter().enumerate() {
if i < skip_until {
starts.push(fold_start);
continue;
}
starts.push(lines.len());
if self.is_folded(block.chapter)
&& (i == 0 || {
#[cfg(test)]
range_probe::note();
self.blocks[i - 1].chapter != block.chapter
})
{
let end = chapter_run_end(&self.blocks, i);
if let Some(row) = chapters::folded_row(&self.blocks, block.chapter, i, end, eff) {
fold_start = lines.len();
lines.push(row);
skip_until = end;
continue;
}
}
if block.text.is_empty()
&& block
.group
.as_ref()
.filter(|group| group.expanded)
.is_none()
{
lines.push(Row::body(block.kind, String::new()));
continue;
}
let mut labelled_yet = false;
let mut emit_body = |raw: &str, lines: &mut WrappedLines| {
if raw.is_empty() {
lines.push(Row::body(block.kind, String::new()));
return;
}
if !labelled_yet {
labelled_yet = true;
if let Some(label) = Row::label(block.kind) {
lines.push(label);
}
}
if block.kind == BlockKind::Table {
lines.push(Row::body(block.kind, raw.to_string()));
} else {
wrap_word_aware(raw, eff, block.kind, lines);
}
};
if let Some(group) = block.group.as_ref().filter(|group| group.expanded) {
for raw in std::iter::once(group.open_header.as_str())
.chain(group.detail.iter().map(String::as_str))
{
emit_body(raw, &mut lines);
}
continue;
}
for raw in block.text.split('\n') {
emit_body(raw, &mut lines);
}
}
(lines, starts)
}
}
pub(super) fn chapter_run_end(blocks: &[Block], start: usize) -> usize {
blocks[start + 1..]
.iter()
.position(|b| {
#[cfg(test)]
range_probe::note();
b.chapter != blocks[start].chapter
})
.map_or(blocks.len(), |rel| start + 1 + rel)
}
#[cfg(test)]
pub(crate) mod range_probe {
use std::cell::Cell;
thread_local! {
static COMPARISONS: Cell<usize> = const { Cell::new(0) };
}
pub(crate) fn note() {
COMPARISONS.with(|count| count.set(count.get() + 1));
}
pub(crate) fn reset() {
COMPARISONS.with(|count| count.set(0));
}
pub(crate) fn take() -> usize {
COMPARISONS.with(|count| count.replace(0))
}
}