core: replace ziglyph.GraphemeIterator with zg version
ziglyph is being replaced by zg. Replace all calls to ziglyph grapheme iterator with the zg version
This commit is contained in:
parent
e7915b5dd7
commit
9fec6f122b
10 changed files with 83 additions and 39 deletions
|
@ -10,6 +10,10 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
.target = target,
|
||||
});
|
||||
const zg_dep = b.dependency("zg", .{
|
||||
.optimize = optimize,
|
||||
.target = target,
|
||||
});
|
||||
const zigimg_dep = b.dependency("zigimg", .{
|
||||
.optimize = optimize,
|
||||
.target = target,
|
||||
|
@ -30,6 +34,7 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
vaxis_mod.addImport("ziglyph", ziglyph_dep.module("ziglyph"));
|
||||
vaxis_mod.addImport("grapheme", zg_dep.module("grapheme"));
|
||||
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"));
|
||||
|
@ -67,6 +72,7 @@ pub fn build(b: *std.Build) void {
|
|||
.optimize = optimize,
|
||||
});
|
||||
tests.root_module.addImport("ziglyph", ziglyph_dep.module("ziglyph"));
|
||||
tests.root_module.addImport("grapheme", zg_dep.module("grapheme"));
|
||||
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"));
|
||||
|
|
|
@ -20,5 +20,9 @@
|
|||
.url = "git+https://github.com/jinzhongjia/znvim#7927b8042872d5fa5f30862302bea1290d372d4f",
|
||||
.hash = "12202372c2043a9ac557144d327c09638ccd8d615bba459ba17d1a7a4197a213d939",
|
||||
},
|
||||
.zg = .{
|
||||
.url = "git+https://codeberg.org/dude_the_builder/zg#16735685fcc3410de361ba3411788ad1fb4fe188",
|
||||
.hash = "1220fe9ac5cdb41833d327a78745614e67d472469f8666567bd8cf9f5847a52b1c51",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ pub fn main() !void {
|
|||
const alloc = gpa.allocator();
|
||||
|
||||
// Initialize Vaxis
|
||||
var vx = try vaxis.init(.{});
|
||||
var vx = try vaxis.init(alloc, .{});
|
||||
// deinit takes an optional allocator. If your program is exiting, you can
|
||||
// choose to pass a null allocator to save some exit time.
|
||||
defer vx.deinit(alloc);
|
||||
|
@ -53,7 +53,7 @@ pub fn main() !void {
|
|||
|
||||
// init our text input widget. The text input widget needs an allocator to
|
||||
// store the contents of the input
|
||||
var text_input = TextInput.init(alloc);
|
||||
var text_input = TextInput.init(alloc, &vx.unicode);
|
||||
defer text_input.deinit();
|
||||
|
||||
// Sends queries to terminal to detect certain features. This should
|
||||
|
|
|
@ -5,6 +5,8 @@ const Cell = @import("Cell.zig");
|
|||
const Shape = @import("Mouse.zig").Shape;
|
||||
const Image = @import("Image.zig");
|
||||
const Winsize = @import("Tty.zig").Winsize;
|
||||
const Unicode = @import("Unicode.zig");
|
||||
const Method = @import("gwidth.zig").Method;
|
||||
|
||||
const log = std.log.scoped(.screen);
|
||||
|
||||
|
@ -22,13 +24,14 @@ cursor_row: usize = 0,
|
|||
cursor_col: usize = 0,
|
||||
cursor_vis: bool = false,
|
||||
|
||||
/// true when we measure cells with unicode
|
||||
unicode: bool = false,
|
||||
unicode: *const Unicode = undefined,
|
||||
|
||||
width_method: Method = .wcwidth,
|
||||
|
||||
mouse_shape: Shape = .default,
|
||||
cursor_shape: Cell.CursorShape = .default,
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator, winsize: Winsize) !Screen {
|
||||
pub fn init(alloc: std.mem.Allocator, winsize: Winsize, unicode: *const Unicode) !Screen {
|
||||
const w = winsize.cols;
|
||||
const h = winsize.rows;
|
||||
var self = Screen{
|
||||
|
@ -37,6 +40,7 @@ pub fn init(alloc: std.mem.Allocator, winsize: Winsize) !Screen {
|
|||
.height = h,
|
||||
.width_pix = winsize.x_pixel,
|
||||
.height_pix = winsize.y_pixel,
|
||||
.unicode = unicode,
|
||||
};
|
||||
for (self.buf, 0..) |_, i| {
|
||||
self.buf[i] = .{};
|
||||
|
|
|
@ -175,8 +175,8 @@ pub fn run(
|
|||
},
|
||||
.cap_unicode => {
|
||||
log.info("unicode capability detected", .{});
|
||||
loop.vaxis.caps.unicode = true;
|
||||
loop.vaxis.screen.unicode = true;
|
||||
loop.vaxis.caps.unicode = .unicode;
|
||||
loop.vaxis.screen.width_method = .unicode;
|
||||
},
|
||||
.cap_da1 => {
|
||||
std.Thread.Futex.wake(&loop.vaxis.query_futex, 10);
|
||||
|
|
26
src/Unicode.zig
Normal file
26
src/Unicode.zig
Normal file
|
@ -0,0 +1,26 @@
|
|||
const std = @import("std");
|
||||
const grapheme = @import("grapheme");
|
||||
|
||||
/// A thin wrapper around zg data
|
||||
const Unicode = @This();
|
||||
|
||||
grapheme_data: grapheme.GraphemeData,
|
||||
|
||||
/// 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,
|
||||
};
|
||||
}
|
||||
|
||||
/// free all data
|
||||
pub fn deinit(self: *Unicode) void {
|
||||
self.grapheme_data.deinit();
|
||||
}
|
||||
|
||||
/// creates a grapheme iterator based on str
|
||||
pub fn graphemeIterator(self: *const Unicode, str: []const u8) grapheme.Iterator {
|
||||
return grapheme.Iterator.init(str, &self.grapheme_data);
|
||||
}
|
|
@ -29,7 +29,7 @@ pub const Capabilities = struct {
|
|||
kitty_keyboard: bool = false,
|
||||
kitty_graphics: bool = false,
|
||||
rgb: bool = false,
|
||||
unicode: bool = false,
|
||||
unicode: gwidth.Method = .wcwidth,
|
||||
};
|
||||
|
||||
pub const Options = struct {};
|
||||
|
@ -63,18 +63,21 @@ query_futex: atomic.Value(u32) = atomic.Value(u32).init(0),
|
|||
// images
|
||||
next_img_id: u32 = 1,
|
||||
|
||||
unicode: Unicode,
|
||||
|
||||
// statistics
|
||||
renders: usize = 0,
|
||||
render_dur: i128 = 0,
|
||||
render_timer: std.time.Timer,
|
||||
|
||||
/// Initialize Vaxis with runtime options
|
||||
pub fn init(_: Options) !Vaxis {
|
||||
pub fn init(alloc: std.mem.Allocator, _: Options) !Vaxis {
|
||||
return .{
|
||||
.tty = null,
|
||||
.screen = .{},
|
||||
.screen_last = .{},
|
||||
.render_timer = try std.time.Timer.start(),
|
||||
.unicode = try Unicode.init(alloc),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -111,6 +114,7 @@ pub fn deinit(self: *Vaxis, alloc: ?std.mem.Allocator) void {
|
|||
log.debug("total renders = {d}", .{self.renders});
|
||||
log.debug("microseconds per render = {d}", .{tpr});
|
||||
}
|
||||
self.unicode.deinit();
|
||||
}
|
||||
|
||||
/// resize allocates a slice of cells equal to the number of cells
|
||||
|
@ -119,8 +123,8 @@ pub fn deinit(self: *Vaxis, alloc: ?std.mem.Allocator) void {
|
|||
pub fn resize(self: *Vaxis, alloc: std.mem.Allocator, winsize: Winsize) !void {
|
||||
log.debug("resizing screen: width={d} height={d}", .{ winsize.cols, winsize.rows });
|
||||
self.screen.deinit(alloc);
|
||||
self.screen = try Screen.init(alloc, winsize);
|
||||
self.screen.unicode = self.caps.unicode;
|
||||
self.screen = try Screen.init(alloc, winsize, &self.unicode);
|
||||
self.screen.width_method = self.caps.unicode;
|
||||
// try self.screen.int(alloc, winsize.cols, winsize.rows);
|
||||
// we only init our current screen. This has the effect of redrawing
|
||||
// every cell
|
||||
|
@ -201,7 +205,7 @@ pub fn queryTerminal(self: *Vaxis) !void {
|
|||
if (self.caps.kitty_keyboard) {
|
||||
try self.enableKittyKeyboard(.{});
|
||||
}
|
||||
if (self.caps.unicode) {
|
||||
if (self.caps.unicode == .unicode) {
|
||||
_ = try tty.write(ctlseqs.unicode_set);
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +260,7 @@ pub fn render(self: *Vaxis) !void {
|
|||
const w = blk: {
|
||||
if (cell.char.width != 0) break :blk cell.char.width;
|
||||
|
||||
const method: gwidth.Method = if (self.caps.unicode) .unicode else .wcwidth;
|
||||
const method: gwidth.Method = self.caps.unicode;
|
||||
const width = gwidth.gwidth(cell.char.grapheme, method) catch 1;
|
||||
break :blk @max(1, width);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
const std = @import("std");
|
||||
const ziglyph = @import("ziglyph");
|
||||
const GraphemeIterator = ziglyph.GraphemeIterator;
|
||||
|
||||
const Screen = @import("Screen.zig");
|
||||
const Cell = @import("Cell.zig");
|
||||
|
@ -202,8 +200,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 {
|
||||
const m: gw.Method = if (self.screen.unicode) .unicode else .wcwidth;
|
||||
return gw.gwidth(str, m) catch 1;
|
||||
return gw.gwidth(str, self.screen.width_method) catch 1;
|
||||
}
|
||||
|
||||
/// fills the window with the provided cell
|
||||
|
@ -270,7 +267,7 @@ pub fn print(self: Window, segments: []Segment, opts: PrintOptions) !PrintResult
|
|||
.grapheme => {
|
||||
var col: usize = 0;
|
||||
const overflow: bool = blk: for (segments) |segment| {
|
||||
var iter = GraphemeIterator.init(segment.text);
|
||||
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);
|
||||
|
@ -353,7 +350,7 @@ pub fn print(self: Window, segments: []Segment, opts: PrintOptions) !PrintResult
|
|||
else
|
||||
word;
|
||||
defer soft_wrapped = false;
|
||||
var iter = GraphemeIterator.init(printed_word);
|
||||
var iter = self.screen.unicode.graphemeIterator(segment.text);
|
||||
while (iter.next()) |grapheme| {
|
||||
const s = grapheme.slice(printed_word);
|
||||
const w = self.gwidth(s);
|
||||
|
@ -405,7 +402,7 @@ pub fn print(self: Window, segments: []Segment, opts: PrintOptions) !PrintResult
|
|||
.none => {
|
||||
var col: usize = 0;
|
||||
const overflow: bool = blk: for (segments) |segment| {
|
||||
var iter = GraphemeIterator.init(segment.text);
|
||||
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);
|
||||
|
|
|
@ -22,8 +22,8 @@ pub const widgets = @import("widgets.zig");
|
|||
pub const gwidth = @import("gwidth.zig");
|
||||
|
||||
/// Initialize a Vaxis application.
|
||||
pub fn init(opts: Vaxis.Options) !Vaxis {
|
||||
return Vaxis.init(opts);
|
||||
pub fn init(alloc: std.mem.Allocator, opts: Vaxis.Options) !Vaxis {
|
||||
return Vaxis.init(alloc, opts);
|
||||
}
|
||||
|
||||
test {
|
||||
|
|
|
@ -3,8 +3,8 @@ const assert = std.debug.assert;
|
|||
const Key = @import("../Key.zig");
|
||||
const Cell = @import("../Cell.zig");
|
||||
const Window = @import("../Window.zig");
|
||||
const GraphemeIterator = @import("ziglyph").GraphemeIterator;
|
||||
const GapBuffer = @import("gap_buffer").GapBuffer;
|
||||
const Unicode = @import("../Unicode.zig");
|
||||
|
||||
const log = std.log.scoped(.text_input);
|
||||
|
||||
|
@ -31,9 +31,12 @@ prev_cursor_idx: usize = 0,
|
|||
/// approximate distance from an edge before we scroll
|
||||
scroll_offset: usize = 4,
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) TextInput {
|
||||
unicode: *const Unicode,
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator, unicode: *const Unicode) TextInput {
|
||||
return TextInput{
|
||||
.buf = GapBuffer(u8).init(alloc),
|
||||
.unicode = unicode,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -73,7 +76,7 @@ pub fn update(self: *TextInput, event: Event) !void {
|
|||
|
||||
/// insert text at the cursor position
|
||||
pub fn insertSliceAtCursor(self: *TextInput, data: []const u8) !void {
|
||||
var iter = GraphemeIterator.init(data);
|
||||
var iter = self.unicode.graphemeIterator(data);
|
||||
var byte_offset_to_cursor = self.byteOffsetToCursor();
|
||||
while (iter.next()) |text| {
|
||||
try self.buf.insertSliceBefore(byte_offset_to_cursor, text.slice(data));
|
||||
|
@ -101,7 +104,7 @@ pub fn sliceToCursor(self: *TextInput, buf: []u8) []const u8 {
|
|||
/// calculates the display width from the draw_offset to the cursor
|
||||
fn widthToCursor(self: *TextInput, win: Window) usize {
|
||||
var width: usize = 0;
|
||||
var first_iter = GraphemeIterator.init(self.buf.items);
|
||||
var first_iter = self.unicode.graphemeIterator(self.buf.items);
|
||||
var i: usize = 0;
|
||||
while (first_iter.next()) |grapheme| {
|
||||
defer i += 1;
|
||||
|
@ -109,18 +112,18 @@ fn widthToCursor(self: *TextInput, win: Window) usize {
|
|||
continue;
|
||||
}
|
||||
if (i == self.cursor_idx) return width;
|
||||
const g = grapheme.slice(self.buf.items);
|
||||
const g = grapheme.bytes(self.buf.items);
|
||||
width += win.gwidth(g);
|
||||
}
|
||||
const second_half = self.buf.secondHalf();
|
||||
var second_iter = GraphemeIterator.init(second_half);
|
||||
var second_iter = self.unicode.graphemeIterator(second_half);
|
||||
while (second_iter.next()) |grapheme| {
|
||||
defer i += 1;
|
||||
if (i < self.draw_offset) {
|
||||
continue;
|
||||
}
|
||||
if (i == self.cursor_idx) return width;
|
||||
const g = grapheme.slice(second_half);
|
||||
const g = grapheme.bytes(second_half);
|
||||
width += win.gwidth(g);
|
||||
}
|
||||
return width;
|
||||
|
@ -141,7 +144,7 @@ pub fn draw(self: *TextInput, win: Window) void {
|
|||
|
||||
// assumption!! the gap is never within a grapheme
|
||||
// one way to _ensure_ this is to move the gap... but that's a cost we probably don't want to pay.
|
||||
var first_iter = GraphemeIterator.init(self.buf.items);
|
||||
var first_iter = self.unicode.graphemeIterator(self.buf.items);
|
||||
var col: usize = 0;
|
||||
var i: usize = 0;
|
||||
while (first_iter.next()) |grapheme| {
|
||||
|
@ -149,7 +152,7 @@ pub fn draw(self: *TextInput, win: Window) void {
|
|||
i += 1;
|
||||
continue;
|
||||
}
|
||||
const g = grapheme.slice(self.buf.items);
|
||||
const g = grapheme.bytes(self.buf.items);
|
||||
const w = win.gwidth(g);
|
||||
if (col + w >= win.width) {
|
||||
win.writeCell(win.width - 1, 0, .{ .char = ellipsis });
|
||||
|
@ -166,13 +169,13 @@ pub fn draw(self: *TextInput, win: Window) void {
|
|||
if (i == self.cursor_idx) self.prev_cursor_col = col;
|
||||
}
|
||||
const second_half = self.buf.secondHalf();
|
||||
var second_iter = GraphemeIterator.init(second_half);
|
||||
var second_iter = self.unicode.graphemeIterator(second_half);
|
||||
while (second_iter.next()) |grapheme| {
|
||||
if (i < self.draw_offset) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
const g = grapheme.slice(second_half);
|
||||
const g = grapheme.bytes(second_half);
|
||||
const w = win.gwidth(g);
|
||||
if (col + w > win.width) {
|
||||
win.writeCell(win.width - 1, 0, .{ .char = ellipsis });
|
||||
|
@ -223,7 +226,7 @@ fn reset(self: *TextInput) void {
|
|||
pub fn byteOffsetToCursor(self: TextInput) usize {
|
||||
// assumption! the gap is never in the middle of a grapheme
|
||||
// one way to _ensure_ this is to move the gap... but that's a cost we probably don't want to pay.
|
||||
var iter = GraphemeIterator.init(self.buf.items);
|
||||
var iter = self.unicode.graphemeIterator(self.buf.items);
|
||||
var offset: usize = 0;
|
||||
var i: usize = 0;
|
||||
while (iter.next()) |grapheme| {
|
||||
|
@ -231,7 +234,7 @@ pub fn byteOffsetToCursor(self: TextInput) usize {
|
|||
offset += grapheme.len;
|
||||
i += 1;
|
||||
} else {
|
||||
var second_iter = GraphemeIterator.init(self.buf.secondHalf());
|
||||
var second_iter = self.unicode.graphemeIterator(self.buf.secondHalf());
|
||||
while (second_iter.next()) |grapheme| {
|
||||
if (i == self.cursor_idx) break;
|
||||
offset += grapheme.len;
|
||||
|
@ -257,7 +260,7 @@ fn deleteToStart(self: *TextInput) !void {
|
|||
fn deleteBeforeCursor(self: *TextInput) !void {
|
||||
// assumption! the gap is never in the middle of a grapheme
|
||||
// one way to _ensure_ this is to move the gap... but that's a cost we probably don't want to pay.
|
||||
var iter = GraphemeIterator.init(self.buf.items);
|
||||
var iter = self.unicode.graphemeIterator(self.buf.items);
|
||||
var offset: usize = 0;
|
||||
var i: usize = 1;
|
||||
while (iter.next()) |grapheme| {
|
||||
|
@ -270,7 +273,7 @@ fn deleteBeforeCursor(self: *TextInput) !void {
|
|||
offset += grapheme.len;
|
||||
i += 1;
|
||||
} else {
|
||||
var second_iter = GraphemeIterator.init(self.buf.secondHalf());
|
||||
var second_iter = self.unicode.graphemeIterator(self.buf.secondHalf());
|
||||
while (second_iter.next()) |grapheme| {
|
||||
if (i == self.cursor_idx) {
|
||||
try self.buf.replaceRangeBefore(offset, grapheme.len, &.{});
|
||||
|
@ -287,7 +290,7 @@ fn deleteBeforeCursor(self: *TextInput) !void {
|
|||
fn deleteAtCursor(self: *TextInput) !void {
|
||||
// assumption! the gap is never in the middle of a grapheme
|
||||
// one way to _ensure_ this is to move the gap... but that's a cost we probably don't want to pay.
|
||||
var iter = GraphemeIterator.init(self.buf.items);
|
||||
var iter = self.unicode.graphemeIterator(self.buf.items);
|
||||
var offset: usize = 0;
|
||||
var i: usize = 1;
|
||||
while (iter.next()) |grapheme| {
|
||||
|
@ -299,7 +302,7 @@ fn deleteAtCursor(self: *TextInput) !void {
|
|||
offset += grapheme.len;
|
||||
i += 1;
|
||||
} else {
|
||||
var second_iter = GraphemeIterator.init(self.buf.secondHalf());
|
||||
var second_iter = self.unicode.graphemeIterator(self.buf.secondHalf());
|
||||
while (second_iter.next()) |grapheme| {
|
||||
if (i == self.cursor_idx + 1) {
|
||||
try self.buf.replaceRangeAfter(offset, grapheme.len, &.{});
|
||||
|
|
Loading…
Reference in a new issue