use ratatui::text::Line;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
#[must_use]
pub(crate) fn count_noun(n: usize, singular: &str) -> String {
if n == 1 {
format!("1 {singular}")
} else {
format!("{n} {singular}s")
}
}
#[must_use]
pub(crate) fn wrapped_line_count(lines: &[Line<'_>], width: u16) -> usize {
if width == 0 {
return lines.len().max(1);
}
let w = width as usize;
lines
.iter()
.map(|line| wrapped_rows(line, w))
.sum::<usize>()
.max(1)
}
fn wrapped_rows(line: &Line<'_>, w: usize) -> usize {
let text: String = line.spans.iter().map(|s| s.content.as_ref()).collect();
if text.is_empty() {
return 1; }
let mut rows = 1usize;
let mut col = 0usize;
for word in text.split_inclusive(' ') {
let word_w = UnicodeWidthStr::width(word);
if word_w <= w {
if col + word_w > w {
rows += 1;
col = word_w;
} else {
col += word_w;
}
} else {
for ch in word.chars() {
let cw = UnicodeWidthChar::width(ch).unwrap_or(0);
if col + cw > w {
rows += 1;
col = 0;
}
col += cw;
}
}
}
rows
}
#[cfg(test)]
mod tests {
use super::{count_noun, wrapped_line_count};
use ratatui::text::Line;
#[test]
fn short_line_is_one_row() {
assert_eq!(wrapped_line_count(&[Line::from("hello world")], 20), 1);
}
#[test]
fn wraps_on_word_boundary_like_ratatui() {
assert_eq!(wrapped_line_count(&[Line::from("Hello World")], 10), 2);
}
#[test]
fn long_unbroken_word_is_hard_broken() {
assert_eq!(wrapped_line_count(&[Line::from("abcdefghij")], 4), 3);
}
#[test]
fn empty_and_multiple_lines() {
assert_eq!(wrapped_line_count(&[Line::from("")], 10), 1);
let lines = [Line::from("Hello World"), Line::from("Hello World")];
assert_eq!(wrapped_line_count(&lines, 10), 4);
}
#[test]
fn zero_width_falls_back_to_logical_count() {
assert_eq!(
wrapped_line_count(&[Line::from("a"), Line::from("b")], 0),
2
);
}
#[test]
fn count_noun_pluralizes_on_anything_but_one() {
assert_eq!(count_noun(1, "algorithm"), "1 algorithm");
assert_eq!(count_noun(3, "algorithm"), "3 algorithms");
assert_eq!(count_noun(0, "suite"), "0 suites");
assert_eq!(count_noun(1, "error"), "1 error");
}
}