mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00
WIP
This commit is contained in:
@ -41,6 +41,11 @@
|
||||
.hash = "ziglyph-0.11.2-AAAAAHPtHwB4Mbzn1KvOV7Wpjo82NYEc_v0WC8oCLrkf",
|
||||
.lazy = true,
|
||||
},
|
||||
.zg = .{
|
||||
.url = "https://codeberg.org/jacobsandlund/zg/archive/21be4c72f2aff537d7630a7aeb641befc935db89.tar.gz",
|
||||
.hash = "zg-0.14.0-oGqU3IkvswJcetRSabEnsUe_jsWz48WkZebbVS-pJ5jB",
|
||||
.lazy = true,
|
||||
},
|
||||
.zig_wayland = .{
|
||||
// codeberg ifreund/zig-wayland
|
||||
.url = "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz",
|
||||
|
@ -27,6 +27,8 @@ hyperfine \
|
||||
"./zig-out/bin/bench-grapheme-break --mode=noop${ARGS} </tmp/ghostty_bench_data" \
|
||||
-n ziglyph \
|
||||
"./zig-out/bin/bench-grapheme-break --mode=ziglyph${ARGS} </tmp/ghostty_bench_data" \
|
||||
-n zg \
|
||||
"./zig-out/bin/bench-grapheme-break --mode=zg${ARGS} </tmp/ghostty_bench_data" \
|
||||
-n table \
|
||||
"./zig-out/bin/bench-grapheme-break --mode=table${ARGS} </tmp/ghostty_bench_data"
|
||||
|
||||
|
@ -13,6 +13,7 @@ const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const ziglyph = @import("ziglyph");
|
||||
const Graphemes = @import("Graphemes");
|
||||
const cli = @import("../cli.zig");
|
||||
const simd = @import("../simd/main.zig");
|
||||
const unicode = @import("../unicode/main.zig");
|
||||
@ -44,6 +45,9 @@ const Mode = enum {
|
||||
/// Use ziglyph library to calculate the display width of each codepoint.
|
||||
ziglyph,
|
||||
|
||||
/// Use zg library to calculate the display width of each codepoint.
|
||||
zg,
|
||||
|
||||
/// Ghostty's table-based approach.
|
||||
table,
|
||||
};
|
||||
@ -56,6 +60,10 @@ pub fn main() !void {
|
||||
// We want to use the c allocator because it is much faster than GPA.
|
||||
const alloc = std.heap.c_allocator;
|
||||
|
||||
// Initialize Graphemes for zg
|
||||
const graphemes = try Graphemes.init(alloc);
|
||||
graphemes.deinit(alloc);
|
||||
|
||||
// Parse our args
|
||||
var args: Args = .{};
|
||||
defer args.deinit();
|
||||
@ -72,6 +80,7 @@ pub fn main() !void {
|
||||
switch (args.mode) {
|
||||
.noop => try benchNoop(reader, buf),
|
||||
.ziglyph => try benchZiglyph(reader, buf),
|
||||
.zg => try benchZg(&graphemes, reader, buf),
|
||||
.table => try benchTable(reader, buf),
|
||||
}
|
||||
}
|
||||
@ -118,6 +127,32 @@ noinline fn benchTable(
|
||||
}
|
||||
}
|
||||
|
||||
noinline fn benchZg(
|
||||
graphemes: *const Graphemes,
|
||||
reader: anytype,
|
||||
buf: []u8,
|
||||
) !void {
|
||||
var d: UTF8Decoder = .{};
|
||||
var state: Graphemes.State = .{};
|
||||
var cp1: u21 = 0;
|
||||
while (true) {
|
||||
const n = try reader.read(buf);
|
||||
if (n == 0) break;
|
||||
|
||||
// Using stream.next directly with a for loop applies a naive
|
||||
// scalar approach.
|
||||
for (buf[0..n]) |c| {
|
||||
const cp_, const consumed = d.next(c);
|
||||
assert(consumed);
|
||||
if (cp_) |cp2| {
|
||||
const v = Graphemes.graphemeBreak(cp1, @intCast(cp2), graphemes, &state);
|
||||
buf[0] = @intCast(@intFromBool(v));
|
||||
cp1 = cp2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
noinline fn benchZiglyph(
|
||||
reader: anytype,
|
||||
buf: []u8,
|
||||
|
@ -417,6 +417,18 @@ pub fn add(
|
||||
})) |dep| {
|
||||
step.root_module.addImport("ziglyph", dep.module("ziglyph"));
|
||||
}
|
||||
if (b.lazyDependency("zg", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
})) |dep| {
|
||||
step.root_module.addImport("CaseFolding", dep.module("CaseFolding"));
|
||||
// Only needed for table gen, but still include to have build succeeed
|
||||
step.root_module.addImport("DisplayWidth", dep.module("DisplayWidth"));
|
||||
step.root_module.addImport("Emoji", dep.module("Emoji"));
|
||||
step.root_module.addImport("GeneralCategories", dep.module("GeneralCategories"));
|
||||
step.root_module.addImport("Graphemes", dep.module("Graphemes"));
|
||||
step.root_module.addImport("LetterCasing", dep.module("LetterCasing"));
|
||||
}
|
||||
if (b.lazyDependency("zf", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
|
@ -21,13 +21,11 @@ pub fn init(b: *std.Build) !UnicodeTables {
|
||||
}),
|
||||
});
|
||||
|
||||
if (b.lazyDependency("ziglyph", .{
|
||||
if (b.lazyDependency("zg", .{
|
||||
.target = b.graph.host,
|
||||
})) |ziglyph_dep| {
|
||||
exe.root_module.addImport(
|
||||
"ziglyph",
|
||||
ziglyph_dep.module("ziglyph"),
|
||||
);
|
||||
})) |dep| {
|
||||
exe.root_module.addImport("Graphemes", dep.module("Graphemes"));
|
||||
exe.root_module.addImport("DisplayWidth", dep.module("DisplayWidth"));
|
||||
}
|
||||
|
||||
const run = b.addRunArtifact(exe);
|
||||
|
@ -16,7 +16,6 @@ const build_config = @import("../build_config.zig");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const global_state = &@import("../global.zig").state;
|
||||
const fontpkg = @import("../font/main.zig");
|
||||
const inputpkg = @import("../input.zig");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
@ -5765,6 +5764,8 @@ pub const Keybinds = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var set: Keybinds = .{};
|
||||
try set.parseCLI(alloc, "shift+a=copy_to_clipboard");
|
||||
@ -5779,6 +5780,8 @@ pub const Keybinds = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: Keybinds = .{};
|
||||
try list.parseCLI(alloc, "shift+a=csi:hello");
|
||||
@ -5795,6 +5798,8 @@ pub const Keybinds = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: Keybinds = .{};
|
||||
try list.parseCLI(alloc, "ctrl+z>1=goto_tab:1");
|
||||
@ -5819,6 +5824,8 @@ pub const Keybinds = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: Keybinds = .{};
|
||||
try list.parseCLI(alloc, "ctrl+a>ctrl+b>n=new_window");
|
||||
@ -6030,6 +6037,8 @@ pub const RepeatableCodepointMap = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "U+ABCD=Comic Sans");
|
||||
@ -6067,6 +6076,8 @@ pub const RepeatableCodepointMap = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "U+ABCD=Comic Sans");
|
||||
@ -6082,6 +6093,8 @@ pub const RepeatableCodepointMap = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "U+0001 - U+0005=Verdana");
|
||||
@ -6097,6 +6110,8 @@ pub const RepeatableCodepointMap = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: Self = .{};
|
||||
try list.parseCLI(alloc, "U+0006-U+0009, U+ABCD=Courier");
|
||||
@ -6175,6 +6190,8 @@ pub const FontStyle = union(enum) {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var p: Self = .{ .default = {} };
|
||||
try p.parseCLI(alloc, "default");
|
||||
@ -6195,6 +6212,8 @@ pub const FontStyle = union(enum) {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var p: Self = .{ .default = {} };
|
||||
try p.parseCLI(alloc, "default");
|
||||
@ -6210,6 +6229,8 @@ pub const FontStyle = union(enum) {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var p: Self = .{ .default = {} };
|
||||
try p.parseCLI(alloc, "false");
|
||||
@ -6225,6 +6246,8 @@ pub const FontStyle = union(enum) {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var p: Self = .{ .default = {} };
|
||||
try p.parseCLI(alloc, "bold");
|
||||
@ -6402,6 +6425,8 @@ pub const RepeatableCommand = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: RepeatableCommand = .{};
|
||||
try list.parseCLI(alloc, "title:Foo,action:ignore");
|
||||
@ -6447,6 +6472,8 @@ pub const RepeatableCommand = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: RepeatableCommand = .{};
|
||||
try list.parseCLI(alloc, "title:Bobr, action:text:Bober");
|
||||
@ -6462,6 +6489,8 @@ pub const RepeatableCommand = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var list: RepeatableCommand = .{};
|
||||
try list.parseCLI(alloc, "title:Bobr, action:text:kurwa");
|
||||
@ -7180,6 +7209,8 @@ pub const Theme = struct {
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
// Single
|
||||
{
|
||||
@ -7597,6 +7628,8 @@ const TestIterator = struct {
|
||||
|
||||
test "parse hook: invalid command" {
|
||||
const testing = std.testing;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
var cfg = try Config.default(testing.allocator);
|
||||
defer cfg.deinit();
|
||||
const alloc = cfg._arena.?.allocator();
|
||||
@ -7608,6 +7641,8 @@ test "parse hook: invalid command" {
|
||||
|
||||
test "parse e: command only" {
|
||||
const testing = std.testing;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
var cfg = try Config.default(testing.allocator);
|
||||
defer cfg.deinit();
|
||||
const alloc = cfg._arena.?.allocator();
|
||||
@ -7623,6 +7658,8 @@ test "parse e: command only" {
|
||||
|
||||
test "parse e: command and args" {
|
||||
const testing = std.testing;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
var cfg = try Config.default(testing.allocator);
|
||||
defer cfg.deinit();
|
||||
const alloc = cfg._arena.?.allocator();
|
||||
@ -7641,6 +7678,8 @@ test "parse e: command and args" {
|
||||
test "clone default" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var source = try Config.default(alloc);
|
||||
defer source.deinit();
|
||||
@ -7658,6 +7697,8 @@ test "clone default" {
|
||||
test "clone preserves conditional state" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var a = try Config.default(alloc);
|
||||
defer a.deinit();
|
||||
@ -7686,6 +7727,8 @@ test "clone can then change conditional state" {
|
||||
var arena = ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
const alloc_arena = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
@ -7746,6 +7789,8 @@ test "clone can then change conditional state" {
|
||||
test "clone preserves conditional set" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var cfg = try Config.default(alloc);
|
||||
defer cfg.deinit();
|
||||
@ -7765,6 +7810,8 @@ test "clone preserves conditional set" {
|
||||
test "changed" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var source = try Config.default(alloc);
|
||||
defer source.deinit();
|
||||
@ -7779,6 +7826,8 @@ test "changed" {
|
||||
test "changeConditionalState ignores irrelevant changes" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
{
|
||||
var cfg = try Config.default(alloc);
|
||||
@ -7798,6 +7847,8 @@ test "changeConditionalState ignores irrelevant changes" {
|
||||
test "changeConditionalState applies relevant changes" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
{
|
||||
var cfg = try Config.default(alloc);
|
||||
@ -7820,6 +7871,8 @@ test "theme loading" {
|
||||
var arena = ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
const alloc_arena = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
@ -7856,6 +7909,8 @@ test "theme loading preserves conditional state" {
|
||||
var arena = ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
const alloc_arena = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
@ -7886,6 +7941,8 @@ test "theme priority is lower than config" {
|
||||
var arena = ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
const alloc_arena = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
@ -7920,6 +7977,8 @@ test "theme loading correct light/dark" {
|
||||
var arena = ArenaAllocator.init(alloc);
|
||||
defer arena.deinit();
|
||||
const alloc_arena = arena.allocator();
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
// Setup our test theme
|
||||
var td = try internal_os.TempDir.init();
|
||||
@ -8009,6 +8068,8 @@ test "theme loading correct light/dark" {
|
||||
test "theme specifying light/dark changes window-theme from auto" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
{
|
||||
var cfg = try Config.default(alloc);
|
||||
@ -8027,6 +8088,8 @@ test "theme specifying light/dark changes window-theme from auto" {
|
||||
test "theme specifying light/dark sets theme usage in conditional state" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
{
|
||||
var cfg = try Config.default(alloc);
|
||||
|
@ -121,6 +121,8 @@ fn fieldByKey(self: *const Config, comptime k: Key) Value(k) {
|
||||
test "c_get: u8" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var c = try Config.default(alloc);
|
||||
defer c.deinit();
|
||||
@ -134,6 +136,8 @@ test "c_get: u8" {
|
||||
test "c_get: enum" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var c = try Config.default(alloc);
|
||||
defer c.deinit();
|
||||
@ -149,6 +153,8 @@ test "c_get: enum" {
|
||||
test "c_get: color" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var c = try Config.default(alloc);
|
||||
defer c.deinit();
|
||||
@ -164,6 +170,8 @@ test "c_get: color" {
|
||||
test "c_get: optional" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var c = try Config.default(alloc);
|
||||
defer c.deinit();
|
||||
@ -187,6 +195,8 @@ test "c_get: optional" {
|
||||
test "c_get: background-blur" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var c = try Config.default(alloc);
|
||||
defer c.deinit();
|
||||
|
@ -193,6 +193,8 @@ pub const FileFormatter = struct {
|
||||
test "format default config" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
var cfg = try Config.default(alloc);
|
||||
defer cfg.deinit();
|
||||
|
||||
@ -212,6 +214,8 @@ test "format default config" {
|
||||
test "format default config changed" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
var cfg = try Config.default(alloc);
|
||||
defer cfg.deinit();
|
||||
cfg.@"font-size" = 42;
|
||||
|
@ -13,8 +13,9 @@ const CodepointResolver = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ziglyph = @import("ziglyph");
|
||||
const Emoji = @import("Emoji");
|
||||
const font = @import("main.zig");
|
||||
const zg = &@import("../global.zig").state.zg;
|
||||
const Atlas = font.Atlas;
|
||||
const CodepointMap = font.CodepointMap;
|
||||
const Collection = font.Collection;
|
||||
@ -150,7 +151,7 @@ pub fn getIndex(
|
||||
// we'll do this multiple times if we recurse, but this is a cached function
|
||||
// call higher up (GroupCache) so this should be rare.
|
||||
const p_mode: Collection.PresentationMode = if (p) |v| .{ .explicit = v } else .{
|
||||
.default = if (ziglyph.emoji.isEmojiPresentation(@intCast(cp)))
|
||||
.default = if (Emoji.isEmojiPresentation(zg.emoji, @intCast(cp)))
|
||||
.emoji
|
||||
else
|
||||
.text,
|
||||
@ -379,6 +380,8 @@ test getIndex {
|
||||
const testFont = font.embedded.regular;
|
||||
const testEmoji = font.embedded.emoji;
|
||||
const testEmojiText = font.embedded.emoji_text;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
@ -457,6 +460,8 @@ test "getIndex disabled font style" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const testFont = font.embedded.regular;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var atlas_grayscale = try font.Atlas.init(alloc, 512, .grayscale);
|
||||
defer atlas_grayscale.deinit(alloc);
|
||||
@ -512,6 +517,8 @@ test "getIndex disabled font style" {
|
||||
test "getIndex box glyph" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var lib = try Library.init(alloc);
|
||||
defer lib.deinit();
|
||||
|
@ -369,6 +369,8 @@ fn testGrid(mode: TestMode, alloc: Allocator, lib: Library) !SharedGrid {
|
||||
test getIndex {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
// const testEmoji = @import("test.zig").fontEmoji;
|
||||
|
||||
var lib = try Library.init(alloc);
|
||||
|
@ -694,6 +694,8 @@ pub const Key = struct {
|
||||
test "Key" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
var cfg = try Config.default(alloc);
|
||||
defer cfg.deinit();
|
||||
|
||||
@ -713,6 +715,8 @@ test "Key" {
|
||||
test "Key different font points" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
var cfg = try Config.default(alloc);
|
||||
defer cfg.deinit();
|
||||
|
||||
@ -731,6 +735,8 @@ test "Key different font points" {
|
||||
test "Key different font DPI" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
var cfg = try Config.default(alloc);
|
||||
defer cfg.deinit();
|
||||
|
||||
@ -749,6 +755,8 @@ test "Key different font DPI" {
|
||||
test SharedGridSet {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var set = try SharedGridSet.init(alloc);
|
||||
defer set.deinit();
|
||||
|
@ -576,6 +576,8 @@ pub const Shaper = struct {
|
||||
test "run iterator" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -656,6 +658,8 @@ test "run iterator" {
|
||||
test "run iterator: empty cells with background set" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -704,6 +708,8 @@ test "run iterator: empty cells with background set" {
|
||||
test "shape" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -737,6 +743,8 @@ test "shape" {
|
||||
test "shape nerd fonts" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaperWithFont(alloc, .nerd_font);
|
||||
defer testdata.deinit();
|
||||
@ -770,6 +778,8 @@ test "shape nerd fonts" {
|
||||
test "shape inconsolata ligs" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -824,6 +834,8 @@ test "shape inconsolata ligs" {
|
||||
test "shape monaspace ligs" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaperWithFont(alloc, .monaspace_neon);
|
||||
defer testdata.deinit();
|
||||
@ -856,6 +868,8 @@ test "shape monaspace ligs" {
|
||||
test "shape left-replaced lig in last run" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaperWithFont(alloc, .geist_mono);
|
||||
defer testdata.deinit();
|
||||
@ -888,6 +902,8 @@ test "shape left-replaced lig in last run" {
|
||||
test "shape left-replaced lig in early run" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaperWithFont(alloc, .geist_mono);
|
||||
defer testdata.deinit();
|
||||
@ -917,6 +933,8 @@ test "shape left-replaced lig in early run" {
|
||||
test "shape U+3C9 with JB Mono" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaperWithFont(alloc, .jetbrains_mono);
|
||||
defer testdata.deinit();
|
||||
@ -948,6 +966,8 @@ test "shape U+3C9 with JB Mono" {
|
||||
test "shape emoji width" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -977,6 +997,8 @@ test "shape emoji width" {
|
||||
test "shape emoji width long" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1024,6 +1046,8 @@ test "shape emoji width long" {
|
||||
test "shape variation selector VS15" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1057,6 +1081,8 @@ test "shape variation selector VS15" {
|
||||
test "shape variation selector VS16" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1090,6 +1116,8 @@ test "shape variation selector VS16" {
|
||||
test "shape with empty cells in between" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1121,6 +1149,8 @@ test "shape with empty cells in between" {
|
||||
test "shape Chinese characters" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1161,6 +1191,8 @@ test "shape Chinese characters" {
|
||||
test "shape box glyphs" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1198,6 +1230,8 @@ test "shape box glyphs" {
|
||||
test "shape selection boundary" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1321,6 +1355,8 @@ test "shape selection boundary" {
|
||||
test "shape cursor boundary" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1458,6 +1494,8 @@ test "shape cursor boundary" {
|
||||
test "shape cursor boundary and colored emoji" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1552,6 +1590,8 @@ test "shape cursor boundary and colored emoji" {
|
||||
test "shape cell attribute change" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
@ -1680,6 +1720,8 @@ test "shape high plane sprite font codepoint" {
|
||||
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var testdata = try testShaper(alloc);
|
||||
defer testdata.deinit();
|
||||
|
@ -1,9 +1,10 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ziglyph = @import("ziglyph");
|
||||
const Graphemes = @import("Graphemes");
|
||||
const font = @import("../main.zig");
|
||||
const terminal = @import("../../terminal/main.zig");
|
||||
const zg = &@import("../../global.zig").state.zg;
|
||||
|
||||
const log = std.log.scoped(.font_shaper);
|
||||
|
||||
@ -111,7 +112,7 @@ pub const Shaper = struct {
|
||||
// font ligatures. However, we do support grapheme clustering.
|
||||
// This means we can render things like skin tone emoji but
|
||||
// we can't render things like single glyph "=>".
|
||||
var break_state: u3 = 0;
|
||||
var break_state: Graphemes.State = .{};
|
||||
var cp1: u21 = @intCast(codepoints[0]);
|
||||
|
||||
var start: usize = 0;
|
||||
@ -126,9 +127,10 @@ pub const Shaper = struct {
|
||||
const cp2: u21 = @intCast(codepoints[i]);
|
||||
defer cp1 = cp2;
|
||||
|
||||
break :blk ziglyph.graphemeBreak(
|
||||
break :blk Graphemes.graphemeBreak(
|
||||
cp1,
|
||||
cp2,
|
||||
zg.graphemes,
|
||||
&break_state,
|
||||
);
|
||||
};
|
||||
|
@ -10,6 +10,12 @@ const oni = @import("oniguruma");
|
||||
const crash = @import("crash/main.zig");
|
||||
const renderer = @import("renderer.zig");
|
||||
const apprt = @import("apprt.zig");
|
||||
const CaseFolding = @import("CaseFolding");
|
||||
const Emoji = @import("Emoji");
|
||||
const GeneralCategories = @import("GeneralCategories");
|
||||
const Graphemes = @import("Graphemes");
|
||||
const LetterCasing = @import("LetterCasing");
|
||||
const unicode = @import("unicode/main.zig");
|
||||
|
||||
/// We export the xev backend we want to use so that the rest of
|
||||
/// Ghostty can import this once and have access to the proper
|
||||
@ -33,6 +39,7 @@ pub const GlobalState = struct {
|
||||
action: ?cli.Action,
|
||||
logging: Logging,
|
||||
rlimits: ResourceLimits = .{},
|
||||
zg: Zg,
|
||||
|
||||
/// The app resources directory, equivalent to zig-out/share when we build
|
||||
/// from source. This is null if we can't detect it.
|
||||
@ -64,6 +71,7 @@ pub const GlobalState = struct {
|
||||
.logging = .{ .stderr = {} },
|
||||
.rlimits = .{},
|
||||
.resources_dir = .{},
|
||||
.zg = undefined,
|
||||
};
|
||||
errdefer self.deinit();
|
||||
|
||||
@ -150,6 +158,8 @@ pub const GlobalState = struct {
|
||||
);
|
||||
};
|
||||
|
||||
self.zg = try Zg.init(self.alloc);
|
||||
|
||||
// const sentrylib = @import("sentry");
|
||||
// if (sentrylib.captureEvent(sentrylib.Value.initMessageEvent(
|
||||
// .info,
|
||||
@ -188,6 +198,8 @@ pub const GlobalState = struct {
|
||||
// Flush our crash logs
|
||||
crash.deinit();
|
||||
|
||||
self.zg.deinit(self.alloc);
|
||||
|
||||
if (self.gpa) |*value| {
|
||||
// We want to ensure that we deinit the GPA because this is
|
||||
// the point at which it will output if there were safety violations.
|
||||
@ -216,6 +228,42 @@ pub const GlobalState = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const Zg = struct {
|
||||
case_folding: CaseFolding,
|
||||
emoji: Emoji,
|
||||
general_categories: GeneralCategories,
|
||||
graphemes: Graphemes,
|
||||
letter_casing: LetterCasing,
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) !Zg {
|
||||
return .{
|
||||
.case_folding = try CaseFolding.init(alloc),
|
||||
.emoji = try Emoji.init(alloc),
|
||||
.general_categories = try GeneralCategories.init(alloc),
|
||||
.graphemes = try Graphemes.init(alloc),
|
||||
.letter_casing = try LetterCasing.init(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Zg, alloc: std.mem.Allocator) void {
|
||||
self.case_folding.deinit(alloc);
|
||||
self.emoji.deinit(alloc);
|
||||
self.general_categories.deinit(alloc);
|
||||
self.graphemes.deinit(alloc);
|
||||
self.letter_casing.deinit(alloc);
|
||||
}
|
||||
|
||||
pub fn initForTesting() !*Zg {
|
||||
const zg = &state.zg;
|
||||
zg.* = try Zg.init(std.testing.allocator);
|
||||
return zg;
|
||||
}
|
||||
|
||||
pub fn deinitForTesting(self: *Zg) void {
|
||||
self.deinit(std.testing.allocator);
|
||||
}
|
||||
};
|
||||
|
||||
/// Maintains the Unix resource limits that we set for our process. This
|
||||
/// can be used to restore the limits to their original values.
|
||||
pub const ResourceLimits = struct {
|
||||
|
@ -5,9 +5,11 @@ const Binding = @This();
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const ziglyph = @import("ziglyph");
|
||||
const LetterCasing = @import("LetterCasing");
|
||||
const CaseFolding = @import("CaseFolding");
|
||||
const key = @import("key.zig");
|
||||
const KeyEvent = key.KeyEvent;
|
||||
const zg = &@import("../global.zig").state.zg;
|
||||
|
||||
/// The trigger that needs to be performed to execute the action.
|
||||
trigger: Trigger,
|
||||
@ -1551,15 +1553,17 @@ pub const Trigger = struct {
|
||||
/// in more codepoints so we need to use a 3 element array.
|
||||
fn foldedCodepoint(cp: u21) [3]u21 {
|
||||
// ASCII fast path
|
||||
if (ziglyph.letter.isAsciiLetter(cp)) {
|
||||
return .{ ziglyph.letter.toLower(cp), 0, 0 };
|
||||
if (('A' <= cp and cp <= 'Z') or ('a' <= cp and cp <= 'z')) {
|
||||
return .{ LetterCasing.toLower(zg.letter_casing, cp), 0, 0 };
|
||||
}
|
||||
|
||||
// Unicode slow path. Case folding can resultin more codepoints.
|
||||
// Unicode slow path. Case folding can result in more codepoints.
|
||||
// If more codepoints are produced then we return the codepoint
|
||||
// as-is which isn't correct but until we have a failing test
|
||||
// then I don't want to handle this.
|
||||
return ziglyph.letter.toCaseFold(cp);
|
||||
var buf: [3]u21 = .{ 0, 0, 0 };
|
||||
_ = CaseFolding.caseFold(&zg.case_folding, cp, &buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/// Convert the trigger to a C API compatible trigger.
|
||||
@ -2612,6 +2616,8 @@ test "parse: sequences" {
|
||||
test "set: parseAndPut typical binding" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2635,6 +2641,8 @@ test "set: parseAndPut typical binding" {
|
||||
test "set: parseAndPut unconsumed binding" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2659,6 +2667,8 @@ test "set: parseAndPut unconsumed binding" {
|
||||
test "set: parseAndPut removed binding" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2677,6 +2687,8 @@ test "set: parseAndPut removed binding" {
|
||||
test "set: parseAndPut sequence" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2701,6 +2713,8 @@ test "set: parseAndPut sequence" {
|
||||
test "set: parseAndPut sequence with two actions" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2733,6 +2747,8 @@ test "set: parseAndPut sequence with two actions" {
|
||||
test "set: parseAndPut overwrite sequence" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2758,6 +2774,8 @@ test "set: parseAndPut overwrite sequence" {
|
||||
test "set: parseAndPut overwrite leader" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2783,6 +2801,8 @@ test "set: parseAndPut overwrite leader" {
|
||||
test "set: parseAndPut unbind sequence unbinds leader" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2799,6 +2819,8 @@ test "set: parseAndPut unbind sequence unbinds leader" {
|
||||
test "set: parseAndPut unbind sequence unbinds leader if not set" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2814,6 +2836,8 @@ test "set: parseAndPut unbind sequence unbinds leader if not set" {
|
||||
test "set: parseAndPut sequence preserves reverse mapping" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2831,6 +2855,8 @@ test "set: parseAndPut sequence preserves reverse mapping" {
|
||||
test "set: put overwrites sequence" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2851,6 +2877,8 @@ test "set: put overwrites sequence" {
|
||||
test "set: maintains reverse mapping" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2879,6 +2907,8 @@ test "set: maintains reverse mapping" {
|
||||
test "set: performable is not part of reverse mappings" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2912,6 +2942,8 @@ test "set: performable is not part of reverse mappings" {
|
||||
test "set: overriding a mapping updates reverse" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2933,6 +2965,8 @@ test "set: overriding a mapping updates reverse" {
|
||||
test "set: consumed state" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2958,6 +2992,8 @@ test "set: consumed state" {
|
||||
test "set: getEvent physical" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -2988,6 +3024,8 @@ test "set: getEvent physical" {
|
||||
test "set: getEvent codepoint" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -3028,6 +3066,8 @@ test "set: getEvent codepoint" {
|
||||
test "set: getEvent codepoint case folding" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
var s: Set = .{};
|
||||
defer s.deinit(alloc);
|
||||
@ -3070,6 +3110,8 @@ test "Action: clone" {
|
||||
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
_ = try @import("../global.zig").Zg.initForTesting();
|
||||
defer zg.deinitForTesting();
|
||||
|
||||
{
|
||||
var a: Action = .ignore;
|
||||
|
@ -1,12 +1,13 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const ziglyph = @import("ziglyph");
|
||||
const font = @import("../font/main.zig");
|
||||
const terminal = @import("../terminal/main.zig");
|
||||
const renderer = @import("../renderer.zig");
|
||||
const shaderpkg = renderer.Renderer.API.shaders;
|
||||
const ArrayListCollection = @import("../datastruct/array_list_collection.zig").ArrayListCollection;
|
||||
const GeneralCategories = @import("GeneralCategories");
|
||||
const zg = &@import("../global.zig").state.zg;
|
||||
|
||||
/// The possible cell content keys that exist.
|
||||
pub const Key = enum {
|
||||
@ -246,8 +247,10 @@ pub fn fgMode(
|
||||
const cell = cell_pin.rowAndCell().cell;
|
||||
const cp = cell.codepoint();
|
||||
|
||||
if (!ziglyph.general_category.isPrivateUse(cp) and
|
||||
!ziglyph.blocks.isDingbats(cp))
|
||||
// If it's not Private Use (Co) or Dingbats (0x2700-0x27bf), use
|
||||
// normal mode.
|
||||
if (GeneralCategories.gc(zg.general_categories, cp) != .Co and
|
||||
!(cp >= 0x2700 and cp <= 0x27bf))
|
||||
{
|
||||
break :text .normal;
|
||||
}
|
||||
@ -278,7 +281,8 @@ pub fn fgMode(
|
||||
// Powerline is whitespace
|
||||
if (isPowerline(prev_cp)) break :prev;
|
||||
|
||||
if (ziglyph.general_category.isPrivateUse(prev_cp)) {
|
||||
// If it's Private Use (Co), then we use constrained mode.
|
||||
if (GeneralCategories.gc(zg.general_categories, cp) == .Co) {
|
||||
break :text .constrained;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ const std = @import("std");
|
||||
extern "c" fn ghostty_simd_codepoint_width(u32) i8;
|
||||
|
||||
pub fn codepointWidth(cp: u32) i8 {
|
||||
//return @import("ziglyph").display_width.codePointWidth(@intCast(cp), .half);
|
||||
// const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
// defer zg.deinitForTesting();
|
||||
// try testing.expectEqual(@as(i8, 1), @import("DisplayWidth").codePointWidth(zg.display_width, @intCast(cp)));
|
||||
return ghostty_simd_codepoint_width(cp);
|
||||
}
|
||||
|
||||
@ -19,27 +21,41 @@ test "codepointWidth basic" {
|
||||
try testing.expectEqual(@as(i8, 2), codepointWidth(0xF900)); // 豈
|
||||
try testing.expectEqual(@as(i8, 2), codepointWidth(0x20000)); // 𠀀
|
||||
try testing.expectEqual(@as(i8, 2), codepointWidth(0x30000)); // 𠀀
|
||||
// try testing.expectEqual(@as(i8, 1), @import("ziglyph").display_width.codePointWidth(0x100, .half));
|
||||
// const zg = try @import("../../global.zig").Zg.initForTesting();
|
||||
// defer zg.deinitForTesting();
|
||||
// try testing.expectEqual(@as(i8, 1), @import("DisplayWidth").codePointWidth(zg.display_width, 0x100));
|
||||
}
|
||||
|
||||
// This is not very fast in debug modes, so its commented by default.
|
||||
// IMPORTANT: UNCOMMENT THIS WHENEVER MAKING CODEPOINTWIDTH CHANGES.
|
||||
// test "codepointWidth matches ziglyph" {
|
||||
// const testing = std.testing;
|
||||
// const ziglyph = @import("ziglyph");
|
||||
//test "codepointWidth matches zg" {
|
||||
// const testing = std.testing;
|
||||
// const DisplayWidth = @import("DisplayWidth");
|
||||
// const display_width = try DisplayWidth.init(std.testing.allocator);
|
||||
// defer display_width.deinit(std.testing.allocator);
|
||||
// var success: bool = true;
|
||||
//
|
||||
// const min = 0xFF + 1; // start outside ascii
|
||||
// for (min..std.math.maxInt(u21)) |cp| {
|
||||
// const simd = codepointWidth(@intCast(cp));
|
||||
// const zg = ziglyph.display_width.codePointWidth(@intCast(cp), .half);
|
||||
// if (simd != zg) mismatch: {
|
||||
// if (cp == 0x2E3B) {
|
||||
// try testing.expectEqual(@as(i8, 2), simd);
|
||||
// break :mismatch;
|
||||
// }
|
||||
// const min = 0xFF + 1; // start outside ascii
|
||||
// for (min..0x110000) |cp| {
|
||||
// const simd = codepointWidth(@intCast(cp));
|
||||
// const zg_width = DisplayWidth.codePointWidth(display_width, @intCast(cp));
|
||||
// if (simd != zg_width) mismatch: {
|
||||
// if (cp == 0x2E3B) {
|
||||
// try testing.expectEqual(@as(i8, 2), simd);
|
||||
// std.log.warn("mismatch for 0x2e3b cp=U+{x} simd={} zg={}", .{ cp, simd, zg_width });
|
||||
// break :mismatch;
|
||||
// }
|
||||
//
|
||||
// std.log.warn("mismatch cp=U+{x} simd={} zg={}", .{ cp, simd, zg });
|
||||
// try testing.expect(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (cp == 0x890) {
|
||||
// try testing.expectEqual(@as(i8, 0), simd);
|
||||
// try testing.expectEqual(@as(i8, 1), zg_width);
|
||||
// break :mismatch;
|
||||
// }
|
||||
//
|
||||
// std.log.warn("mismatch cp=U+{x} simd={} zg={}", .{ cp, simd, zg_width });
|
||||
// success = false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// try testing.expect(success);
|
||||
//}
|
||||
|
@ -344,7 +344,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
||||
// VS15 makes it narrow.
|
||||
if (c == 0xFE0F or c == 0xFE0E) {
|
||||
// This only applies to emoji
|
||||
const prev_props = unicode.getProperties(prev.cell.content.codepoint);
|
||||
const prev_props = unicode.table.get(prev.cell.content.codepoint);
|
||||
const emoji = prev_props.grapheme_boundary_class.isExtendedPictographic();
|
||||
if (!emoji) return;
|
||||
|
||||
@ -416,7 +416,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
||||
const width: usize = if (c <= 0xFF) 1 else @intCast(unicode.table.get(c).width);
|
||||
|
||||
// Note: it is possible to have a width of "3" and a width of "-1"
|
||||
// from ziglyph. We should look into those cases and handle them
|
||||
// from zg. We should look into those cases and handle them
|
||||
// appropriately.
|
||||
assert(width <= 2);
|
||||
// log.debug("c={x} width={}", .{ c, width });
|
||||
@ -452,7 +452,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
||||
|
||||
// If this is a emoji variation selector, prev must be an emoji
|
||||
if (c == 0xFE0F or c == 0xFE0E) {
|
||||
const prev_props = unicode.getProperties(prev.content.codepoint);
|
||||
const prev_props = unicode.table.get(prev.content.codepoint);
|
||||
const emoji = prev_props.grapheme_boundary_class == .extended_pictographic;
|
||||
if (!emoji) return;
|
||||
}
|
||||
|
@ -149,21 +149,19 @@ fn graphemeBreakClass(
|
||||
return true;
|
||||
}
|
||||
|
||||
/// If you build this file as a binary, we will verify the grapheme break
|
||||
/// implementation. This iterates over billions of codepoints so it is
|
||||
/// SLOW. It's not meant to be run in CI, but it's useful for debugging.
|
||||
pub fn main() !void {
|
||||
// This test will verify the grapheme break implementation. This iterates over billions of codepoints so it is SLOW.
|
||||
// It's not meant to be run in CI, but it's useful for debugging.
|
||||
test "grapheme break check against ziglyph" {
|
||||
const ziglyph = @import("ziglyph");
|
||||
|
||||
// Set the min and max to control the test range.
|
||||
const min = 0;
|
||||
const max = std.math.maxInt(u21) + 1;
|
||||
var success: bool = true;
|
||||
|
||||
var state: BreakState = .{};
|
||||
var zg_state: u3 = 0;
|
||||
for (min..max) |cp1| {
|
||||
if (cp1 % 1000 == 0) std.log.warn("progress cp1={}", .{cp1});
|
||||
|
||||
if (cp1 == '\r' or cp1 == '\n' or
|
||||
ziglyph.grapheme_break.isControl(@intCast(cp1))) continue;
|
||||
|
||||
@ -174,6 +172,7 @@ pub fn main() !void {
|
||||
const gb = graphemeBreak(@intCast(cp1), @intCast(cp2), &state);
|
||||
const zg_gb = ziglyph.graphemeBreak(@intCast(cp1), @intCast(cp2), &zg_state);
|
||||
if (gb != zg_gb) {
|
||||
success = false;
|
||||
std.log.warn("cp1={x} cp2={x} gb={} state={} zg_gb={} zg_state={}", .{
|
||||
cp1,
|
||||
cp2,
|
||||
@ -185,6 +184,8 @@ pub fn main() !void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try std.testing.expect(success);
|
||||
}
|
||||
|
||||
pub const std_options = struct {
|
||||
|
@ -11,7 +11,7 @@ const Allocator = std.mem.Allocator;
|
||||
/// can in theory be generated at runtime.
|
||||
///
|
||||
/// Context must have two functions:
|
||||
/// - `get(Context, u21) Elem`: returns the mapping for a given codepoint
|
||||
/// - `get(Context, u21) !Elem`: returns the mapping for a given codepoint
|
||||
/// - `eql(Context, Elem, Elem) bool`: returns true if two mappings are equal
|
||||
///
|
||||
pub fn Generator(
|
||||
|
@ -4,7 +4,6 @@ const grapheme = @import("grapheme.zig");
|
||||
const props = @import("props.zig");
|
||||
pub const table = props.table;
|
||||
pub const Properties = props.Properties;
|
||||
pub const getProperties = props.get;
|
||||
pub const graphemeBreak = grapheme.graphemeBreak;
|
||||
pub const GraphemeBreakState = grapheme.BreakState;
|
||||
|
||||
|
@ -1,9 +1,26 @@
|
||||
const props = @This();
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const ziglyph = @import("ziglyph");
|
||||
const Graphemes = @import("Graphemes");
|
||||
const DisplayWidth = @import("DisplayWidth");
|
||||
const lut = @import("lut.zig");
|
||||
|
||||
graphemes: Graphemes,
|
||||
display_width: DisplayWidth,
|
||||
|
||||
fn init(alloc: std.mem.Allocator) !props {
|
||||
const graphemes = try Graphemes.init(alloc);
|
||||
return .{
|
||||
.graphemes = graphemes,
|
||||
.display_width = try DisplayWidth.initWithGraphemes(alloc, graphemes),
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *props, alloc: std.mem.Allocator) void {
|
||||
self.graphemes.deinit(alloc);
|
||||
self.display_width.deinit(alloc);
|
||||
}
|
||||
|
||||
/// The lookup tables for Ghostty.
|
||||
pub const table = table: {
|
||||
// This is only available after running main() below as part of the Ghostty
|
||||
@ -79,31 +96,26 @@ pub const GraphemeBoundaryClass = enum(u4) {
|
||||
|
||||
/// Gets the grapheme boundary class for a codepoint. This is VERY
|
||||
/// SLOW. The use case for this is only in generating lookup tables.
|
||||
pub fn init(cp: u21) GraphemeBoundaryClass {
|
||||
// We special-case modifier bases because we should not break
|
||||
// if a modifier isn't next to a base.
|
||||
if (ziglyph.emoji.isEmojiModifierBase(cp)) {
|
||||
assert(ziglyph.emoji.isExtendedPictographic(cp));
|
||||
return .extended_pictographic_base;
|
||||
}
|
||||
|
||||
if (ziglyph.emoji.isEmojiModifier(cp)) return .emoji_modifier;
|
||||
if (ziglyph.emoji.isExtendedPictographic(cp)) return .extended_pictographic;
|
||||
if (ziglyph.grapheme_break.isL(cp)) return .L;
|
||||
if (ziglyph.grapheme_break.isV(cp)) return .V;
|
||||
if (ziglyph.grapheme_break.isT(cp)) return .T;
|
||||
if (ziglyph.grapheme_break.isLv(cp)) return .LV;
|
||||
if (ziglyph.grapheme_break.isLvt(cp)) return .LVT;
|
||||
if (ziglyph.grapheme_break.isPrepend(cp)) return .prepend;
|
||||
if (ziglyph.grapheme_break.isExtend(cp)) return .extend;
|
||||
if (ziglyph.grapheme_break.isZwj(cp)) return .zwj;
|
||||
if (ziglyph.grapheme_break.isSpacingmark(cp)) return .spacing_mark;
|
||||
if (ziglyph.grapheme_break.isRegionalIndicator(cp)) return .regional_indicator;
|
||||
|
||||
// This is obviously not INVALID invalid, there is SOME grapheme
|
||||
// boundary class for every codepoint. But we don't care about
|
||||
// anything that doesn't fit into the above categories.
|
||||
return .invalid;
|
||||
pub fn init(ctx: props, cp: u21) GraphemeBoundaryClass {
|
||||
return switch (Graphemes.gbp(ctx.graphemes, cp)) {
|
||||
.Emoji_Modifier_Base => .extended_pictographic_base,
|
||||
.Emoji_Modifier => .emoji_modifier,
|
||||
.Extended_Pictographic => .extended_pictographic,
|
||||
.L => .L,
|
||||
.V => .V,
|
||||
.T => .T,
|
||||
.LV => .LV,
|
||||
.LVT => .LVT,
|
||||
.Prepend => .prepend,
|
||||
.Extend => .extend,
|
||||
.ZWJ => .zwj,
|
||||
.SpacingMark => .spacing_mark,
|
||||
.Regional_Indicator => .regional_indicator,
|
||||
// This is obviously not INVALID invalid, there is SOME grapheme
|
||||
// boundary class for every codepoint. But we don't care about
|
||||
// anything that doesn't fit into the above categories.
|
||||
.none, .Control, .CR, .LF => .invalid,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true if this is an extended pictographic type. This
|
||||
@ -120,13 +132,25 @@ pub const GraphemeBoundaryClass = enum(u4) {
|
||||
}
|
||||
};
|
||||
|
||||
pub fn get(cp: u21) Properties {
|
||||
const zg_width = ziglyph.display_width.codePointWidth(cp, .half);
|
||||
pub fn get(ctx: props, cp: u21) !Properties {
|
||||
if (cp > 0x10FFFF) {
|
||||
return .{
|
||||
.width = 0,
|
||||
.grapheme_boundary_class = .invalid,
|
||||
};
|
||||
} else {
|
||||
const zg_width = DisplayWidth.codePointWidth(ctx.display_width, cp);
|
||||
|
||||
return .{
|
||||
.width = @intCast(@min(2, @max(0, zg_width))),
|
||||
.grapheme_boundary_class = .init(cp),
|
||||
};
|
||||
return .{
|
||||
.width = @intCast(@min(2, @max(0, zg_width))),
|
||||
.grapheme_boundary_class = .init(ctx, cp),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eql(ctx: props, a: Properties, b: Properties) bool {
|
||||
_ = ctx;
|
||||
return a.eql(b);
|
||||
}
|
||||
|
||||
/// Runnable binary to generate the lookup tables and output to stdout.
|
||||
@ -135,20 +159,13 @@ pub fn main() !void {
|
||||
defer arena_state.deinit();
|
||||
const alloc = arena_state.allocator();
|
||||
|
||||
var self = try init(alloc);
|
||||
defer self.deinit(alloc);
|
||||
|
||||
const gen: lut.Generator(
|
||||
Properties,
|
||||
struct {
|
||||
pub fn get(ctx: @This(), cp: u21) !Properties {
|
||||
_ = ctx;
|
||||
return props.get(cp);
|
||||
}
|
||||
|
||||
pub fn eql(ctx: @This(), a: Properties, b: Properties) bool {
|
||||
_ = ctx;
|
||||
return a.eql(b);
|
||||
}
|
||||
},
|
||||
) = .{};
|
||||
props,
|
||||
) = .{ .ctx = self };
|
||||
|
||||
const t = try gen.generate(alloc);
|
||||
defer alloc.free(t.stage1);
|
||||
@ -166,16 +183,19 @@ pub fn main() !void {
|
||||
|
||||
// This is not very fast in debug modes, so its commented by default.
|
||||
// IMPORTANT: UNCOMMENT THIS WHENEVER MAKING CODEPOINTWIDTH CHANGES.
|
||||
// test "tables match ziglyph" {
|
||||
// const testing = std.testing;
|
||||
//test "tables match zg" {
|
||||
// const testing = std.testing;
|
||||
//
|
||||
// const min = 0xFF + 1; // start outside ascii
|
||||
// for (min..std.math.maxInt(u21)) |cp| {
|
||||
// const t = table.get(@intCast(cp));
|
||||
// const zg = @min(2, @max(0, ziglyph.display_width.codePointWidth(@intCast(cp), .half)));
|
||||
// if (t.width != zg) {
|
||||
// std.log.warn("mismatch cp=U+{x} t={} zg={}", .{ cp, t, zg });
|
||||
// try testing.expect(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// const display_width = try DisplayWidth.init(std.testing.allocator);
|
||||
// defer display_width.deinit(std.testing.allocator);
|
||||
//
|
||||
// const min = 0xFF + 1; // start outside ascii
|
||||
// for (min..0x110000) |cp| {
|
||||
// const t = table.get(@intCast(cp));
|
||||
// const zg = @min(2, @max(0, DisplayWidth.codePointWidth(display_width, @intCast(cp))));
|
||||
// if (t.width != zg) {
|
||||
// std.log.warn("mismatch cp=U+{x} t={} zg={}", .{ cp, t, zg });
|
||||
// try testing.expect(false);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
Reference in New Issue
Block a user