use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
pub(crate) fn display_width(s: &str) -> usize {
UnicodeWidthStr::width(s)
}
pub(crate) fn char_width(c: char) -> usize {
UnicodeWidthChar::width(c).unwrap_or(0)
}
pub(crate) fn wrap(text: &str, width: usize) -> Vec<String> {
let width = width.max(1);
let mut lines: Vec<String> = Vec::new();
let mut line = String::new();
let mut line_w = 0usize;
for word in text.split_whitespace() {
place_word(word, width, &mut lines, &mut line, &mut line_w);
}
if !line.is_empty() || lines.is_empty() {
lines.push(line);
}
lines
}
fn place_word(
word: &str,
width: usize,
lines: &mut Vec<String>,
line: &mut String,
line_w: &mut usize,
) {
let ww = display_width(word);
if line.is_empty() {
if ww <= width {
line.push_str(word);
*line_w = ww;
} else {
let mut chunks = hard_break(word, width);
let last = chunks.pop().unwrap_or_default();
lines.extend(chunks);
*line_w = display_width(&last);
*line = last;
}
} else if *line_w + 1 + ww <= width {
line.push(' ');
line.push_str(word);
*line_w += 1 + ww;
} else {
lines.push(std::mem::take(line));
*line_w = 0;
place_word(word, width, lines, line, line_w); }
}
fn hard_break(word: &str, width: usize) -> Vec<String> {
let mut out = Vec::new();
let mut cur = String::new();
let mut w = 0usize;
for c in word.chars() {
let cw = char_width(c);
if w + cw > width && !cur.is_empty() {
out.push(std::mem::take(&mut cur));
w = 0;
}
cur.push(c);
w += cw;
}
if !cur.is_empty() {
out.push(cur);
}
out
}