zg: complete replacement of ziglyph with zg
This commit is contained in:
parent
9fec6f122b
commit
8a71cd4c85
11 changed files with 88 additions and 79 deletions
10
build.zig
10
build.zig
|
@ -6,10 +6,6 @@ pub fn build(b: *std.Build) void {
|
|||
const root_source_file = b.path("src/main.zig");
|
||||
|
||||
// Dependencies
|
||||
const ziglyph_dep = b.dependency("ziglyph", .{
|
||||
.optimize = optimize,
|
||||
.target = target,
|
||||
});
|
||||
const zg_dep = b.dependency("zg", .{
|
||||
.optimize = optimize,
|
||||
.target = target,
|
||||
|
@ -33,8 +29,9 @@ pub fn build(b: *std.Build) void {
|
|||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
vaxis_mod.addImport("ziglyph", ziglyph_dep.module("ziglyph"));
|
||||
vaxis_mod.addImport("code_point", zg_dep.module("code_point"));
|
||||
vaxis_mod.addImport("grapheme", zg_dep.module("grapheme"));
|
||||
vaxis_mod.addImport("DisplayWidth", zg_dep.module("DisplayWidth"));
|
||||
vaxis_mod.addImport("zigimg", zigimg_dep.module("zigimg"));
|
||||
vaxis_mod.addImport("gap_buffer", gap_buffer_dep.module("gap_buffer"));
|
||||
vaxis_mod.addImport("znvim", znvim_dep.module("znvim"));
|
||||
|
@ -71,8 +68,9 @@ pub fn build(b: *std.Build) void {
|
|||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
tests.root_module.addImport("ziglyph", ziglyph_dep.module("ziglyph"));
|
||||
tests.root_module.addImport("code_point", zg_dep.module("code_point"));
|
||||
tests.root_module.addImport("grapheme", zg_dep.module("grapheme"));
|
||||
tests.root_module.addImport("DisplayWidth", zg_dep.module("DisplayWidth"));
|
||||
tests.root_module.addImport("zigimg", zigimg_dep.module("zigimg"));
|
||||
tests.root_module.addImport("gap_buffer", gap_buffer_dep.module("gap_buffer"));
|
||||
tests.root_module.addImport("znvim", znvim_dep.module("znvim"));
|
||||
|
|
|
@ -4,13 +4,9 @@
|
|||
.paths = .{""},
|
||||
.minimum_zig_version = "0.12.0-dev.3397+43edd53c3",
|
||||
.dependencies = .{
|
||||
.ziglyph = .{
|
||||
.url = "git+https://codeberg.org/dude_the_builder/ziglyph/#947ed39203bf90412e3d16cbcf936518b6f23af0",
|
||||
.hash = "12208b23d1eb6dcb929e85346524db8f8b8aa1401bdf8a97dee1e0cfb55da8d5fb42",
|
||||
},
|
||||
.zigimg = .{
|
||||
.url = "git+https://github.com/zigimg/zigimg#8873f29fc449e1b63400e9f4ad86d3c76204f962",
|
||||
.hash = "122019f6439545235af116d0d8eb81fde1ff05fdb070da57c723772c557f84c5bf39",
|
||||
.url = "git+https://github.com/zigimg/zigimg#637974e2d31dcdbc33f1e9cc8ffb2e46abd2e215",
|
||||
.hash = "122012026c3a65ff1d4acba3b3fe80785f7cee9c6b4cdaff7ed0fbf23b0a6c803989",
|
||||
},
|
||||
.gap_buffer = .{
|
||||
.url = "https://github.com/ryleelyman/GapBuffer.zig/archive/6a746497d5a2494026d0f471e42556f1f153f153.tar.gz",
|
||||
|
|
18
src/Key.zig
18
src/Key.zig
|
@ -1,6 +1,5 @@
|
|||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const ziglyph = @import("ziglyph");
|
||||
|
||||
const Key = @This();
|
||||
|
||||
|
@ -101,25 +100,12 @@ pub fn matchText(self: Key, cp: u21, mods: Modifiers) bool {
|
|||
|
||||
var self_mods = self.mods;
|
||||
self_mods.num_lock = false;
|
||||
self_mods.shift = false;
|
||||
self_mods.caps_lock = false;
|
||||
var arg_mods = mods;
|
||||
arg_mods.num_lock = false;
|
||||
var code = cp;
|
||||
// if the passed codepoint is upper, we consume all shift and caps mods for
|
||||
// checking
|
||||
if (ziglyph.isUpper(cp)) {
|
||||
// consume mods
|
||||
self_mods.shift = false;
|
||||
self_mods.caps_lock = false;
|
||||
arg_mods.shift = false;
|
||||
arg_mods.caps_lock = false;
|
||||
} else if (mods.shift or mods.caps_lock) {
|
||||
// uppercase the cp and consume all mods
|
||||
code = ziglyph.toUpper(cp);
|
||||
self_mods.shift = false;
|
||||
self_mods.caps_lock = false;
|
||||
arg_mods.shift = false;
|
||||
arg_mods.caps_lock = false;
|
||||
}
|
||||
|
||||
var buf: [4]u8 = undefined;
|
||||
const n = std.unicode.utf8Encode(cp, buf[0..]) catch return false;
|
||||
|
|
|
@ -20,7 +20,12 @@ pub fn Loop(comptime T: type) type {
|
|||
pub fn run(self: *Self) !void {
|
||||
if (self.thread) |_| return;
|
||||
if (self.vaxis.tty == null) self.vaxis.tty = try Tty.init();
|
||||
self.thread = try std.Thread.spawn(.{}, Tty.run, .{ &self.vaxis.tty.?, T, self });
|
||||
self.thread = try std.Thread.spawn(.{}, Tty.run, .{
|
||||
&self.vaxis.tty.?,
|
||||
T,
|
||||
self,
|
||||
&self.vaxis.unicode.grapheme_data,
|
||||
});
|
||||
}
|
||||
|
||||
/// stops reading from the tty and returns it to it's initial state
|
||||
|
|
|
@ -3,8 +3,8 @@ const testing = std.testing;
|
|||
const Event = @import("event.zig").Event;
|
||||
const Key = @import("Key.zig");
|
||||
const Mouse = @import("Mouse.zig");
|
||||
const CodePointIterator = @import("ziglyph").CodePointIterator;
|
||||
const graphemeBreak = @import("ziglyph").graphemeBreak;
|
||||
const code_point = @import("code_point");
|
||||
const grapheme = @import("grapheme");
|
||||
|
||||
const log = std.log.scoped(.parser);
|
||||
|
||||
|
@ -59,6 +59,8 @@ const State = enum {
|
|||
// text-as-codepoints
|
||||
buf: [128]u8 = undefined,
|
||||
|
||||
grapheme_data: *const grapheme.GraphemeData,
|
||||
|
||||
pub fn parse(self: *Parser, input: []const u8) !Result {
|
||||
const n = input.len;
|
||||
|
||||
|
@ -104,15 +106,15 @@ pub fn parse(self: *Parser, input: []const u8) !Result {
|
|||
},
|
||||
0x7F => .{ .codepoint = Key.backspace },
|
||||
else => blk: {
|
||||
var iter: CodePointIterator = .{ .bytes = input[i..] };
|
||||
var iter: code_point.Iterator = .{ .bytes = input[i..] };
|
||||
// return null if we don't have a valid codepoint
|
||||
var cp = iter.next() orelse return .{ .event = null, .n = 0 };
|
||||
|
||||
var code = cp.code;
|
||||
i += cp.len - 1; // subtract one for the loop iter
|
||||
var g_state: u3 = 0;
|
||||
var g_state: grapheme.State = .{};
|
||||
while (iter.next()) |next_cp| {
|
||||
if (graphemeBreak(cp.code, next_cp.code, &g_state)) {
|
||||
if (grapheme.graphemeBreak(cp.code, next_cp.code, self.grapheme_data, &g_state)) {
|
||||
break;
|
||||
}
|
||||
code = Key.multicodepoint;
|
||||
|
|
|
@ -5,6 +5,7 @@ const Loop = @import("Loop.zig").Loop;
|
|||
const Parser = @import("Parser.zig");
|
||||
const GraphemeCache = @import("GraphemeCache.zig");
|
||||
const ctlseqs = @import("ctlseqs.zig");
|
||||
const grapheme = @import("grapheme");
|
||||
|
||||
const log = std.log.scoped(.tty);
|
||||
|
||||
|
@ -58,6 +59,7 @@ pub fn run(
|
|||
self: *Tty,
|
||||
comptime Event: type,
|
||||
loop: *Loop(Event),
|
||||
grapheme_data: *const grapheme.GraphemeData,
|
||||
) !void {
|
||||
|
||||
// get our initial winsize
|
||||
|
@ -105,7 +107,9 @@ pub fn run(
|
|||
// initialize a grapheme cache
|
||||
var cache: GraphemeCache = .{};
|
||||
|
||||
var parser: Parser = .{};
|
||||
var parser: Parser = .{
|
||||
.grapheme_data = grapheme_data,
|
||||
};
|
||||
|
||||
// initialize the read buffer
|
||||
var buf: [1024]u8 = undefined;
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
const std = @import("std");
|
||||
const grapheme = @import("grapheme");
|
||||
const DisplayWidth = @import("DisplayWidth");
|
||||
|
||||
/// A thin wrapper around zg data
|
||||
const Unicode = @This();
|
||||
|
||||
grapheme_data: grapheme.GraphemeData,
|
||||
width_data: DisplayWidth.DisplayWidthData,
|
||||
|
||||
/// initialize all unicode data vaxis may possibly need
|
||||
pub fn init(alloc: std.mem.Allocator) !Unicode {
|
||||
const grapheme_data = try grapheme.GraphemeData.init(alloc);
|
||||
|
||||
return .{
|
||||
.grapheme_data = grapheme_data,
|
||||
.grapheme_data = try grapheme.GraphemeData.init(alloc),
|
||||
.width_data = try DisplayWidth.DisplayWidthData.init(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
/// free all data
|
||||
pub fn deinit(self: *Unicode) void {
|
||||
pub fn deinit(self: *const Unicode) void {
|
||||
self.grapheme_data.deinit();
|
||||
self.width_data.deinit();
|
||||
}
|
||||
|
||||
/// creates a grapheme iterator based on str
|
||||
|
|
|
@ -261,7 +261,7 @@ pub fn render(self: *Vaxis) !void {
|
|||
if (cell.char.width != 0) break :blk cell.char.width;
|
||||
|
||||
const method: gwidth.Method = self.caps.unicode;
|
||||
const width = gwidth.gwidth(cell.char.grapheme, method) catch 1;
|
||||
const width = gwidth.gwidth(cell.char.grapheme, method, &self.unicode.width_data) catch 1;
|
||||
break :blk @max(1, width);
|
||||
};
|
||||
std.debug.assert(w > 0);
|
||||
|
|
|
@ -4,6 +4,7 @@ const Screen = @import("Screen.zig");
|
|||
const Cell = @import("Cell.zig");
|
||||
const Mouse = @import("Mouse.zig");
|
||||
const Segment = @import("Cell.zig").Segment;
|
||||
const Unicode = @import("Unicode.zig");
|
||||
const gw = @import("gwidth.zig");
|
||||
|
||||
const log = std.log.scoped(.window);
|
||||
|
@ -200,7 +201,7 @@ pub fn clear(self: Window) void {
|
|||
|
||||
/// returns the width of the grapheme. This depends on the terminal capabilities
|
||||
pub fn gwidth(self: Window, str: []const u8) usize {
|
||||
return gw.gwidth(str, self.screen.width_method) catch 1;
|
||||
return gw.gwidth(str, self.screen.width_method, &self.screen.unicode.width_data) catch 1;
|
||||
}
|
||||
|
||||
/// fills the window with the provided cell
|
||||
|
@ -270,7 +271,7 @@ pub fn print(self: Window, segments: []Segment, opts: PrintOptions) !PrintResult
|
|||
var iter = self.screen.unicode.graphemeIterator(segment.text);
|
||||
while (iter.next()) |grapheme| {
|
||||
if (row >= self.height) break :blk true;
|
||||
const s = grapheme.slice(segment.text);
|
||||
const s = grapheme.bytes(segment.text);
|
||||
if (std.mem.eql(u8, s, "\n")) {
|
||||
row += 1;
|
||||
col = 0;
|
||||
|
@ -350,9 +351,9 @@ pub fn print(self: Window, segments: []Segment, opts: PrintOptions) !PrintResult
|
|||
else
|
||||
word;
|
||||
defer soft_wrapped = false;
|
||||
var iter = self.screen.unicode.graphemeIterator(segment.text);
|
||||
var iter = self.screen.unicode.graphemeIterator(printed_word);
|
||||
while (iter.next()) |grapheme| {
|
||||
const s = grapheme.slice(printed_word);
|
||||
const s = grapheme.bytes(printed_word);
|
||||
const w = self.gwidth(s);
|
||||
if (opts.commit) self.writeCell(col, row, .{
|
||||
.char = .{
|
||||
|
@ -405,7 +406,7 @@ pub fn print(self: Window, segments: []Segment, opts: PrintOptions) !PrintResult
|
|||
var iter = self.screen.unicode.graphemeIterator(segment.text);
|
||||
while (iter.next()) |grapheme| {
|
||||
if (col >= self.width) break :blk true;
|
||||
const s = grapheme.slice(segment.text);
|
||||
const s = grapheme.bytes(segment.text);
|
||||
if (std.mem.eql(u8, s, "\n")) break :blk true;
|
||||
const w = self.gwidth(s);
|
||||
if (w == 0) continue;
|
||||
|
@ -521,9 +522,10 @@ test "Window size nested offsets" {
|
|||
}
|
||||
|
||||
test "print: grapheme" {
|
||||
var screen: Screen = .{
|
||||
.unicode = true,
|
||||
};
|
||||
const alloc = std.testing.allocator_instance.allocator();
|
||||
const unicode = try Unicode.init(alloc);
|
||||
defer unicode.deinit();
|
||||
var screen: Screen = .{ .width_method = .unicode, .unicode = &unicode };
|
||||
const win: Window = .{
|
||||
.x_off = 0,
|
||||
.y_off = 0,
|
||||
|
@ -584,8 +586,12 @@ test "print: grapheme" {
|
|||
}
|
||||
|
||||
test "print: word" {
|
||||
const alloc = std.testing.allocator_instance.allocator();
|
||||
const unicode = try Unicode.init(alloc);
|
||||
defer unicode.deinit();
|
||||
var screen: Screen = .{
|
||||
.unicode = true,
|
||||
.width_method = .unicode,
|
||||
.unicode = &unicode,
|
||||
};
|
||||
const win: Window = .{
|
||||
.x_off = 0,
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
const std = @import("std");
|
||||
const unicode = std.unicode;
|
||||
const testing = std.testing;
|
||||
const ziglyph = @import("ziglyph");
|
||||
const DisplayWidth = @import("DisplayWidth");
|
||||
const code_point = @import("code_point");
|
||||
|
||||
/// the method to use when calculating the width of a grapheme
|
||||
pub const Method = enum {
|
||||
|
@ -11,18 +12,22 @@ pub const Method = enum {
|
|||
};
|
||||
|
||||
/// returns the width of the provided string, as measured by the method chosen
|
||||
pub fn gwidth(str: []const u8, method: Method) !usize {
|
||||
pub fn gwidth(str: []const u8, method: Method, data: *const DisplayWidth.DisplayWidthData) !usize {
|
||||
switch (method) {
|
||||
.unicode => {
|
||||
return try ziglyph.display_width.strWidth(str, .half);
|
||||
const dw: DisplayWidth = .{ .data = data };
|
||||
return dw.strWidth(str);
|
||||
},
|
||||
.wcwidth => {
|
||||
var total: usize = 0;
|
||||
const utf8 = try unicode.Utf8View.init(str);
|
||||
var iter = utf8.iterator();
|
||||
|
||||
while (iter.nextCodepoint()) |cp| {
|
||||
const w = ziglyph.display_width.codePointWidth(cp, .half);
|
||||
var iter: code_point.Iterator = .{ .bytes = str };
|
||||
while (iter.next()) |cp| {
|
||||
const w = switch (cp.code) {
|
||||
// undo an override in zg for emoji skintone selectors
|
||||
0x1f3fb...0x1f3ff,
|
||||
=> 2,
|
||||
else => data.codePointWidth(cp.code),
|
||||
};
|
||||
if (w < 0) continue;
|
||||
total += @intCast(w);
|
||||
}
|
||||
|
@ -33,37 +38,43 @@ pub fn gwidth(str: []const u8, method: Method) !usize {
|
|||
if (str.len > out.len) return error.OutOfMemory;
|
||||
const n = std.mem.replacementSize(u8, str, "\u{200D}", "");
|
||||
_ = std.mem.replace(u8, str, "\u{200D}", "", &out);
|
||||
return gwidth(out[0..n], .unicode);
|
||||
return gwidth(out[0..n], .unicode, data);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test "gwidth: a" {
|
||||
try testing.expectEqual(1, try gwidth("a", .unicode));
|
||||
try testing.expectEqual(1, try gwidth("a", .wcwidth));
|
||||
try testing.expectEqual(1, try gwidth("a", .no_zwj));
|
||||
const alloc = testing.allocator_instance.allocator();
|
||||
const data = try DisplayWidth.DisplayWidthData.init(alloc);
|
||||
defer data.deinit();
|
||||
try testing.expectEqual(1, try gwidth("a", .unicode, &data));
|
||||
try testing.expectEqual(1, try gwidth("a", .wcwidth, &data));
|
||||
try testing.expectEqual(1, try gwidth("a", .no_zwj, &data));
|
||||
}
|
||||
|
||||
test "gwidth: emoji with ZWJ" {
|
||||
try testing.expectEqual(2, try gwidth("👩🚀", .unicode));
|
||||
try testing.expectEqual(4, try gwidth("👩🚀", .wcwidth));
|
||||
try testing.expectEqual(4, try gwidth("👩🚀", .no_zwj));
|
||||
const alloc = testing.allocator_instance.allocator();
|
||||
const data = try DisplayWidth.DisplayWidthData.init(alloc);
|
||||
defer data.deinit();
|
||||
try testing.expectEqual(2, try gwidth("👩🚀", .unicode, &data));
|
||||
try testing.expectEqual(4, try gwidth("👩🚀", .wcwidth, &data));
|
||||
try testing.expectEqual(4, try gwidth("👩🚀", .no_zwj, &data));
|
||||
}
|
||||
|
||||
test "gwidth: emoji with VS16 selector" {
|
||||
try testing.expectEqual(2, try gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .unicode));
|
||||
try testing.expectEqual(1, try gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .wcwidth));
|
||||
try testing.expectEqual(2, try gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .no_zwj));
|
||||
const alloc = testing.allocator_instance.allocator();
|
||||
const data = try DisplayWidth.DisplayWidthData.init(alloc);
|
||||
defer data.deinit();
|
||||
try testing.expectEqual(2, try gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .unicode, &data));
|
||||
try testing.expectEqual(1, try gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .wcwidth, &data));
|
||||
try testing.expectEqual(2, try gwidth("\xE2\x9D\xA4\xEF\xB8\x8F", .no_zwj, &data));
|
||||
}
|
||||
|
||||
test "gwidth: emoji with skin tone selector" {
|
||||
try testing.expectEqual(2, try gwidth("👋🏿", .unicode));
|
||||
try testing.expectEqual(4, try gwidth("👋🏿", .wcwidth));
|
||||
try testing.expectEqual(2, try gwidth("👋🏿", .no_zwj));
|
||||
}
|
||||
|
||||
test "gwidth: invalid string" {
|
||||
try testing.expectError(error.InvalidUtf8, gwidth("\xc3\x28", .unicode));
|
||||
try testing.expectError(error.InvalidUtf8, gwidth("\xc3\x28", .wcwidth));
|
||||
try testing.expectError(error.InvalidUtf8, gwidth("\xc3\x28", .no_zwj));
|
||||
const alloc = testing.allocator_instance.allocator();
|
||||
const data = try DisplayWidth.DisplayWidthData.init(alloc);
|
||||
defer data.deinit();
|
||||
try testing.expectEqual(2, try gwidth("👋🏿", .unicode, &data));
|
||||
try testing.expectEqual(4, try gwidth("👋🏿", .wcwidth, &data));
|
||||
try testing.expectEqual(2, try gwidth("👋🏿", .no_zwj, &data));
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ pub const AllocatingScreen = @import("InternalScreen.zig");
|
|||
pub const Winsize = @import("Tty.zig").Winsize;
|
||||
pub const Window = @import("Window.zig");
|
||||
|
||||
pub const ziglyph = @import("ziglyph");
|
||||
pub const widgets = @import("widgets.zig");
|
||||
pub const gwidth = @import("gwidth.zig");
|
||||
|
||||
|
|
Loading…
Reference in a new issue