summaryrefslogtreecommitdiff
path: root/src/DisplayWidth.zig
diff options
context:
space:
mode:
authorGravatar Lich2026-01-13 01:10:17 +0300
committerGravatar Lich2026-01-13 01:10:17 +0300
commitdfece51720a17fddd0520ce8bda1a3c05d110949 (patch)
treed841e9321c1135ba5644b444ba7bb508ec4df025 /src/DisplayWidth.zig
parentMoved part of the `strWidth` into its own `graphemeClusterWidth` function (diff)
parentMerge pull request 'Use width 2 when skin tone modifier detected' (#96) from ... (diff)
downloadzg-dfece51720a17fddd0520ce8bda1a3c05d110949.tar.gz
zg-dfece51720a17fddd0520ce8bda1a3c05d110949.tar.xz
zg-dfece51720a17fddd0520ce8bda1a3c05d110949.zip
Merge branch 'master' of https://codeberg.org/atman/zg into graphemeClusterWidth
Diffstat (limited to 'src/DisplayWidth.zig')
-rw-r--r--src/DisplayWidth.zig31
1 files changed, 23 insertions, 8 deletions
diff --git a/src/DisplayWidth.zig b/src/DisplayWidth.zig
index 629087b..dee7ebd 100644
--- a/src/DisplayWidth.zig
+++ b/src/DisplayWidth.zig
@@ -39,11 +39,9 @@ pub fn setupWithGraphemes(dw: *DisplayWidth, allocator: Allocator, graphemes: Gr
39 39
40// Sets up the DisplayWidthData, leaving the GraphemeData undefined. 40// Sets up the DisplayWidthData, leaving the GraphemeData undefined.
41pub fn setup(dw: *DisplayWidth, allocator: Allocator) Allocator.Error!void { 41pub fn setup(dw: *DisplayWidth, allocator: Allocator) Allocator.Error!void {
42 const decompressor = compress.flate.inflate.decompressor;
43 const in_bytes = @embedFile("dwp"); 42 const in_bytes = @embedFile("dwp");
44 var in_fbs = std.io.fixedBufferStream(in_bytes); 43 var in_fbs = std.io.fixedBufferStream(in_bytes);
45 var in_decomp = decompressor(.raw, in_fbs.reader()); 44 var reader = in_fbs.reader();
46 var reader = in_decomp.reader();
47 45
48 const endian = builtin.cpu.arch.endian(); 46 const endian = builtin.cpu.arch.endian();
49 47
@@ -118,6 +116,8 @@ pub fn graphemeClusterWidth(dw: DisplayWidth, gc: []const u8) isize {
118 // emoji text sequence. 116 // emoji text sequence.
119 if (ncp.code == 0xFE0E) w = 1; 117 if (ncp.code == 0xFE0E) w = 1;
120 if (ncp.code == 0xFE0F) w = 2; 118 if (ncp.code == 0xFE0F) w = 2;
119 // Skin tones
120 if (0x1F3FB <= ncp.code and ncp.code <= 0x1F3FF) w = 2;
121 } 121 }
122 122
123 // Only adding width of first non-zero-width code point. 123 // Only adding width of first non-zero-width code point.
@@ -207,6 +207,9 @@ test "strWidth" {
207 try testing.expectEqual(@as(usize, 9), dw.strWidth("Ẓ̌á̲l͔̝̞̄̑͌g̖̘̘̔̔͢͞͝o̪̔T̢̙̫̈̍͞e̬͈͕͌̏͑x̺̍ṭ̓̓ͅ")); 207 try testing.expectEqual(@as(usize, 9), dw.strWidth("Ẓ̌á̲l͔̝̞̄̑͌g̖̘̘̔̔͢͞͝o̪̔T̢̙̫̈̍͞e̬͈͕͌̏͑x̺̍ṭ̓̓ͅ"));
208 try testing.expectEqual(@as(usize, 17), dw.strWidth("슬라바 우크라이나")); 208 try testing.expectEqual(@as(usize, 17), dw.strWidth("슬라바 우크라이나"));
209 try testing.expectEqual(@as(usize, 1), dw.strWidth("\u{378}")); 209 try testing.expectEqual(@as(usize, 1), dw.strWidth("\u{378}"));
210
211 // https://codeberg.org/atman/zg/issues/82
212 try testing.expectEqual(@as(usize, 12), dw.strWidth("✍️✍🏻✍🏼✍🏽✍🏾✍🏿"));
210} 213}
211 214
212/// centers `str` in a new string of width `total_width` (in display cells) using `pad` as padding. 215/// centers `str` in a new string of width `total_width` (in display cells) using `pad` as padding.
@@ -404,7 +407,7 @@ pub fn wrap(
404 columns: usize, 407 columns: usize,
405 threshold: usize, 408 threshold: usize,
406) ![]u8 { 409) ![]u8 {
407 var result = ArrayList(u8).init(allocator); 410 var result = std.array_list.Managed(u8).init(allocator);
408 defer result.deinit(); 411 defer result.deinit();
409 412
410 var line_iter = mem.tokenizeAny(u8, str, "\r\n"); 413 var line_iter = mem.tokenizeAny(u8, str, "\r\n");
@@ -426,8 +429,10 @@ pub fn wrap(
426 } 429 }
427 430
428 // Remove trailing space and newline. 431 // Remove trailing space and newline.
429 _ = result.pop(); 432 if (result.items[result.items.len - 1] == '\n')
430 _ = result.pop(); 433 _ = result.pop();
434 if (result.items[result.items.len - 1] == ' ')
435 _ = result.pop();
431 436
432 return try result.toOwnedSlice(); 437 return try result.toOwnedSlice();
433} 438}
@@ -444,6 +449,18 @@ test "wrap" {
444 try testing.expectEqualStrings(want, got); 449 try testing.expectEqualStrings(want, got);
445} 450}
446 451
452test "zg/74" {
453 var debug_alloc = std.heap.DebugAllocator(.{}).init;
454 const allocator = debug_alloc.allocator();
455 defer _ = debug_alloc.deinit();
456 const dw = try DisplayWidth.init(allocator);
457 defer dw.deinit(allocator);
458 const wrapped = try dw.wrap(allocator, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pellentesque pulvinar felis, sit amet commodo ligula feugiat sed. Sed quis malesuada elit, nec eleifend lectus. Sed tincidunt finibus aliquet. Praesent consectetur nibh libero, tempus imperdiet lorem congue eget.", 16, 1);
459 defer allocator.free(wrapped);
460 const expected_wrap = "Lorem ipsum dolor \nsit amet, consectetur \nadipiscing elit. \nNullam pellentesque \npulvinar felis, \nsit amet commodo \nligula feugiat \nsed. Sed quis malesuada \nelit, nec eleifend \nlectus. Sed tincidunt \nfinibus aliquet. \nPraesent consectetur \nnibh libero, tempus \nimperdiet lorem \ncongue eget.";
461 try std.testing.expectEqualStrings(expected_wrap, wrapped);
462}
463
447fn testAllocation(allocator: Allocator) !void { 464fn testAllocation(allocator: Allocator) !void {
448 { 465 {
449 var dw = try DisplayWidth.init(allocator); 466 var dw = try DisplayWidth.init(allocator);
@@ -464,8 +481,6 @@ test "allocation test" {
464const std = @import("std"); 481const std = @import("std");
465const builtin = @import("builtin"); 482const builtin = @import("builtin");
466const options = @import("options"); 483const options = @import("options");
467const ArrayList = std.ArrayList;
468const compress = std.compress;
469const mem = std.mem; 484const mem = std.mem;
470const Allocator = mem.Allocator; 485const Allocator = mem.Allocator;
471const simd = std.simd; 486const simd = std.simd;