mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-24 05:40:15 +00:00
renderer: fix preedit range width (#12479)
Related to #12466 `Preedit.range()` returns an inclusive range, but the end position was calculated as `start + w`. For wide preedit text, this covers one extra cell. In Debug builds, Korean IME composition between existing Hangul characters can panic with: `index out of bounds: index 2, len 2` I reproduced this reliably when there are two Hangul characters to the right of the cursor. For example, type `가나다`, move the cursor between `가` and `나`, then start a new Korean IME composition. With the old range calculation, the renderer skips the first wide character plus the head cell of the next wide character, then resumes on that character's spacer tail. This changes the inclusive end to `start + (w - 1)` and adds focused tests for narrow, wide, and right-edge preedit ranges. This does not fully fix the visual behavior reported in #12466. The adjacent character can still disappear during composition, so this PR only fixes the crash side of the problem.
This commit is contained in:
@@ -112,7 +112,7 @@ pub const Preedit = struct {
|
||||
|
||||
// If our preedit goes off the end of the screen, we adjust it so
|
||||
// that it shifts left.
|
||||
const end = start + w;
|
||||
const end = if (w > 0) start + (w - 1) else start;
|
||||
const start_offset = if (end > max) end - max else 0;
|
||||
return .{
|
||||
.start = start -| start_offset,
|
||||
@@ -121,3 +121,41 @@ pub const Preedit = struct {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const test_hangul_ga: u21 = 0xAC00; // U+AC00 HANGUL SYLLABLE GA
|
||||
|
||||
test "preedit range covers exact cell width" {
|
||||
const testing = std.testing;
|
||||
|
||||
{
|
||||
const p: Preedit = .{
|
||||
.codepoints = &.{.{ .codepoint = 'a' }},
|
||||
};
|
||||
const range = p.range(2, 9);
|
||||
try testing.expectEqual(@as(terminalpkg.size.CellCountInt, 2), range.start);
|
||||
try testing.expectEqual(@as(terminalpkg.size.CellCountInt, 2), range.end);
|
||||
try testing.expectEqual(@as(usize, 0), range.cp_offset);
|
||||
}
|
||||
|
||||
{
|
||||
const p: Preedit = .{
|
||||
.codepoints = &.{.{ .codepoint = test_hangul_ga, .wide = true }},
|
||||
};
|
||||
const range = p.range(2, 9);
|
||||
try testing.expectEqual(@as(terminalpkg.size.CellCountInt, 2), range.start);
|
||||
try testing.expectEqual(@as(terminalpkg.size.CellCountInt, 3), range.end);
|
||||
try testing.expectEqual(@as(usize, 0), range.cp_offset);
|
||||
}
|
||||
}
|
||||
|
||||
test "preedit range shifts left at right edge" {
|
||||
const testing = std.testing;
|
||||
|
||||
const p: Preedit = .{
|
||||
.codepoints = &.{.{ .codepoint = test_hangul_ga, .wide = true }},
|
||||
};
|
||||
const range = p.range(9, 9);
|
||||
try testing.expectEqual(@as(terminalpkg.size.CellCountInt, 8), range.start);
|
||||
try testing.expectEqual(@as(terminalpkg.size.CellCountInt, 9), range.end);
|
||||
try testing.expectEqual(@as(usize, 0), range.cp_offset);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user