charmbracelet / charmbracelet/bubbletea

External process input errors when using `cmd.exe`

Open
#923 3 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.