a modern tui library written in zig
at v0.5.1 3.0 kB view raw
1const std = @import("std"); 2const unicode = std.unicode; 3const testing = std.testing; 4const DisplayWidth = @import("DisplayWidth"); 5const code_point = @import("code_point"); 6 7/// the method to use when calculating the width of a grapheme 8pub const Method = enum { 9 unicode, 10 wcwidth, 11 no_zwj, 12}; 13 14/// returns the width of the provided string, as measured by the method chosen 15pub fn gwidth(str: []const u8, method: Method, data: *const DisplayWidth.DisplayWidthData) usize { 16 switch (method) { 17 .unicode => { 18 const dw: DisplayWidth = .{ .data = data }; 19 return dw.strWidth(str); 20 }, 21 .wcwidth => { 22 var total: usize = 0; 23 var iter: code_point.Iterator = .{ .bytes = str }; 24 while (iter.next()) |cp| { 25 const w = switch (cp.code) { 26 // undo an override in zg for emoji skintone selectors 27 0x1f3fb...0x1f3ff, 28 => 2, 29 else => data.codePointWidth(cp.code), 30 }; 31 if (w < 0) continue; 32 total += @intCast(w); 33 } 34 return total; 35 }, 36 .no_zwj => { 37 var iter = std.mem.split(u8, str, "\u{200D}"); 38 var result: usize = 0; 39 while (iter.next()) |s| { 40 result += gwidth(s, .unicode, data); 41 } 42 return result; 43 }, 44 } 45} 46 47test "gwidth: a" { 48 const alloc = testing.allocator_instance.allocator(); 49 const data = try DisplayWidth.DisplayWidthData.init(alloc); 50 defer data.deinit(); 51 try testing.expectEqual(1, gwidth("a", .unicode, &data)); 52 try testing.expectEqual(1, gwidth("a", .wcwidth, &data)); 53 try testing.expectEqual(1, gwidth("a", .no_zwj, &data)); 54} 55 56test "gwidth: emoji with ZWJ" { 57 const alloc = testing.allocator_instance.allocator(); 58 const data = try DisplayWidth.DisplayWidthData.init(alloc); 59 defer data.deinit(); 60 try testing.expectEqual(2, gwidth("👩‍🚀", .unicode, &data)); 61 try testing.expectEqual(4, gwidth("👩‍🚀", .wcwidth, &data)); 62 try testing.expectEqual(4, gwidth("👩‍🚀", .no_zwj, &data)); 63} 64 65test "gwidth: emoji with VS16 selector" { 66 const alloc = testing.allocator_instance.allocator(); 67 const data = try DisplayWidth.DisplayWidthData.init(alloc); 68 defer data.deinit(); 69 try testing.expectEqual(2, gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .unicode, &data)); 70 try testing.expectEqual(1, gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .wcwidth, &data)); 71 try testing.expectEqual(2, gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .no_zwj, &data)); 72} 73 74test "gwidth: emoji with skin tone selector" { 75 const alloc = testing.allocator_instance.allocator(); 76 const data = try DisplayWidth.DisplayWidthData.init(alloc); 77 defer data.deinit(); 78 try testing.expectEqual(2, gwidth("👋🏿", .unicode, &data)); 79 try testing.expectEqual(4, gwidth("👋🏿", .wcwidth, &data)); 80 try testing.expectEqual(2, gwidth("👋🏿", .no_zwj, &data)); 81}