diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index 69e0fa7..e43e545 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -19,9 +19,12 @@ pub const Metrics = struct { ctx: *const anyopaque, egc_length: egc_length_func, egc_chunk_width: egc_chunk_width_func, + egc_last: egc_last_func, tab_width: usize, pub const egc_length_func = *const fn (self: Metrics, egcs: []const u8, colcount: *c_int, abs_col: usize) usize; pub const egc_chunk_width_func = *const fn (self: Metrics, chunk_: []const u8, abs_col_: usize) usize; + pub const egc_last_func = *const fn (self: Metrics, egcs: []const u8) []const u8; + }; arena: std.heap.ArenaAllocator, diff --git a/src/renderer/vaxis/Plane.zig b/src/renderer/vaxis/Plane.zig index e06db47..cac38b3 100644 --- a/src/renderer/vaxis/Plane.zig +++ b/src/renderer/vaxis/Plane.zig @@ -432,6 +432,13 @@ pub fn egc_chunk_width(self: *const Plane, chunk_: []const u8, abs_col_: usize, return colcount; } +pub fn egc_last(self: *const Plane, egcs: []const u8) []const u8 { + var iter = self.window.screen.unicode.graphemeIterator(egcs); + var last: []const u8 = egcs[0..0]; + while (iter.next()) |grapheme| last = grapheme.bytes(egcs); + return last; +} + pub fn metrics(self: *const Plane, tab_width: usize) Buffer.Metrics { return .{ .ctx = self, @@ -448,6 +455,12 @@ pub fn metrics(self: *const Plane, tab_width: usize) Buffer.Metrics { } }.f, .tab_width = tab_width, + .egc_last = struct { + fn f(self_: Buffer.Metrics, egcs: []const u8) []const u8 { + const plane: *const Plane = @ptrCast(@alignCast(self_.ctx)); + return plane.egc_last(egcs); + } + }.f, }; } diff --git a/src/tui/mode/mini/file_browser.zig b/src/tui/mode/mini/file_browser.zig index ed79aaf..df5a0e3 100644 --- a/src/tui/mode/mini/file_browser.zig +++ b/src/tui/mode/mini/file_browser.zig @@ -115,7 +115,7 @@ pub fn Create(options: type) type { } if (tui.current().mini_mode) |*mini_mode| { mini_mode.text = self.file_path.items; - mini_mode.cursor = self.file_path.items.len; + mini_mode.cursor = tui.current().stdplane().egc_chunk_width(self.file_path.items, 0, 8); } return; } @@ -139,7 +139,7 @@ pub fn Create(options: type) type { defer { if (tui.current().mini_mode) |*mini_mode| { mini_mode.text = self.file_path.items; - mini_mode.cursor = self.file_path.items.len; + mini_mode.cursor = tui.current().stdplane().egc_chunk_width(self.file_path.items, 0, 8); } } var count: usize = undefined; @@ -243,7 +243,7 @@ pub fn Create(options: type) type { fn update_mini_mode_text(self: *Self) void { if (tui.current().mini_mode) |*mini_mode| { mini_mode.text = self.file_path.items; - mini_mode.cursor = self.file_path.items.len; + mini_mode.cursor = tui.current().stdplane().egc_chunk_width(self.file_path.items, 0, 8); } } @@ -273,7 +273,7 @@ pub fn Create(options: type) type { pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result { if (self.file_path.items.len > 0) { self.complete_trigger_count = 0; - self.file_path.shrinkRetainingCapacity(self.file_path.items.len - 1); + self.file_path.shrinkRetainingCapacity(self.file_path.items.len - tui.current().stdplane().egc_last(self.file_path.items).len); } self.update_mini_mode_text(); } diff --git a/src/tui/mode/mini/find.zig b/src/tui/mode/mini/find.zig index 117f1b1..eca8080 100644 --- a/src/tui/mode/mini/find.zig +++ b/src/tui/mode/mini/find.zig @@ -142,7 +142,7 @@ fn load_history(self: *Self, pos: usize) void { fn update_mini_mode_text(self: *Self) void { if (tui.current().mini_mode) |*mini_mode| { mini_mode.text = self.input.items; - mini_mode.cursor = self.input.items.len; + mini_mode.cursor = tui.current().stdplane().egc_chunk_width(self.input.items, 0, 8); } } @@ -187,7 +187,7 @@ const cmds = struct { pub const mini_mode_insert_bytes_meta = .{ .arguments = &.{.string} }; pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result { - _ = self.input.popOrNull(); + self.input.resize(self.input.items.len - tui.current().stdplane().egc_last(self.input.items).len) catch {}; self.update_mini_mode_text(); } pub const mini_mode_delete_backwards_meta = .{ .description = "Delete backwards" }; diff --git a/src/tui/mode/mini/find_in_files.zig b/src/tui/mode/mini/find_in_files.zig index 1827d12..934b19f 100644 --- a/src/tui/mode/mini/find_in_files.zig +++ b/src/tui/mode/mini/find_in_files.zig @@ -73,7 +73,7 @@ fn insert_bytes(self: *Self, bytes_: []const u8) !void { } fn start_query(self: *Self) !void { - if (self.input.len < 1 or eql(u8, self.input, self.last_input)) + if (self.input.len < 2 or eql(u8, self.input, self.last_input)) return; @memcpy(self.last_buf[0..self.input.len], self.input); self.last_input = self.last_buf[0..self.input.len]; @@ -83,7 +83,7 @@ fn start_query(self: *Self) !void { fn update_mini_mode_text(self: *Self) void { if (tui.current().mini_mode) |*mini_mode| { mini_mode.text = self.input; - mini_mode.cursor = self.input.len; + mini_mode.cursor = tui.current().stdplane().egc_chunk_width(self.input, 0, 8); } } @@ -128,9 +128,8 @@ const cmds = struct { pub const mini_mode_insert_bytes_meta = .{ .arguments = &.{.string} }; pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result { - if (self.input.len > 0) { - self.input = self.input[0 .. self.input.len - 1]; - } + self.input = self.input[0 .. self.input.len - tui.current().stdplane().egc_last(self.input).len]; + self.update_mini_mode_text(); } pub const mini_mode_delete_backwards_meta = .{ .description = "Delete backwards" }; diff --git a/src/tui/mode/mini/goto.zig b/src/tui/mode/mini/goto.zig index 4923876..c23bebd 100644 --- a/src/tui/mode/mini/goto.zig +++ b/src/tui/mode/mini/goto.zig @@ -54,7 +54,7 @@ fn update_mini_mode_text(self: *Self) void { (fmt.bufPrint(&self.buf, "{d}", .{linenum}) catch "") else ""; - mini_mode.cursor = mini_mode.text.len; + mini_mode.cursor = tui.current().stdplane().egc_chunk_width(mini_mode.text, 0, 8); } } diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index 50050cc..d929829 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -227,14 +227,14 @@ fn delete_word(self: *Self) !void { } else { self.inputbox.text.shrinkRetainingCapacity(0); } - self.inputbox.cursor = self.inputbox.text.items.len; + self.inputbox.cursor = tui.current().stdplane().egc_chunk_width(self.inputbox.text.items, 0, 8); return self.start_query(); } fn delete_code_point(self: *Self) !void { if (self.inputbox.text.items.len > 0) { - self.inputbox.text.shrinkRetainingCapacity(self.inputbox.text.items.len - 1); - self.inputbox.cursor = self.inputbox.text.items.len; + self.inputbox.text.shrinkRetainingCapacity(self.inputbox.text.items.len - tui.current().stdplane().egc_last(self.inputbox.text.items).len); + self.inputbox.cursor = tui.current().stdplane().egc_chunk_width(self.inputbox.text.items, 0, 8); } return self.start_query(); } @@ -243,13 +243,13 @@ fn insert_code_point(self: *Self, c: u32) !void { var buf: [6]u8 = undefined; const bytes = try input.ucs32_to_utf8(&[_]u32{c}, &buf); try self.inputbox.text.appendSlice(buf[0..bytes]); - self.inputbox.cursor = self.inputbox.text.items.len; + self.inputbox.cursor = tui.current().stdplane().egc_chunk_width(self.inputbox.text.items, 0, 8); return self.start_query(); } fn insert_bytes(self: *Self, bytes: []const u8) !void { try self.inputbox.text.appendSlice(bytes); - self.inputbox.cursor = self.inputbox.text.items.len; + self.inputbox.cursor = tui.current().stdplane().egc_chunk_width(self.inputbox.text.items, 0, 8); return self.start_query(); } diff --git a/src/tui/mode/overlay/palette.zig b/src/tui/mode/overlay/palette.zig index 717cf7a..108f145 100644 --- a/src/tui/mode/overlay/palette.zig +++ b/src/tui/mode/overlay/palette.zig @@ -296,15 +296,15 @@ pub fn Create(options: type) type { } else { self.inputbox.text.shrinkRetainingCapacity(0); } - self.inputbox.cursor = self.inputbox.text.items.len; + self.inputbox.cursor = tui.current().stdplane().egc_chunk_width(self.inputbox.text.items, 0, 8); self.view_pos = 0; return self.start_query(0); } fn delete_code_point(self: *Self) !void { if (self.inputbox.text.items.len > 0) { - self.inputbox.text.shrinkRetainingCapacity(self.inputbox.text.items.len - 1); - self.inputbox.cursor = self.inputbox.text.items.len; + self.inputbox.text.shrinkRetainingCapacity(self.inputbox.text.items.len - tui.current().stdplane().egc_last(self.inputbox.text.items).len); + self.inputbox.cursor = tui.current().stdplane().egc_chunk_width(self.inputbox.text.items, 0, 8); } self.view_pos = 0; return self.start_query(0); @@ -314,14 +314,14 @@ pub fn Create(options: type) type { var buf: [6]u8 = undefined; const bytes = try input.ucs32_to_utf8(&[_]u32{c}, &buf); try self.inputbox.text.appendSlice(buf[0..bytes]); - self.inputbox.cursor = self.inputbox.text.items.len; + self.inputbox.cursor = tui.current().stdplane().egc_chunk_width(self.inputbox.text.items, 0, 8); self.view_pos = 0; return self.start_query(0); } fn insert_bytes(self: *Self, bytes: []const u8) !void { try self.inputbox.text.appendSlice(bytes); - self.inputbox.cursor = self.inputbox.text.items.len; + self.inputbox.cursor = tui.current().stdplane().egc_chunk_width(self.inputbox.text.items, 0, 8); self.view_pos = 0; return self.start_query(0); }