diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 6f1a4d23e..a2ec06780 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -1434,10 +1434,13 @@ fn rebuildCells( const preedit_range: ?struct { y: usize, x: [2]usize, + cp_offset: usize, } = if (preedit) |preedit_v| preedit: { + const range = preedit_v.range(screen.cursor.x, screen.cols - 1); break :preedit .{ .y = screen.cursor.y, - .x = preedit_v.range(screen.cursor.x, screen.cols - 1), + .x = .{ range.start, range.end }, + .cp_offset = range.cp_offset, }; } else null; @@ -1583,7 +1586,7 @@ fn rebuildCells( if (preedit) |preedit_v| { const range = preedit_range.?; var x = range.x[0]; - for (preedit_v.codepoints) |cp| { + for (preedit_v.codepoints[range.cp_offset..]) |cp| { self.addPreeditCell(cp, x, range.y) catch |err| { log.warn("error building preedit cell, will be invalid x={} y={}, err={}", .{ x, diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 4909b255e..6b001cede 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -954,10 +954,13 @@ pub fn rebuildCells( const preedit_range: ?struct { y: usize, x: [2]usize, + cp_offset: usize, } = if (preedit) |preedit_v| preedit: { + const range = preedit_v.range(screen.cursor.x, screen.cols - 1); break :preedit .{ .y = screen.cursor.y, - .x = preedit_v.range(screen.cursor.x, screen.cols - 1), + .x = .{ range.start, range.end }, + .cp_offset = range.cp_offset, }; } else null; @@ -1094,7 +1097,7 @@ pub fn rebuildCells( if (preedit) |preedit_v| { const range = preedit_range.?; var x = range.x[0]; - for (preedit_v.codepoints) |cp| { + for (preedit_v.codepoints[range.cp_offset..]) |cp| { self.addPreeditCell(cp, x, range.y) catch |err| { log.warn("error building preedit cell, will be invalid x={} y={}, err={}", .{ x, diff --git a/src/renderer/State.zig b/src/renderer/State.zig index ec4c6f971..409cfb046 100644 --- a/src/renderer/State.zig +++ b/src/renderer/State.zig @@ -69,11 +69,45 @@ pub const Preedit = struct { return result; } - pub fn range(self: *const Preedit, start: usize, max: usize) [2]usize { + /// Range returns the start and end x position of the preedit text + /// along with any codepoint offset necessary to fit the preedit + /// into the available space. + pub fn range(self: *const Preedit, start: usize, max: usize) struct { + start: usize, + end: usize, + cp_offset: usize, + } { + // If our width is greater than the number of cells we have + // then we need to adjust our codepoint start to a point where + // our width would be less than the number of cells we have. + const w, const cp_offset = width: { + // max is inclusive, so we need to add 1 to it. + const max_width = max - start + 1; + + // Rebuild our width in reverse order. This is because we want + // to offset by the end cells, not the start cells (if we have to). + var w: usize = 0; + for (0..self.codepoints.len) |i| { + const reverse_i = self.codepoints.len - i - 1; + const cp = self.codepoints[reverse_i]; + w += if (cp.wide) 2 else 1; + if (w > max_width) { + break :width .{ w, reverse_i }; + } + } + + // Width fit in the max width so no offset necessary. + break :width .{ w, 0 }; + }; + // If our preedit goes off the end of the screen, we adjust it so // that it shifts left. - const end = start + self.width(); - const offset = if (end > max) end - max else 0; - return .{ start -| offset, end -| offset }; + const end = start + w; + const start_offset = if (end > max) end - max else 0; + return .{ + .start = start -| start_offset, + .end = end -| start_offset, + .cp_offset = cp_offset, + }; } };