Read input regardless of whether or not it's a TTY

This commit also contains some refactors:

* Refactor away inputStatus type
* Refactor away program.inputIsTTY member
* Simplify how we setup and restore input when it's a TTY
This commit is contained in:
Christian Rocha
2021-07-29 16:47:13 -04:00
parent 9826251f08
commit 9f9b3aea96
5 changed files with 32 additions and 83 deletions

59
tea.go
View File

@@ -52,8 +52,8 @@ type Model interface {
// function.
type Cmd func() Msg
// startupOptions contains configuration options to be run while the program
// is initializing.
// Options to customize the program during its initialization. These are
// generally set with ProgramOptions.
//
// The options here are treated as bits.
type startupOptions byte
@@ -62,42 +62,14 @@ func (s startupOptions) has(option startupOptions) bool {
return s&option != 0
}
// Available startup options.
const (
withAltScreen startupOptions = 1 << iota
withMouseCellMotion
withMouseAllMotion
withInputTTY
withCustomInput
)
// inputStatus indicates the current state of the input. By default, input is
// stdin, however we'll change this if input's not a TTY. The user can also set
// the input.
type inputStatus int
const (
// Generally this will be stdin.
//
// Lint ignore note: this is the implicit default value. While it's not
// checked explicitly, it's presence nullifies the other possible values
// of this type in logical statements.
defaultInput inputStatus = iota // nolint:golint,deadcode,unused,varcheck
// The user explicitly set the input.
customInput
// We've opened a TTY for input.
managedInput
)
func (i inputStatus) String() string {
return [...]string{
"default input",
"custom input",
"managed input",
}[i]
}
// Program is a terminal user interface.
type Program struct {
initialModel Model
@@ -122,8 +94,6 @@ type Program struct {
// is on by default.
CatchPanics bool
inputStatus inputStatus
inputIsTTY bool
outputIsTTY bool
console console.Console
@@ -311,22 +281,19 @@ func (p *Program) Start() error {
p.input = f
}
// Is input a terminal?
if f, ok := p.input.(*os.File); ok {
p.inputIsTTY = isatty.IsTerminal(f.Fd())
}
// If input is not a terminal, and the user hasn't set a custom input, open
// a TTY so we can capture input as normal. This will allow things to "just
// work" in cases where data was piped or redirected into this application.
if !p.inputIsTTY && p.inputStatus != customInput {
f, err := openInputTTY()
if err != nil {
return err
if f, ok := p.input.(*os.File); ok {
inputIsTTY := isatty.IsTerminal(f.Fd())
if !inputIsTTY && !p.startupOptions.has(withCustomInput) {
f, err := openInputTTY()
if err != nil {
return err
}
p.input = f
}
p.input = f
p.inputIsTTY = true
p.inputStatus = managedInput
}
// Listen for SIGINT. Note that in most cases ^C will not send an
@@ -394,7 +361,7 @@ func (p *Program) Start() error {
p.renderer.write(model.View())
// Subscribe to user input
if p.inputIsTTY {
if p.input != nil {
go func() {
for {
msg, err := readInput(p.input)