From 5e0cf1ede41b2887db98ca61c8100cbe1da61170 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 16 Mar 2026 04:38:32 +0200 Subject: Fix byte vs UTF-16 indexing in LSP position handling Adds utf16OffsetToByteOffset helper to correctly convert LSP character positions (UTF-16 code units) to Go string byte offsets. Fixes trigger detection, prefix heuristic, and completion text slicing for files containing multi-byte characters. Co-Authored-By: Claude Opus 4.6 --- internal/lsp/handlers.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'internal/lsp/handlers.go') diff --git a/internal/lsp/handlers.go b/internal/lsp/handlers.go index ebdefc1..ad2f98d 100644 --- a/internal/lsp/handlers.go +++ b/internal/lsp/handlers.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "strings" + "unicode/utf8" ) func (s *Server) handle(req Request) { @@ -274,16 +275,18 @@ func (s *Server) isTriggerEvent(p CompletionParams, current string) bool { } // For TriggerForIncomplete (3), require manual char check below } - // 2) Fallback: check the character immediately prior to cursor - idx := p.Position.Character - if idx <= 0 || idx > len(current) { + // 2) Fallback: check the character immediately prior to cursor. + // Convert UTF-16 offset to byte offset for correct multi-byte handling. + byteIdx := utf16OffsetToByteOffset(current, p.Position.Character) + if byteIdx <= 0 || byteIdx > len(current) { return false } // Bare double-open should not trigger via fallback char either (only when configured) if containsAny(current, doubleSeqs) && !hasDoubleOpenTrigger(current, open, openChar, closeChar) { return false } - ch := string(current[idx-1]) + r, _ := utf8.DecodeLastRuneInString(current[:byteIdx]) + ch := string(r) for _, c := range triggerChars { if c == ch { return true -- cgit v1.2.3