Files
Stefan HallerandClaude Opus 4.8 d36ce51559 Capture suggestions inputs on the UI thread
RefreshSuggestions dispatched to an AsyncHandler worker that read
State.FindSuggestions and the prompt's TextArea (via GetPromptInput)
from the worker goroutine. The main thread rewrites both in
preparePromptPanel when it (re)creates a prompt panel, so an in-flight
suggestions worker races those writes -- two data races surfaced under
-race (filter_by_path/reword_commit_in_filtering_mode).

Capture both on the UI thread (RefreshSuggestions is only ever called
from UI-thread handlers) before dispatching to the worker. This is also
more correct: we search for the input as it was when dispatched, which
is what this request's AsyncHandler id corresponds to.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 12:35:54 +02:00

62 lines
1.8 KiB
Go

package gui
import (
"github.com/jesseduffield/lazygit/pkg/gocui"
)
func (gui *Gui) handleEditorKeypress(v *gocui.View, key gocui.Key, allowMultiline bool) bool {
if key.Equals(gocui.NewKeyName(gocui.KeyEnter)) && allowMultiline {
v.TextArea.TypeCharacter("\n")
v.RenderTextArea()
return true
}
return gocui.DefaultEditor.Edit(v, key)
}
// we've just copy+pasted the editor from gocui to here so that we can also re-
// render the commit message length on each keypress
func (gui *Gui) commitMessageEditor(v *gocui.View, key gocui.Key) bool {
matched := gui.handleEditorKeypress(v, key, false)
v.RenderTextArea()
gui.c.Contexts().CommitMessage.RenderSubtitle()
return matched
}
func (gui *Gui) commitDescriptionEditor(v *gocui.View, key gocui.Key) bool {
matched := gui.handleEditorKeypress(v, key, true)
v.RenderTextArea()
return matched
}
func (gui *Gui) promptEditor(v *gocui.View, key gocui.Key) bool {
matched := gui.handleEditorKeypress(v, key, false)
v.RenderTextArea()
suggestionsContext := gui.State.Contexts.Suggestions
// Capture the suggestions function and the input here, on the UI thread; the
// main thread rewrites State.FindSuggestions when it (re)creates a prompt
// panel, so reading it from the worker below would race that write.
if findSuggestions := suggestionsContext.State.FindSuggestions; findSuggestions != nil {
input := v.TextArea.GetContent()
suggestionsContext.State.AsyncHandler.Do(func() func() {
suggestions := findSuggestions(input)
return func() { suggestionsContext.SetSuggestions(suggestions) }
})
}
return matched
}
func (gui *Gui) searchEditor(v *gocui.View, key gocui.Key) bool {
matched := gui.handleEditorKeypress(v, key, false)
v.RenderTextArea()
searchString := v.TextArea.GetContent()
gui.helpers.Search.OnPromptContentChanged(searchString)
return matched
}