pub(crate) mod ansi;
pub(crate) mod sgr;
pub(crate) mod wrap;
use std::io;
use unicode_segmentation::UnicodeSegmentation;
use self::ansi::{Esc, display_width, escape_display, parse_escape};
use self::wrap::cluster_width_at;
pub(crate) use self::wrap::wrap_line_ansi;
pub(crate) fn write_visible_row(
text: &str,
width: usize,
out: &mut dyn io::Write,
) -> io::Result<usize> {
let bytes = text.as_bytes();
let mut used = 0;
let mut i = 0;
while i < bytes.len() {
if bytes[i] == 0x1b {
let (end, kind) = parse_escape(bytes, i);
let seq = &text[i..end];
if matches!(kind, Esc::Sgr | Esc::Osc8) {
write!(out, "{seq}")?;
} else {
let display = escape_display(seq);
let w = display_width(&display);
if used + w > width {
return Ok(used);
}
write!(out, "{display}")?;
used += w;
}
i = end;
continue;
}
let cluster = &text[i..].graphemes(true).next().unwrap_or_default();
let w = cluster_width_at(cluster, used);
if used + w > width {
return Ok(used);
}
if *cluster == "\t" {
write!(out, "{}", " ".repeat(w))?;
} else {
write!(out, "{cluster}")?;
}
used += w;
i += cluster.len();
}
Ok(used)
}