use crate::data::grapheme_break::gcb;
use crate::data::grapheme_break::Gcb;
use crate::data::line_break::is_east_asian_wide;
#[must_use]
pub fn display_width(s: &str) -> u32 {
s.chars().map(char_width).sum()
}
#[must_use]
pub fn char_width(ch: char) -> u32 {
let cp = ch as u32;
if ch == '\n' || ch == '\r' || ch == '\u{0008}' {
return 0;
}
let g = gcb(ch);
if matches!(g, Gcb::Extend | Gcb::Zwj | Gcb::SpacingMark | Gcb::ConjunctLinker) {
return 0;
}
if (cp <= 0x1F) || (0x7F..=0x9F).contains(&cp) {
return 0;
}
if is_east_asian_wide(cp) {
return 2;
}
1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn width_ascii() {
assert_eq!(display_width(""), 0);
assert_eq!(display_width("abc"), 3);
}
#[test]
fn width_cjk() {
let s = "a一b";
assert_eq!(display_width(s), 1 + 2 + 1);
}
#[test]
fn width_combining() {
let s = "e\u{0301}"; assert_eq!(display_width(s), 1);
}
}