fresh/primitives/
display_width.rs1use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
9
10#[inline]
16pub fn char_width(c: char) -> usize {
17 c.width().unwrap_or(0)
19}
20
21#[inline]
26pub fn str_width(s: &str) -> usize {
27 s.width()
28}
29
30pub trait DisplayWidth {
32 fn display_width(&self) -> usize;
34}
35
36impl DisplayWidth for str {
37 #[inline]
38 fn display_width(&self) -> usize {
39 str_width(self)
40 }
41}
42
43impl DisplayWidth for String {
44 #[inline]
45 fn display_width(&self) -> usize {
46 str_width(self)
47 }
48}
49
50#[inline]
54pub fn visual_column_at_byte(s: &str, byte_offset: usize) -> usize {
55 s[..byte_offset.min(s.len())].chars().map(char_width).sum()
56}
57
58#[inline]
64pub fn byte_offset_at_visual_column(s: &str, visual_col: usize) -> usize {
65 let mut current_col = 0;
66 for (byte_idx, ch) in s.char_indices() {
67 if current_col >= visual_col {
68 return byte_idx;
69 }
70 current_col += char_width(ch);
71 }
72 s.len()
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn test_ascii_width() {
81 assert_eq!(str_width("Hello"), 5);
82 assert_eq!(str_width(""), 0);
83 assert_eq!(str_width(" "), 1);
84 }
85
86 #[test]
87 fn test_cjk_width() {
88 assert_eq!(str_width("你好"), 4);
90 assert_eq!(str_width("你好世界"), 8);
91
92 assert_eq!(str_width("月"), 2);
94 assert_eq!(str_width("日本"), 4);
95
96 assert_eq!(str_width("한글"), 4);
98 }
99
100 #[test]
101 fn test_emoji_width() {
102 assert_eq!(str_width("🚀"), 2);
104 assert_eq!(str_width("🎉"), 2);
105 assert_eq!(str_width("🚀🎉"), 4);
106 }
107
108 #[test]
109 fn test_mixed_width() {
110 assert_eq!(str_width("Hello你好"), 5 + 4);
112 assert_eq!(str_width("a你b"), 1 + 2 + 1);
113
114 assert_eq!(str_width("Hi🚀"), 2 + 2);
116 }
117
118 #[test]
119 fn test_char_width() {
120 assert_eq!(char_width('a'), 1);
121 assert_eq!(char_width('你'), 2);
122 assert_eq!(char_width('🚀'), 2);
123 }
124
125 #[test]
126 fn test_zero_width() {
127 assert_eq!(char_width('\0'), 0);
129 assert_eq!(char_width('\t'), 0); assert_eq!(char_width('\u{200B}'), 0);
133 }
134
135 #[test]
136 fn test_display_width_trait() {
137 let s = "你好";
138 assert_eq!(s.display_width(), 4);
139
140 let string = String::from("Hello🚀");
141 assert_eq!(string.display_width(), 7);
142 }
143}