a modern tui library written in zig
at v0.1.0 1.8 kB view raw
1const std = @import("std"); 2const unicode = std.unicode; 3const testing = std.testing; 4const ziglyph = @import("ziglyph"); 5 6/// the method to use when calculating the width of a grapheme 7pub const Method = enum { 8 unicode, 9 wcwidth, 10}; 11 12/// returns the width of the provided string, as measured by the method chosen 13pub fn gwidth(str: []const u8, method: Method) !usize { 14 switch (method) { 15 .unicode => { 16 return try ziglyph.display_width.strWidth(str, .half); 17 }, 18 .wcwidth => { 19 var total: usize = 0; 20 const utf8 = try unicode.Utf8View.init(str); 21 var iter = utf8.iterator(); 22 23 while (iter.nextCodepoint()) |cp| { 24 const w = ziglyph.display_width.codePointWidth(cp, .half); 25 if (w < 0) continue; 26 total += @intCast(w); 27 } 28 return total; 29 }, 30 } 31} 32 33test "gwidth: a" { 34 try testing.expectEqual(1, try gwidth("a", .unicode)); 35 try testing.expectEqual(1, try gwidth("a", .wcwidth)); 36} 37 38test "gwidth: emoji with ZWJ" { 39 try testing.expectEqual(2, try gwidth("👩‍🚀", .unicode)); 40 try testing.expectEqual(4, try gwidth("👩‍🚀", .wcwidth)); 41} 42 43test "gwidth: emoji with VS16 selector" { 44 try testing.expectEqual(2, try gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .unicode)); 45 try testing.expectEqual(1, try gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .wcwidth)); 46} 47 48test "gwidth: emoji with skin tone selector" { 49 try testing.expectEqual(2, try gwidth("👋🏿", .unicode)); 50 try testing.expectEqual(4, try gwidth("👋🏿", .wcwidth)); 51} 52 53test "gwidth: invalid string" { 54 try testing.expectError(error.InvalidUtf8, gwidth("\xc3\x28", .unicode)); 55 try testing.expectError(error.InvalidUtf8, gwidth("\xc3\x28", .wcwidth)); 56}