use super::*;
pub(super) type LaidOut = (usize, Vec<char>, Vec<(usize, usize)>);
pub(super) fn colored_spans(text: &str, colors: &[ratatui::style::Color], backgrounds: Option<&[ratatui::style::Color]>) -> Vec<Span<'static>> {
let paint = |(fg, bg): (ratatui::style::Color, Option<ratatui::style::Color>)| match bg {
Some(bg) => Style::new().fg(fg).bg(bg),
None => Style::new().fg(fg),
};
let mut spans = Vec::new();
let mut run = String::new();
let mut run_color: Option<(ratatui::style::Color, Option<ratatui::style::Color>)> = None;
for (index, character) in text.chars().enumerate() {
let Some(&color) = colors.get(index) else {
break;
};
let pair = (color, backgrounds.and_then(|row| row.get(index)).copied());
if let Some(previous) = run_color
&& previous != pair
{
spans.push(Span::styled(std::mem::take(&mut run), paint(previous)));
}
run_color = Some(pair);
run.push(character);
}
if let Some(pair) = run_color {
spans.push(Span::styled(run, paint(pair)));
}
spans
}
pub(super) fn visual_column(line: &str, char_col: usize) -> usize {
line.chars().take(char_col).map(|c| if c == '\t' { tab_width() } else { 1 }).sum()
}
pub(super) fn overlay_range(spans: Vec<Span<'static>>, from: usize, to: usize, extra: Style) -> Vec<Span<'static>> {
if from >= to {
return spans;
}
let mut result = Vec::with_capacity(spans.len() + 2);
let mut column = 0;
for span in spans {
let text = span.content.into_owned();
let length = text.chars().count();
let span_end = column + length;
if span_end <= from || column >= to {
result.push(Span::styled(text, span.style));
} else {
let characters: Vec<char> = text.chars().collect();
let head = from.saturating_sub(column).min(length);
let tail = to.saturating_sub(column).min(length);
if head > 0 {
result.push(Span::styled(characters[..head].iter().collect::<String>(), span.style));
}
result.push(Span::styled(characters[head..tail].iter().collect::<String>(), span.style.patch(extra)));
if tail < length {
result.push(Span::styled(characters[tail..].iter().collect::<String>(), span.style));
}
}
column = span_end;
}
result
}
pub(super) fn skip_columns(spans: Vec<Span<'static>>, columns: usize) -> Vec<Span<'static>> {
if columns == 0 {
return spans;
}
let mut left = columns;
let mut result = Vec::with_capacity(spans.len());
for span in spans {
if left == 0 {
result.push(span);
continue;
}
let mut kept = String::new();
for character in span.content.chars() {
if left == 0 {
kept.push(character);
continue;
}
let width = UnicodeWidthChar::width(character).unwrap_or(0);
if width > left {
kept.extend(std::iter::repeat_n(' ', width - left));
left = 0;
} else {
left -= width;
}
}
if !kept.is_empty() {
result.push(Span::styled(kept, span.style));
}
}
result
}
pub(super) fn place_cursor(mut spans: Vec<Span<'static>>, column: usize, style: Style) -> Vec<Span<'static>> {
let total: usize = spans.iter().map(|span| span.content.chars().count()).sum();
if column < total {
return overlay_range(spans, column, column + 1, style);
}
if column > total {
spans.push(Span::raw(" ".repeat(column - total)));
}
spans.push(Span::styled(" ", style));
spans
}