summaryrefslogtreecommitdiff
path: root/uucode_config.zig
blob: a9606914f1cc45c486b43060357a98831ec66e1a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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;
        }
    }
};