charmbracelet / charmbracelet/bubbletea
External process input errors when using `cmd.exe`
- Dominant language
- Go
- Stars
- 44.9k
- Forks
- 1.3k
- PR merge metrics
- No merged PRs in 30d
Description
**Describe the bug**
When use bubbletea app to run an external process on Windows OS, the process looses first character of the user input.
**Setup**
Please complete the following information along with version numbers, if applicable.
- Windows 10
- cmd.exe
- Tested on github.com/charmbracelet/bubbletea `v0.24.2` and `v0.25.0`
**To Reproduce**
Steps to reproduce the behavior:
1. Create a project using the source code below. Place `echo_input.cmd` in the project's folder.
2. Open `cmd.exe`, go to the project folder, and run the app: `go run main.go`
3. Once the app started press `up` or `down` arrow keys
4. Press `e` to run external process (the process will launch `echo_input.cmd`)
5. Press `1 2 3` sequence on your keyboard
6. Notice that only `23` was printed on your screen. Press `enter` key.
7. Press any key to exit from the external process
8. Notice that your lost character `1` was intercepted by the model's update method
**Source Code**
```go
// main.go
package main
import (
"fmt"
"os"
"os/exec"
tea "github.com/charmbracelet/bubbletea"
)
type editorFinishedMsg struct{ err error }
func runExternalProcess() tea.Cmd {
c := exec.Command("cmd", "/c", "echo_input.cmd")
return tea.ExecProcess(c, func(err error) tea.Msg {
return editorFinishedMsg{err}
})
}
type model struct {
input string
err error
}
func (m model) Init() tea.Cmd { return nil }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "e":
return m, runExternalProcess()
case "ctrl+c", "q":
return m, tea.Quit
default:
{
if msg.Type != tea.KeyUp && msg.Type != tea.KeyDown {
m.input = string(msg.Runes)
}
}
}
case editorFinishedMsg:
if msg.err != nil {
m.err = msg.err
return m, tea.Quit
}
}
return m, nil
}
func (m model) View() string {
if m.err != nil {
return "Error: " + m.err.Error() + "\n"
}
return fmt.Sprintf("Press 'e' to test input.\nPress 'q' to quit.\nLost character: %s", m.input)
}
func main() {
if _, err := tea.NewProgram(model{}, tea.WithAltScreen()).Run(); err != nil {
fmt.Println("Error running program:", err)
os.Exit(1)
}
}
```
```cmd
rem echo_input.cmd
@echo off
set /p user_input=Enter 123:
echo You entered: %user_input%
pause
```
**Expected behavior**
All user input should be redirected to external process
Contributor guide
Research direction
Start at bubbletea's tea.ExecProcess entry point and reproduce the supplied Windows 10 cmd.exe example using main.go and echo_input.cmd. Trace how input is handed to the external process and returned to the model; done means all characters reach cmd.exe and the first character is not consumed by Update.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, shell
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100