Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

when start a exec, the first key press always be ignored, it seems that I need one press to "active" the io #1116

Open
rickywei opened this issue Aug 28, 2024 · 4 comments

Comments

@rickywei
Copy link

package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"

	tea "github.com/charmbracelet/bubbletea"
)

type editorFinishedMsg struct{ err error }

func openEditor() tea.Cmd {
	return tea.Exec(&echo{}, func(err error) tea.Msg { return err })

	editor := os.Getenv("EDITOR")
	if editor == "" {
		editor = "vim"
	}
	c := exec.Command(editor) //nolint:gosec
	return tea.ExecProcess(c, func(err error) tea.Msg {
		return editorFinishedMsg{err}
	})
}

type model struct {
	altscreenActive bool
	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 "a":
			m.altscreenActive = !m.altscreenActive
			cmd := tea.EnterAltScreen
			if !m.altscreenActive {
				cmd = tea.ExitAltScreen
			}
			return m, cmd
		case "e":
			// return m, tea.Exec(&echo{}, func(err error) tea.Msg { return err })
			return m, tea.Sequence(openEditor())
		case "ctrl+c", "q":
			return m, tea.Quit
		}
	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 "Press 'e' to open your EDITOR.\nPress 'a' to toggle the altscreen\nPress 'q' to quit.\n"
}

func main() {
	m := model{}
	if _, err := tea.NewProgram(m).Run(); err != nil {
		fmt.Println("Error running program:", err)
		os.Exit(1)
	}
}

type echo struct {
	stdin          io.Reader
	stdout, stderr io.Writer
}

func (e *echo) Run() error {
	fmt.Println(io.Copy(e.stdout, e.stdin))
	return nil
}
func (e *echo) SetStdin(r io.Reader) {
	if e.stdin == nil {
		e.stdin = r
	}
}
func (e *echo) SetStdout(w io.Writer) {
	if e.stdin == nil {

		e.stdout = w
	}
}
func (e *echo) SetStderr(w io.Writer) {
	if e.stdin == nil {
		e.stdout = w
	}

}
@rickywei
Copy link
Author

also, it needs an extra key press after exec run exit to input

@meowgorithm
Copy link
Member

Hey there! What are you trying to do in the above code? Typically you're going to want to use tea.ExecProcess as tea.Exec is for lower level work (like working with SSH).

Here's a diff that fixes the above code:

diff --git a/main.go b/main.go
index 5b6b2f5..1e4a82f 100644
--- a/main.go
+++ b/main.go
@@ -2,7 +2,6 @@ package main
 
 import (
 	"fmt"
-	"io"
 	"os"
 	"os/exec"
 
@@ -12,8 +11,6 @@ import (
 type editorFinishedMsg struct{ err error }
 
 func openEditor() tea.Cmd {
-	return tea.Exec(&echo{}, func(err error) tea.Msg { return err })
-
 	editor := os.Getenv("EDITOR")
 	if editor == "" {
 		editor = "vim"
@@ -73,30 +70,3 @@ func main() {
 		os.Exit(1)
 	}
 }
-
-type echo struct {
-	stdin          io.Reader
-	stdout, stderr io.Writer
-}
-
-func (e *echo) Run() error {
-	fmt.Println(io.Copy(e.stdout, e.stdin))
-	return nil
-}
-func (e *echo) SetStdin(r io.Reader) {
-	if e.stdin == nil {
-		e.stdin = r
-	}
-}
-func (e *echo) SetStdout(w io.Writer) {
-	if e.stdin == nil {
-
-		e.stdout = w
-	}
-}
-func (e *echo) SetStderr(w io.Writer) {
-	if e.stdin == nil {
-		e.stdout = w
-	}
-
-}

@rickywei
Copy link
Author

@meowgorithm Thanks for your reply.
And as you guessed, I am going to interactive with SSH.
Since my code is a little long, so I post the code with some modification of example code. (The echo is replaced with SSH in my code). Let me simplify the code here. this example code can reproduce the problem too.

package main

import (
	"fmt"
	"io"
	"os"

	tea "github.com/charmbracelet/bubbletea"
)

type editorFinishedMsg struct{ err error }

type model struct {
	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, tea.Exec(&echo{}, func(err error) tea.Msg { return err })
			return m, tea.Exec(&echo{}, func(err error) tea.Msg { return err })
		case "ctrl+c", "q":
			return m, tea.Quit
		}
	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 "Press 'e' to echo"
}

func main() {
	m := model{}
	if _, err := tea.NewProgram(m).Run(); err != nil {
		fmt.Println("Error running program:", err)
		os.Exit(1)
	}
}

type echo struct {
	stdin          io.Reader
	stdout, stderr io.Writer
}

func (e *echo) Run() error {
	fmt.Println(io.Copy(e.stdout, e.stdin))

	return nil
}
func (e *echo) SetStdin(r io.Reader) {
	e.stdin = r
}
func (e *echo) SetStdout(w io.Writer) {
	e.stdout = w
}
func (e *echo) SetStderr(w io.Writer) {
	e.stdout = w
}

When you run it and press "e", your input should be echoed on screen. But The thing is, the first key you input is not echoed. For example, you only get bc when you input abc.

@BenediktGerlach
Copy link

Hi!
On Windows I noticed a similar behaviour for reading user input after a tea.Program was run: The first key input is somehow ignored.

package main

import (
	"bufio"
	"fmt"
	tea "github.com/charmbracelet/bubbletea"
	"log"
	"os"
)

func main() {
	p := tea.NewProgram(model{})
	if _, err := p.Run(); err != nil {
		log.Fatal(err)
	}

	fmt.Println("input something")
	if _, err := bufio.NewReader(os.Stdin).ReadString('\n'); err != nil {
		log.Fatal(err)
	}
}

type model struct{}

func (m model) Init() tea.Cmd {
	return nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmd tea.Cmd
	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEnter, tea.KeyCtrlC, tea.KeyEsc:
			return m, tea.Quit
		}
	}
	return m, cmd
}

func (m model) View() string {
	return fmt.Sprint("hi, press enter to quit")
}

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

No branches or pull requests

3 participants