From c1a96bc09e857e61531ce193bbb74e80beac488c Mon Sep 17 00:00:00 2001 From: David Gageot Date: Mon, 13 Apr 2026 12:56:44 +0200 Subject: [PATCH] fix: clear suggestion ghost text when completion dialog closes on backspace When typing '/' to open the completion dialog, the first item (e.g. /attach) was shown as ghost text. Pressing backspace correctly removed the '/' and closed the dialog, but the ghost text 'attach' remained visible in the editor. The root cause was a timing issue: updateCompletionQuery sent an async CloseMsg but didn't clear the suggestion immediately. Meanwhile, refreshSuggestion returned early (without clearing) because currentCompletion was still set. Fix: clear the suggestion synchronously in updateCompletionQuery when closing the completion popup. Also simplify the suggestion management code: - Consolidate 5 clearSuggestion calls in refreshSuggestion into one - Simplify SelectionChangedMsg with a clear-first-then-set pattern - Remove redundant clearSuggestion in ClosedMsg handler Assisted-By: docker-agent --- pkg/tui/components/editor/editor.go | 42 +++++++---------------------- 1 file changed, 9 insertions(+), 33 deletions(-) diff --git a/pkg/tui/components/editor/editor.go b/pkg/tui/components/editor/editor.go index c7c9fc45e..79f98b3b7 100644 --- a/pkg/tui/components/editor/editor.go +++ b/pkg/tui/components/editor/editor.go @@ -464,46 +464,26 @@ func deleteLastGraphemeCluster(s string) string { // refreshSuggestion updates the cached suggestion to reflect the current // textarea value and available history entries. func (e *editor) refreshSuggestion() { - if e.hist == nil { - e.clearSuggestion() - return - } - - // Don't show history suggestions when completion popup is active. - // The completion's selected item takes precedence. + // Don't overwrite completion-managed suggestions with history suggestions. if e.currentCompletion != nil { return } - current := e.textarea.Value() - if current == "" { - e.clearSuggestion() - return - } + e.clearSuggestion() - // Only show suggestions when cursor is at the end of the text. - // If cursor is not at the end, moving left/right would cause the - // suggestion overlay to overwrite existing characters. - if !e.isCursorAtEnd() { - e.clearSuggestion() + current := e.textarea.Value() + if e.hist == nil || current == "" || !e.isCursorAtEnd() { return } + // Only show a suggestion when history has a longer match. match := e.hist.LatestMatch(current) - - if match == "" || match == current || len(match) <= len(current) { - e.clearSuggestion() + if len(match) <= len(current) { return } e.suggestion = match[len(current):] - if e.suggestion == "" { - e.clearSuggestion() - return - } - e.hasSuggestion = true - // Keep cursor visible - suggestion is rendered as overlay after cursor position } // clearSuggestion removes any pending suggestion. @@ -698,7 +678,6 @@ func (e *editor) Update(msg tea.Msg) (layout.Model, tea.Cmd) { case completion.ClosedMsg: e.completionWord = "" e.currentCompletion = nil - e.clearSuggestion() e.refreshSuggestion() // Reset file loading state e.fileLoadStarted = false @@ -731,18 +710,14 @@ func (e *editor) Update(msg tea.Msg) (layout.Model, tea.Cmd) { itemsCmd, ) case completion.SelectionChangedMsg: - // Show the selected completion item as a suggestion in the editor + // Show the selected completion item as a suggestion in the editor. + e.clearSuggestion() if msg.Value != "" && e.currentCompletion != nil { - // Calculate the suggestion: what needs to be added after current text currentText := e.textarea.Value() if strings.HasPrefix(msg.Value, currentText) { e.suggestion = msg.Value[len(currentText):] e.hasSuggestion = e.suggestion != "" - } else { - e.clearSuggestion() } - } else { - e.clearSuggestion() } return e, nil case tea.KeyPressMsg: @@ -1019,6 +994,7 @@ func (e *editor) updateCompletionQuery() tea.Cmd { } e.completionWord = "" + e.clearSuggestion() return core.CmdHandler(completion.CloseMsg{}) }