use ratatui::text::{Line, Span};
pub fn wrap_line(line: Line<'static>, width: usize) -> Vec<Line<'static>> {
if width == 0 {
return vec![line];
}
let mut out: Vec<Line<'static>> = Vec::new();
let mut cur: Vec<Span<'static>> = Vec::new();
let mut cur_w = 0usize;
for span in line.spans {
let mut remaining: &str = span.content.as_ref();
loop {
if remaining.is_empty() {
break;
}
let avail = width - cur_w;
if avail == 0 {
out.push(Line::from(std::mem::take(&mut cur)));
cur_w = 0;
continue;
}
let take: String = remaining.chars().take(avail).collect();
cur_w += take.chars().count();
remaining = &remaining[take.len()..];
cur.push(Span::styled(take, span.style));
}
}
out.push(Line::from(cur));
out
}
pub fn wrap_line_window(
text: &str,
width: usize,
skip_rows: usize,
max_rows: usize,
) -> Vec<Line<'static>> {
if max_rows == 0 {
return Vec::new();
}
if width == 0 {
return if skip_rows == 0 {
vec![Line::raw(text.to_string())]
} else {
Vec::new()
};
}
let skip_chars = skip_rows.saturating_mul(width);
let take_chars = max_rows.saturating_mul(width);
let windowed: String = text.chars().skip(skip_chars).take(take_chars).collect();
if windowed.is_empty() {
return Vec::new();
}
wrap_line(Line::raw(windowed), width)
}
pub fn wrapped_row_count(char_len: usize, width: usize) -> usize {
if width == 0 || char_len == 0 {
1
} else {
char_len.div_ceil(width)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wrapped_row_count_matches_wrap_line_for_various_lengths() {
for (len, width) in [(0, 10), (1, 10), (10, 10), (11, 10), (25, 10), (7, 0)] {
let text = "x".repeat(len);
let actual = wrap_line(Line::raw(text), width).len();
assert_eq!(
wrapped_row_count(len, width),
actual,
"len={len} width={width}"
);
}
}
}