diff options
Diffstat (limited to 'uucode_config.zig')
| -rw-r--r-- | uucode_config.zig | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/uucode_config.zig b/uucode_config.zig new file mode 100644 index 00000000..a9606914 --- /dev/null +++ b/uucode_config.zig @@ -0,0 +1,155 @@ +//! uucode build config shared by ghostty-vt and libvaxis. +//! +//! This is a copy of ghostty's `src/build/uucode_config.zig` (so ghostty-vt +//! gets exactly the fields/components it expects), with one change: every +//! table is forced to `.packing = .unpacked`. +//! +//! Why: libvaxis calls `uucode.get(...)`, which comptime-forces evaluation of +//! the whole generated `tables` value. With the default `.auto` packing the +//! `buildtime` table is emitted as a packed `@bitCast` of a huge `[N]u10` +//! array, and Zig 0.16.0 cannot evaluate that at comptime (it panics with +//! "TODO implement writeToPackedMemory for more types"). ghostty-vt itself +//! never forces that evaluation, so ghostty's own build doesn't hit it, but we +//! do the moment vaxis touches uucode. Unpacked tables are byte-aligned and +//! evaluate fine (see also the raised eval branch quota in main.zig). +const std = @import("std"); +const assert = std.debug.assert; +const config = @import("config.zig"); + +const Allocator = std.mem.Allocator; + +pub const fields = &config.mergeFields(config.fields, &.{ + .{ .name = "width", .type = u2 }, + .{ .name = "is_symbol", .type = bool }, +}); +pub const build_components = &config.mergeComponents(config.build_components, &.{ + .{ + .Impl = WidthComponent, + .inputs = &.{ + "wcwidth_standalone", + "wcwidth_zero_in_grapheme", + "is_emoji_modifier", + "grapheme_break_no_control", + }, + .fields = &.{"width"}, + }, + .{ + .Impl = IsSymbolComponent, + .inputs = &.{ "block", "general_category" }, + .fields = &.{"is_symbol"}, + }, +}); + +pub const get_components: []const config.Component = &.{}; + +pub const tables = [_]config.Table{ + .{ + .name = "runtime", + .packing = .unpacked, + .fields = &.{ + "is_emoji_presentation", + "case_folding_full", + }, + }, + .{ + // Fields that libvaxis needs that aren't included in the `runtime` + // table. + .name = "libvaxis_only", + .packing = .unpacked, + .fields = &.{ + "east_asian_width", + "general_category", + "grapheme_break", + }, + }, + .{ + .name = "buildtime", + .packing = .unpacked, + .fields = &.{ + "width", + "wcwidth_zero_in_grapheme", + "grapheme_break_no_control", + "is_symbol", + "is_emoji_vs_base", + }, + }, +}; + +const WidthComponent = struct { + pub fn build( + comptime InputRow: type, + comptime Row: type, + allocator: std.mem.Allocator, + io: std.Io, + inputs: config.MultiSlice(InputRow), + rows: *config.MultiSlice(Row), + backing: anytype, + tracking: anytype, + ) !void { + _ = allocator; + _ = io; + _ = backing; + _ = tracking; + + rows.len = config.num_code_points; + const items = rows.items(.width); + const standalone = inputs.items(.wcwidth_standalone); + const zero_in_grapheme = inputs.items(.wcwidth_zero_in_grapheme); + const is_emoji_modifier = inputs.items(.is_emoji_modifier); + const grapheme_break_no_control = inputs.items(.grapheme_break_no_control); + + // This condition is needed as Ghostty currently has a singular concept for + // the `width` of a code point, while `uucode` splits the concept into + // `wcwidth_standalone` and `wcwidth_zero_in_grapheme`. The two cases where + // we want to use the `wcwidth_standalone` despite the code point occupying + // zero width in a grapheme (`wcwidth_zero_in_grapheme`) are emoji + // modifiers and prepend code points. For emoji modifiers we want to + // support displaying them in isolation as color patches, and if prepend + // characters were to be width 0 they would disappear from the output with + // Ghostty's current width 0 handling. Future work will take advantage of + // the new uucode `wcwidth_standalone` vs `wcwidth_zero_in_grapheme` split. + for (0..config.num_code_points) |i| { + if (zero_in_grapheme[i] and !is_emoji_modifier[i] and grapheme_break_no_control[i] != .prepend) { + items[i] = 0; + } else { + items[i] = @min(2, standalone[i]); + } + } + } +}; + +const IsSymbolComponent = struct { + pub fn build( + comptime InputRow: type, + comptime Row: type, + allocator: std.mem.Allocator, + io: std.Io, + inputs: config.MultiSlice(InputRow), + rows: *config.MultiSlice(Row), + backing: anytype, + tracking: anytype, + ) !void { + _ = allocator; + _ = io; + _ = backing; + _ = tracking; + + rows.len = config.num_code_points; + const items = rows.items(.is_symbol); + const block = inputs.items(.block); + const general_category = inputs.items(.general_category); + + for (0..config.num_code_points) |i| { + items[i] = + general_category[i] == .other_private_use or + block[i] == .arrows or + block[i] == .dingbats or + block[i] == .emoticons or + block[i] == .miscellaneous_symbols or + block[i] == .enclosed_alphanumerics or + block[i] == .enclosed_alphanumeric_supplement or + block[i] == .miscellaneous_symbols_and_pictographs or + block[i] == .transport_and_map_symbols; + } + } +}; |
