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