Skip to content

Commit c18425d

Browse files
fix: reset autocomplete state when switching input modes (#755)
## Summary - Adds a `Reset()` method to the autocomplete component that clears all state including filtered suggestions, selection, and visibility flags - Calls `Reset()` when entering commenting, assigning, or unassigning modes to prevent stale suggestions from labeling mode from leaking into other input modes - Adds regression tests to verify the fix Fixes #751
1 parent 1cee318 commit c18425d

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed

internal/tui/components/autocomplete/autocomplete.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,16 @@ func (m *Model) Hide() {
180180
m.visible = false
181181
}
182182

183+
// Reset clears all autocomplete state including filtered suggestions, selection,
184+
// and visibility flags. Use this when switching between different input modes
185+
// (e.g., from labeling to commenting) to prevent stale suggestions from leaking.
186+
func (m *Model) Reset() {
187+
m.filtered = nil
188+
m.selected = 0
189+
m.visible = false
190+
m.hiddenByUser = false
191+
}
192+
183193
// Suppress hides the popup immediately and prevents it from being shown again
184194
// automatically until `Unsuppress()` is called. The underlying filtered results
185195
// are still updated while suppressed so navigation and selection keys will

internal/tui/components/issueview/action_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,102 @@ func TestUpdateWithReboundKeys(t *testing.T) {
157157
require.NotNil(t, action, "expected action for rebound key")
158158
require.Equal(t, IssueActionLabel, action.Type, "expected label action for rebound key")
159159
}
160+
161+
// TestAutocompleteStateResetWhenSwitchingModes verifies that autocomplete
162+
// suggestions from labeling mode don't leak into commenting mode.
163+
// This is a regression test for https://github.com/dlvhdr/gh-dash/issues/751
164+
func TestAutocompleteStateResetWhenSwitchingModes(t *testing.T) {
165+
m := newTestModelForAction(t)
166+
167+
// Simulate what happens when entering labeling mode:
168+
// 1. Autocomplete gets populated with label suggestions
169+
m.ac.SetSuggestions([]string{"bug", "feature", "documentation", "enhancement"})
170+
171+
// 2. User types something and autocomplete filters/shows suggestions
172+
// This populates the internal 'filtered' slice
173+
m.ac.Show("fea", nil) // Would match "feature"
174+
175+
// Verify autocomplete has suggestions (the bug condition)
176+
require.True(t, m.ac.HasSuggestions(),
177+
"autocomplete should have suggestions after Show()")
178+
179+
// 3. User exits labeling mode (Escape) - in the bug, this only hid but didn't reset
180+
m.ac.Hide()
181+
182+
// In the buggy code, HasSuggestions() would still return true here
183+
// because Hide() only sets visible=false but doesn't clear filtered
184+
185+
// 4. User enters commenting mode
186+
m.SetIsCommenting(true)
187+
188+
// 5. After the fix, autocomplete state should be fully reset
189+
require.False(t, m.ac.HasSuggestions(),
190+
"autocomplete should have no suggestions after entering comment mode")
191+
require.False(t, m.ac.IsVisible(),
192+
"autocomplete should not be visible after entering comment mode")
193+
}
194+
195+
// TestAutocompleteResetOnAssignMode verifies autocomplete is reset when entering assign mode
196+
func TestAutocompleteResetOnAssignMode(t *testing.T) {
197+
m := newTestModelForAction(t)
198+
199+
// Set up autocomplete with label suggestions
200+
m.ac.SetSuggestions([]string{"bug", "feature"})
201+
m.ac.Show("bug", nil)
202+
require.True(t, m.ac.HasSuggestions(), "precondition: autocomplete should have suggestions")
203+
204+
// Enter assign mode
205+
m.SetIsAssigning(true)
206+
207+
// Autocomplete should be reset
208+
require.False(t, m.ac.HasSuggestions(),
209+
"autocomplete should have no suggestions after entering assign mode")
210+
}
211+
212+
// TestAutocompleteResetOnUnassignMode verifies autocomplete is reset when entering unassign mode
213+
func TestAutocompleteResetOnUnassignMode(t *testing.T) {
214+
m := newTestModelForAction(t)
215+
216+
// Set up autocomplete with label suggestions
217+
m.ac.SetSuggestions([]string{"bug", "feature"})
218+
m.ac.Show("bug", nil)
219+
require.True(t, m.ac.HasSuggestions(), "precondition: autocomplete should have suggestions")
220+
221+
// Enter unassign mode
222+
m.SetIsUnassigning(true)
223+
224+
// Autocomplete should be reset
225+
require.False(t, m.ac.HasSuggestions(),
226+
"autocomplete should have no suggestions after entering unassign mode")
227+
}
228+
229+
// TestInputBoxTextNotReplacedByStaleAutocomplete is an end-to-end test that
230+
// verifies the full bug scenario: typing in comment box after exiting label mode
231+
// should not autocomplete with label names.
232+
func TestInputBoxTextNotReplacedByStaleAutocomplete(t *testing.T) {
233+
m := newTestModelForAction(t)
234+
235+
// Step 1: Simulate labeling mode with suggestions
236+
m.ac.SetSuggestions([]string{"bug", "feature", "documentation"})
237+
m.isLabeling = true
238+
m.ac.Show("fea", nil) // User typed "fea", matches "feature"
239+
240+
// Step 2: Exit labeling mode (simulates pressing Escape)
241+
m.isLabeling = false
242+
m.ac.Hide()
243+
244+
// Step 3: Enter commenting mode
245+
m.SetIsCommenting(true)
246+
247+
// Step 4: Set some text in the input box (simulates user typing)
248+
m.inputBox.SetValue("This is my comment about fea")
249+
250+
// Step 5: Verify that Tab key does NOT trigger autocomplete selection
251+
// because autocomplete was reset when entering comment mode
252+
tabMsg := tea.KeyMsg{Type: tea.KeyTab}
253+
m.inputBox.Update(tabMsg)
254+
255+
// The text should remain unchanged (not replaced with "feature")
256+
require.Equal(t, "This is my comment about fea", m.inputBox.Value(),
257+
"input text should not be modified by Tab when autocomplete is reset")
258+
}

internal/tui/components/issueview/issueview.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ func (m *Model) SetIsCommenting(isCommenting bool) tea.Cmd {
420420

421421
if !m.isCommenting && isCommenting {
422422
m.inputBox.Reset()
423+
m.ac.Reset() // Clear any stale autocomplete state (e.g., from labeling)
423424
}
424425
m.isCommenting = isCommenting
425426
m.inputBox.SetPrompt("Leave a comment...")
@@ -441,6 +442,7 @@ func (m *Model) SetIsAssigning(isAssigning bool) tea.Cmd {
441442

442443
if !m.isAssigning && isAssigning {
443444
m.inputBox.Reset()
445+
m.ac.Reset() // Clear any stale autocomplete state (e.g., from labeling)
444446
}
445447
m.isAssigning = isAssigning
446448
m.inputBox.SetPrompt("Assign users (whitespace-separated)...")
@@ -532,6 +534,7 @@ func (m *Model) SetIsUnassigning(isUnassigning bool) tea.Cmd {
532534

533535
if !m.isUnassigning && isUnassigning {
534536
m.inputBox.Reset()
537+
m.ac.Reset() // Clear any stale autocomplete state (e.g., from labeling)
535538
}
536539
m.isUnassigning = isUnassigning
537540
m.inputBox.SetPrompt("Unassign users (whitespace-separated)...")

0 commit comments

Comments
 (0)