From 5251d78bd76d4ab87661a91eb180784bfb04ce39 Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Thu, 25 Dec 2025 09:48:18 -0800 Subject: [PATCH] fix: use ansi.Cut for Unicode grapheme handling in editor completions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 💘 Generated with Crush Assisted-by: Kimi K2 0905 via Crush --- internal/tui/components/chat/editor/editor.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/internal/tui/components/chat/editor/editor.go b/internal/tui/components/chat/editor/editor.go index 1623ba806cd9e93bd1544aabbdb6a7e3f6604bcc..2b88ff2f0979c49d149696cb2c77098248efef72 100644 --- a/internal/tui/components/chat/editor/editor.go +++ b/internal/tui/components/chat/editor/editor.go @@ -189,11 +189,17 @@ func (m *editorCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) { } if item, ok := msg.Value.(FileCompletionItem); ok { word := m.textarea.Word() - // If the selected item is a file, insert its path into the textarea + // If the selected item is a file, insert its path into the textarea. value := m.textarea.Value() - value = value[:m.completionsStartIndex] + // Remove the current query - item.Path + // Insert the file path - value[m.completionsStartIndex+len(word):] // Append the rest of the value + + // Convert byte indices to grapheme indices. + _, graphemeEnd := ansi.ByteToGraphemeRange(value, m.completionsStartIndex+len(word), m.completionsStartIndex+len(word)) + + // Rebuild with the file path inserted. + value = value[:m.completionsStartIndex] + // remove the current query + item.Path + // insert the file path + ansi.Cut(value, graphemeEnd, ansi.StringWidth(value)) // append the rest of the value + // XXX: This will always move the cursor to the end of the textarea. m.textarea.SetValue(value) m.textarea.MoveToEnd() @@ -339,7 +345,9 @@ func (m *editorCmp) Update(msg tea.Msg) (util.Model, tea.Cmd) { if strings.HasPrefix(word, "@") { // XXX: wont' work if editing in the middle of the field. m.completionsStartIndex = strings.LastIndex(m.textarea.Value(), word) - m.currentQuery = word[1:] + // Convert byte index to grapheme index for proper Unicode handling + _, graphemeEnd := ansi.ByteToGraphemeRange(word, 1, 1) + m.currentQuery = ansi.Cut(word, graphemeEnd, ansi.StringWidth(word)) x, y := m.completionsPosition() x -= len(m.currentQuery) m.isCompletionsOpen = true