-
Notifications
You must be signed in to change notification settings - Fork 1.1k
bug: textarea infinite loop when pressing alt+left on empty input #1652
Description
Describe the bug
Pressing alt+b or alt+left (the WordBackward binding) on an empty textarea.Model causes an infinite loop that freezes the entire Bubble Tea event loop. The UI becomes completely unresponsive.
wordLeft() in textarea/textarea.go:923-929 has an unconditional for {} loop whose only exit is finding a non-space rune under the cursor. When the textarea is empty (m.value is [][]rune{ {} }, cursor at row=0, col=0), characterLeft(true) is a no-op and the break condition col < len(value[row]) evaluates to 0 < 0 → false, so the loop spins forever.
Note: doWordRight() already handles this correctly — it checks for end-of-text before looping.
Setup
charm.land/bubbles/v2 v2.0.0- OS: macOS
- Shell: zsh
- Terminal emulator: Ghostty
- Terminal multiplexer: N/A
To Reproduce
- Create a
textarea.Modelwith default (empty) content - Focus the textarea
- Without typing anything, press
alt+boralt+left - The UI freezes indefinitely
Source Code
package main
import (
"fmt"
"time"
"charm.land/bubbles/v2/textarea"
tea "charm.land/bubbletea/v2"
)
type model struct {
ta textarea.Model
}
func (m model) Init() tea.Cmd {
return m.ta.Focus()
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
m.ta, cmd = m.ta.Update(msg)
return m, cmd
}
func (m model) View() tea.View {
return tea.NewView(fmt.Sprintf(
"Press alt+b or alt+left to freeze.\n\n%s",
m.ta.View(),
))
}
func main() {
ta := textarea.New()
ta.SetHeight(3)
ta.SetWidth(40)
ta.Placeholder = "type something, or don't..."
p := tea.NewProgram(model{ta: ta})
go func() {
time.Sleep(10 * time.Second)
fmt.Println("\n[timeout] program was stuck — killed")
p.Kill()
}()
if _, err := p.Run(); err != nil {
fmt.Println("error:", err)
}
}Expected behavior
Pressing alt+b / alt+left on an empty textarea should be a no-op (there is no word to the left of position 0,0), not freeze the program.
Screenshots
N/A
Additional context
Suggested fix — add a boundary check at the top of wordLeft(), mirroring what doWordRight() already does:
func (m *Model) wordLeft() {
if m.row == 0 && m.col == 0 {
return
}
// ... rest unchanged
}