use rich::measure::Measurement;
use rich::{Console, ConsoleOptions, Overflow, Renderable, Segment, Style};
use super::overflow::{fit_segments, OverflowPolicy};
const PROBE_WIDTH: usize = 1 << 16;
pub struct Overflowing {
inner: Box<dyn Renderable>,
policy: OverflowPolicy,
}
impl Overflowing {
pub fn new(inner: Box<dyn Renderable>, policy: OverflowPolicy) -> Self {
Overflowing { inner, policy }
}
pub fn policy(&self) -> OverflowPolicy {
self.policy
}
}
fn trim_padding(mut line: Vec<Segment>) -> (Vec<Segment>, Option<Option<Style>>) {
let mut pad = None;
while let Some(last) = line.last_mut() {
if last.control {
break;
}
let kept = last.text.trim_end_matches(' ').len();
if kept == last.text.len() {
break;
}
pad.get_or_insert_with(|| last.style.clone());
if kept == 0 {
line.pop();
} else {
last.text.truncate(kept);
break;
}
}
(line, pad)
}
impl Renderable for Overflowing {
fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
let width = options.max_width;
if width == 0 {
return Vec::new();
}
let natural = self
.inner
.measure(console, &options.update_width(PROBE_WIDTH))
.maximum;
let natural = if natural >= PROBE_WIDTH {
width
} else {
natural.max(width)
};
let mut opts = options.update_width(natural);
opts.overflow = Some(Overflow::Ignore);
opts.no_wrap = Some(true);
let segments = self.inner.rich_render(console, &opts);
let lines = Segment::split_lines(&segments);
let fits =
|line: &Vec<Segment>| line.iter().map(Segment::cell_length).sum::<usize>() <= width;
if self.policy == OverflowPolicy::Visible || lines.iter().all(fits) {
return segments;
}
let trimmed: Vec<_> = lines.into_iter().map(trim_padding).collect();
let pad = trimmed.iter().find_map(|(_, pad)| pad.clone()).map(|pad| {
match pad.as_ref().and_then(Style::bgcolor) {
Some(_) => pad,
None => trimmed
.iter()
.flat_map(|(content, _)| content.iter())
.find_map(|segment| segment.style.as_ref()?.bgcolor().cloned())
.map(|bg| Style::new().with_bgcolor(bg))
.or(pad),
}
});
let mut out = Vec::new();
for (index, (content, _)) in trimmed.iter().enumerate() {
let mut rows = fit_segments(content, width, self.policy);
if rows.is_empty() {
rows.push(Vec::new());
}
for (row_index, mut row) in rows.into_iter().enumerate() {
if index + row_index > 0 {
out.push(Segment::line());
}
if let Some(style) = &pad {
let used: usize = row.iter().map(Segment::cell_length).sum();
if used < width {
row.push(Segment::new(" ".repeat(width - used), style.clone()));
}
}
out.extend(row);
}
}
out
}
fn measure(&self, console: &Console, options: &ConsoleOptions) -> Measurement {
self.inner.measure(console, options)
}
fn fit_to_measurement(&self) -> bool {
self.inner.fit_to_measurement()
}
}