diff options
Diffstat (limited to 'src/modal.zig')
| -rw-r--r-- | src/modal.zig | 22 |
1 files changed, 15 insertions, 7 deletions
diff --git a/src/modal.zig b/src/modal.zig index dd7d2b57..11eda7b9 100644 --- a/src/modal.zig +++ b/src/modal.zig @@ -1327,18 +1327,26 @@ pub fn lineEndOffset(text: []const u8, line: usize) usize { /// acme's do, not grapheme clusters: a lone combining mark or a `\r` is a /// place of its own, whatever a terminal draws in one cell. pub fn runeStart(text: []const u8, off: usize) usize { - var o = @min(off, text.len); + const o = @min(off, text.len); + if (o == text.len or text[o] & 0xC0 != 0x80) return o; + var s = o; var steps: u8 = 0; - while (o > 0 and o < text.len and text[o] & 0xC0 == 0x80 and steps < 3) : (steps += 1) o -= 1; - return o; + while (s > 0 and text[s] & 0xC0 == 0x80 and steps < 3) : (steps += 1) s -= 1; + // Only a lead byte whose sequence reaches `o`, its bytes all + // continuations, makes `o` the inside of a rune; a stray continuation + // byte (invalid UTF-8) is a place of its own. + const n = std.unicode.utf8ByteSequenceLength(text[s]) catch return o; + if (s + n <= o or s + n > text.len) return o; + for (text[s + 1 .. s + n]) |c| if (c & 0xC0 != 0x80) return o; + return s; } /// `off`, or the end of the rune it falls inside. pub fn runeEnd(text: []const u8, off: usize) usize { - var o = @min(off, text.len); - var steps: u8 = 0; - while (o < text.len and text[o] & 0xC0 == 0x80 and steps < 3) : (steps += 1) o += 1; - return o; + const o = @min(off, text.len); + const s = runeStart(text, o); + if (s == o) return o; + return s + (std.unicode.utf8ByteSequenceLength(text[s]) catch 1); } /// The rune boundary after the one at `off`. |
