use crate::{encoding::Encoding, grapheme::next_cluster, simd::plain_prefix, utf8::Utf8};
#[inline]
pub fn truncate_measured<E: Encoding>(input: &[E::Unit], max_width: usize) -> (&[E::Unit], usize) {
let mut width = 0usize;
let mut pos = 0usize;
loop {
if pos == input.len() {
return (input, width);
}
if !E::FOREIGN {
let remaining = &input[pos..];
let budget = max_width - width;
let window = remaining.len().min(budget.saturating_add(1));
let run = plain_prefix(&remaining[..window]);
if run > 0 {
if run == remaining.len() {
let take = run.min(budget);
pos += take;
width += take;
return if take == run {
(input, width)
} else {
(&input[..pos], width)
};
}
let safe = run - 1;
pos += safe;
width += safe;
}
}
let scan = next_cluster::<E>(&input[pos..]);
if width + scan.width > max_width {
return (&input[..pos], width);
}
pos += scan.units;
width += scan.width;
}
}
#[inline]
pub fn truncate<E: Encoding>(input: &[E::Unit], max_width: usize) -> &[E::Unit] {
truncate_measured::<E>(input, max_width).0
}
#[inline]
pub fn truncate_measured_str(input: &str, max_width: usize) -> (&str, usize) {
let (prefix, width) = truncate_measured::<Utf8>(input.as_bytes(), max_width);
(unsafe { core::str::from_utf8_unchecked(prefix) }, width)
}
#[inline]
pub fn truncate_str(input: &str, max_width: usize) -> &str {
unsafe { core::str::from_utf8_unchecked(truncate::<Utf8>(input.as_bytes(), max_width)) }
}
#[inline]
pub fn skip_columns<E: Encoding>(input: &[E::Unit], columns: usize) -> (&[E::Unit], usize) {
if columns == 0 {
return (input, 0);
}
let mut width = 0usize;
let mut pos = 0usize;
loop {
if pos == input.len() {
return (&input[pos..], width);
}
if width >= columns {
let scan = next_cluster::<E>(&input[pos..]);
if scan.width != 0 {
return (&input[pos..], width);
}
pos += scan.units;
continue;
}
if !E::FOREIGN {
let remaining = &input[pos..];
let budget = columns - width;
let window = remaining.len().min(budget.saturating_add(1));
let run = plain_prefix(&remaining[..window]);
if run > 0 {
let safe = if pos + run < input.len() {
run - 1
} else {
run
};
let take = safe.min(budget);
pos += take;
width += take;
if take < safe {
return (&input[pos..], width);
}
if pos == input.len() {
return (&input[pos..], width);
}
}
}
let scan = next_cluster::<E>(&input[pos..]);
pos += scan.units;
width += scan.width;
}
}
#[inline]
pub fn skip_columns_str(input: &str, columns: usize) -> (&str, usize) {
let (tail, width) = skip_columns::<Utf8>(input.as_bytes(), columns);
(unsafe { core::str::from_utf8_unchecked(tail) }, width)
}