Files
lukaszraczylo 3195e353e2 fix(util): rune-safe truncation across formatters
firstLineOf (query) and truncateString (edit) sliced strings at a raw byte
offset, splitting a multibyte UTF-8 character into invalid bytes in
LLM-facing output (ast_query compact format and edit not-found errors are
hot paths). Extract the rune-boundary backoff already used by search's
truncateLine into util.RuneBoundary and route all three sites through it.

Adds table-driven tests for the primitive and a multibyte case for firstLineOf.
2026-06-20 00:44:38 +01:00

21 lines
528 B
Go

package util
import "unicode/utf8"
// RuneBoundary returns the largest index j <= i such that s[:j] is valid UTF-8
// (j falls on a rune start, never inside a multibyte sequence). i is clamped to
// [0, len(s)]. Callers slicing s[:i] for truncation should slice s[:RuneBoundary(s, i)]
// instead so a multibyte character is never split into an invalid encoding.
func RuneBoundary(s string, i int) int {
if i <= 0 {
return 0
}
if i >= len(s) {
return len(s)
}
for i > 0 && !utf8.RuneStart(s[i]) {
i--
}
return i
}