Skip to content

textarea: guard wordLeft against empty buffer so alt+left doesn't hang#959

Open
SAY-5 wants to merge 11 commits intocharmbracelet:masterfrom
SAY-5:fix/textarea-word-left-empty-1652
Open

textarea: guard wordLeft against empty buffer so alt+left doesn't hang#959
SAY-5 wants to merge 11 commits intocharmbracelet:masterfrom
SAY-5:fix/textarea-word-left-empty-1652

Conversation

@SAY-5
Copy link
Copy Markdown

@SAY-5 SAY-5 commented Apr 22, 2026

Summary

Fixes charmbracelet/bubbletea#1652.

wordLeft() ran an unconditional for {} loop whose only exit was finding a non-space rune under the cursor. On an empty textarea (m.value == [][]rune{{}}, cursor at row=0/col=0), characterLeft() is a no-op and the break condition col < len(value[row]) && !unicode.IsSpace(...) evaluates to 0 < 0 — so the loop spins forever and freezes the entire Bubble Tea event loop the first time the user hits alt+b or alt+left on an empty input.

doWordRight() already has an analogous end-of-text guard. Mirror it: bail at (row 0, col 0) before stepping left so there is no loop with no exit.

Tests

TestWordLeftEmptyDoesNotHang — runs Update with alt+left on a freshly-created textarea inside a goroutine and fails if it doesn't return within 2s. On master it hangs; with this fix it returns instantly.

--- PASS: TestWordLeftEmptyDoesNotHang (0.00s)

go test ./textarea/..., go vet, go build all clean.

meowgorithm and others added 11 commits February 24, 2026 08:24
Bumps the all group with 2 updates: [charm.land/bubbletea/v2](https://github.com/charmbracelet/bubbletea) and [github.com/mattn/go-runewidth](https://github.com/mattn/go-runewidth).


Updates `charm.land/bubbletea/v2` from 2.0.0 to 2.0.1
- [Release notes](https://github.com/charmbracelet/bubbletea/releases)
- [Commits](charmbracelet/bubbletea@v2.0.0...v2.0.1)

Updates `github.com/mattn/go-runewidth` from 0.0.20 to 0.0.21
- [Commits](mattn/go-runewidth@v0.0.20...v0.0.21)

---
updated-dependencies:
- dependency-name: charm.land/bubbletea/v2
  dependency-version: 2.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: github.com/mattn/go-runewidth
  dependency-version: 0.0.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the all group with 2 updates: [charm.land/bubbletea/v2](https://github.com/charmbracelet/bubbletea) and [charm.land/lipgloss/v2](https://github.com/charmbracelet/lipgloss).


Updates `charm.land/bubbletea/v2` from 2.0.1 to 2.0.2
- [Release notes](https://github.com/charmbracelet/bubbletea/releases)
- [Commits](charmbracelet/bubbletea@v2.0.1...v2.0.2)

Updates `charm.land/lipgloss/v2` from 2.0.0 to 2.0.2
- [Release notes](https://github.com/charmbracelet/lipgloss/releases)
- [Commits](charmbracelet/lipgloss@v2.0.0...v2.0.2)

---
updated-dependencies:
- dependency-name: charm.land/bubbletea/v2
  dependency-version: 2.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: charm.land/lipgloss/v2
  dependency-version: 2.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Andrey Nering <andreynering@users.noreply.github.com>
Co-authored-by: rohan436 <rohan.santhoshkumar@googlemail.com>
…rmbracelet#945)

Bumps the all group with 1 update: [github.com/mattn/go-runewidth](https://github.com/mattn/go-runewidth).


Updates `github.com/mattn/go-runewidth` from 0.0.21 to 0.0.22
- [Commits](mattn/go-runewidth@v0.0.21...v0.0.22)

---
updated-dependencies:
- dependency-name: github.com/mattn/go-runewidth
  dependency-version: 0.0.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…oup (charmbracelet#953)

Bumps the all group with 1 update: [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata).


Updates `dependabot/fetch-metadata` from 2 to 3
- [Release notes](https://github.com/dependabot/fetch-metadata/releases)
- [Commits](dependabot/fetch-metadata@v2...v3)

---
updated-dependencies:
- dependency-name: dependabot/fetch-metadata
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…rmbracelet#952)

Bumps the all group with 1 update: [github.com/mattn/go-runewidth](https://github.com/mattn/go-runewidth).


Updates `github.com/mattn/go-runewidth` from 0.0.22 to 0.0.23
- [Commits](mattn/go-runewidth@v0.0.22...v0.0.23)

---
updated-dependencies:
- dependency-name: github.com/mattn/go-runewidth
  dependency-version: 0.0.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the all group with 3 updates: [charm.land/bubbletea/v2](https://github.com/charmbracelet/bubbletea), [charm.land/lipgloss/v2](https://github.com/charmbracelet/lipgloss) and [github.com/charmbracelet/x/ansi](https://github.com/charmbracelet/x).


Updates `charm.land/bubbletea/v2` from 2.0.2 to 2.0.6
- [Release notes](https://github.com/charmbracelet/bubbletea/releases)
- [Commits](charmbracelet/bubbletea@v2.0.2...v2.0.6)

Updates `charm.land/lipgloss/v2` from 2.0.2 to 2.0.3
- [Release notes](https://github.com/charmbracelet/lipgloss/releases)
- [Commits](charmbracelet/lipgloss@v2.0.2...v2.0.3)

Updates `github.com/charmbracelet/x/ansi` from 0.11.6 to 0.11.7
- [Commits](charmbracelet/x@ansi/v0.11.6...ansi/v0.11.7)

---
updated-dependencies:
- dependency-name: charm.land/bubbletea/v2
  dependency-version: 2.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: charm.land/lipgloss/v2
  dependency-version: 2.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
- dependency-name: github.com/charmbracelet/x/ansi
  dependency-version: 0.11.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
wordLeft() ran an unconditional for {} loop whose only exit was
finding a non-space rune under the cursor. On an empty textarea
(m.value == [][]rune{{}}, cursor at row=0/col=0), characterLeft() is
a no-op and the break condition 'col < len(value[row]) &&
!unicode.IsSpace' evaluates to '0 < 0' — so the loop spun forever and
froze the entire Bubble Tea event loop the first time the user hit
alt+b or alt+left on an empty input (charmbracelet/bubbletea#1652).

doWordRight() already has an analogous end-of-text guard. Add the
mirror: bail at (row 0, col 0) before stepping left so there is no
loop with no exit.

Adds TestWordLeftEmptyDoesNotHang which runs Update with alt+left on
a freshly-created textarea in a goroutine and fails if the goroutine
doesn't return within 2s. Confirmed: fails on master (hang), passes
with the fix.

Fixes charmbracelet/bubbletea#1652

Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants