1#[inline]
2pub fn display_width(s: &str) -> usize {
3 s.chars().map(char_display_width).sum()
4}
5
6pub fn char_display_width(c: char) -> usize {
7 use std::cmp::Ordering::*;
8
9 match c {
10 '\u{0000}' => 0, '\u{0001}'..='\u{001F}' | '\u{007F}' => 0, '\u{0300}'..='\u{036F}'
14 | '\u{1AB0}'..='\u{1AFF}'
15 | '\u{1DC0}'..='\u{1DFF}'
16 | '\u{20D0}'..='\u{20FF}'
17 | '\u{FE20}'..='\u{FE2F}' => 0, _ => match unicode_width_hint(c) {
20 Less => 1,
21 Equal | Greater => 2,
22 },
23 }
24}
25
26fn unicode_width_hint(c: char) -> std::cmp::Ordering {
28 match c as u32 {
29 0x1100..=0x115F
30 | 0x2329
31 | 0x232A
32 | 0x2E80..=0xA4CF
33 | 0xAC00..=0xD7A3
34 | 0xF900..=0xFAFF
35 | 0xFE10..=0xFE19
36 | 0xFE30..=0xFE6F
37 | 0xFF00..=0xFF60
38 | 0xFFE0..=0xFFE6
39 | 0x1F300..=0x1F64F
40 | 0x1F900..=0x1F9FF => std::cmp::Ordering::Greater,
41 _ => std::cmp::Ordering::Less,
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48
49 #[test]
50 fn test_display_width() {
51 for (s, expected) in [
52 ("hello", 5), ("你好", 4), ("á", 1), ] {
56 assert_eq!(display_width(s), expected);
57 }
58 }
59}