Don't prompt the user when the terminal is not interactive

pull/6/head
Mickaël Menu 3 years ago
parent eb130a8107
commit 1b905dacfd
No known key found for this signature in database
GPG Key ID: 53D73664CD359895

@ -1,4 +1,4 @@
package tty package term
import ( import (
"fmt" "fmt"
@ -20,7 +20,7 @@ type PromptOpt struct {
// Prompt displays a message and waits for the user to input one of the // Prompt displays a message and waits for the user to input one of the
// available options. // available options.
// Returns the selected option index. // Returns the selected option index.
func (t *TTY) Prompt(msg string, defaultOpt int, options []PromptOpt) int { func (t *Terminal) Prompt(msg string, defaultOpt int, options []PromptOpt) int {
responses := "" responses := ""
for i, opt := range options { for i, opt := range options {
if i == len(options)-1 { if i == len(options)-1 {
@ -42,8 +42,7 @@ func (t *TTY) Prompt(msg string, defaultOpt int, options []PromptOpt) int {
for { for {
fmt.Printf("%s\n%s > ", msg, responses) fmt.Printf("%s\n%s > ", msg, responses)
// Don't prompt when --no-input is on. if !t.IsInteractive() {
if t.NoInput {
fmt.Println(options[defaultOpt].AllowedResponses[0]) fmt.Println(options[defaultOpt].AllowedResponses[0])
return defaultOpt return defaultOpt
} }
@ -68,7 +67,7 @@ func (t *TTY) Prompt(msg string, defaultOpt int, options []PromptOpt) int {
} }
// Confirm is a shortcut to prompt a yes/no question to the user. // Confirm is a shortcut to prompt a yes/no question to the user.
func (t *TTY) Confirm(msg string, yesDescription string, noDescription string) bool { func (t *Terminal) Confirm(msg string, yesDescription string, noDescription string) bool {
return t.Prompt(msg, 1, []PromptOpt{ return t.Prompt(msg, 1, []PromptOpt{
{ {
Label: t.MustStyle("y", style.RuleEmphasis) + "es", Label: t.MustStyle("y", style.RuleEmphasis) + "es",

@ -1,4 +1,4 @@
package tty package term
import ( import (
"fmt" "fmt"
@ -7,8 +7,8 @@ import (
"github.com/mickael-menu/zk/core/style" "github.com/mickael-menu/zk/core/style"
) )
// Style implements style.Styler using ANSI escape codes to be used with a TTY. // Style implements style.Styler using ANSI escape codes to be used with a terminal.
func (t *TTY) Style(text string, rules ...style.Rule) (string, error) { func (t *Terminal) Style(text string, rules ...style.Rule) (string, error) {
if text == "" { if text == "" {
return text, nil return text, nil
} }
@ -22,7 +22,7 @@ func (t *TTY) Style(text string, rules ...style.Rule) (string, error) {
return color.New(attrs...).Sprint(text), nil return color.New(attrs...).Sprint(text), nil
} }
func (t *TTY) MustStyle(text string, rules ...style.Rule) string { func (t *Terminal) MustStyle(text string, rules ...style.Rule) string {
text, err := t.Style(text, rules...) text, err := t.Style(text, rules...)
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())

@ -1,4 +1,4 @@
package tty package term
import ( import (
"testing" "testing"
@ -8,42 +8,42 @@ import (
"github.com/mickael-menu/zk/util/test/assert" "github.com/mickael-menu/zk/util/test/assert"
) )
func createTTY() *TTY { func createTerminal() *Terminal {
color.NoColor = false // Otherwise the color codes are not injected during tests color.NoColor = false // Otherwise the color codes are not injected during tests
return New() return New()
} }
func TestStyleNoRule(t *testing.T) { func TestStyleNoRule(t *testing.T) {
res, err := createTTY().Style("Hello") res, err := createTerminal().Style("Hello")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, res, "Hello") assert.Equal(t, res, "Hello")
} }
func TestStyleOneRule(t *testing.T) { func TestStyleOneRule(t *testing.T) {
res, err := createTTY().Style("Hello", style.Rule("red")) res, err := createTerminal().Style("Hello", style.Rule("red"))
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, res, "\033[31mHello\033[0m") assert.Equal(t, res, "\033[31mHello\033[0m")
} }
func TestStyleMultipleRule(t *testing.T) { func TestStyleMultipleRule(t *testing.T) {
res, err := createTTY().Style("Hello", style.Rule("red"), style.Rule("bold")) res, err := createTerminal().Style("Hello", style.Rule("red"), style.Rule("bold"))
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, res, "\033[31;1mHello\033[0m") assert.Equal(t, res, "\033[31;1mHello\033[0m")
} }
func TestStyleUnknownRule(t *testing.T) { func TestStyleUnknownRule(t *testing.T) {
_, err := createTTY().Style("Hello", style.Rule("unknown")) _, err := createTerminal().Style("Hello", style.Rule("unknown"))
assert.Err(t, err, "unknown styling rule: unknown") assert.Err(t, err, "unknown styling rule: unknown")
} }
func TestStyleEmptyString(t *testing.T) { func TestStyleEmptyString(t *testing.T) {
res, err := createTTY().Style("", style.Rule("bold")) res, err := createTerminal().Style("", style.Rule("bold"))
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, res, "") assert.Equal(t, res, "")
} }
func TestStyleAllRules(t *testing.T) { func TestStyleAllRules(t *testing.T) {
styler := createTTY() styler := createTerminal()
test := func(rule string, expected string) { test := func(rule string, expected string) {
res, err := styler.Style("Hello", style.Rule(rule)) res, err := styler.Style("Hello", style.Rule(rule))
assert.Nil(t, err) assert.Nil(t, err)

@ -0,0 +1,22 @@
package term
import (
"os"
"github.com/mattn/go-isatty"
)
// Terminal offers utilities to interact with the terminal.
type Terminal struct {
NoInput bool
}
func New() *Terminal {
return &Terminal{}
}
// IsInteractive returns whether the app is attached to an interactive terminal
// and can prompt the user.
func (t *Terminal) IsInteractive() bool {
return !t.NoInput && isatty.IsTerminal(os.Stdin.Fd())
}

@ -1,9 +0,0 @@
package tty
type TTY struct {
NoInput bool
}
func New() *TTY {
return &TTY{}
}

@ -7,7 +7,7 @@ import (
"github.com/mickael-menu/zk/adapter/handlebars" "github.com/mickael-menu/zk/adapter/handlebars"
"github.com/mickael-menu/zk/adapter/markdown" "github.com/mickael-menu/zk/adapter/markdown"
"github.com/mickael-menu/zk/adapter/sqlite" "github.com/mickael-menu/zk/adapter/sqlite"
"github.com/mickael-menu/zk/adapter/tty" "github.com/mickael-menu/zk/adapter/term"
"github.com/mickael-menu/zk/core/note" "github.com/mickael-menu/zk/core/note"
"github.com/mickael-menu/zk/core/zk" "github.com/mickael-menu/zk/core/zk"
"github.com/mickael-menu/zk/util" "github.com/mickael-menu/zk/util"
@ -18,7 +18,7 @@ import (
type Container struct { type Container struct {
Date date.Provider Date date.Provider
Logger util.Logger Logger util.Logger
TTY *tty.TTY Terminal *term.Terminal
templateLoader *handlebars.Loader templateLoader *handlebars.Loader
} }
@ -29,14 +29,14 @@ func NewContainer() *Container {
Logger: util.NewStdLogger("zk: ", 0), Logger: util.NewStdLogger("zk: ", 0),
// zk is short-lived, so we freeze the current date to use the same // zk is short-lived, so we freeze the current date to use the same
// date for any rendering during the execution. // date for any rendering during the execution.
Date: &date, Date: &date,
TTY: tty.New(), Terminal: term.New(),
} }
} }
func (c *Container) TemplateLoader(lang string) *handlebars.Loader { func (c *Container) TemplateLoader(lang string) *handlebars.Loader {
if c.templateLoader == nil { if c.templateLoader == nil {
handlebars.Init(lang, c.Logger, c.TTY) handlebars.Init(lang, c.Logger, c.Terminal)
c.templateLoader = handlebars.NewLoader() c.templateLoader = handlebars.NewLoader()
} }
return c.templateLoader return c.templateLoader
@ -48,7 +48,7 @@ func (c *Container) Parser() *markdown.Parser {
func (c *Container) NoteFinder(tx sqlite.Transaction) note.Finder { func (c *Container) NoteFinder(tx sqlite.Transaction) note.Finder {
notes := sqlite.NewNoteDAO(tx, c.Logger) notes := sqlite.NewNoteDAO(tx, c.Logger)
return fzf.NewNoteFinder(notes, c.TTY) return fzf.NewNoteFinder(notes, c.Terminal)
} }
// Database returns the DB instance for the given slip box, after executing any // Database returns the DB instance for the given slip box, after executing any

@ -46,7 +46,7 @@ func (cmd *Edit) Run(container *Container) error {
if count > 0 { if count > 0 {
if !cmd.Force && count > 2 { if !cmd.Force && count > 2 {
if !container.TTY.Confirm( if !container.Terminal.Confirm(
fmt.Sprintf("Are you sure you want to open %v notes in the editor?", count), fmt.Sprintf("Are you sure you want to open %v notes in the editor?", count),
"Open all the notes", "Open all the notes",
"Don't open any note", "Don't open any note",

@ -43,7 +43,7 @@ func (cmd *List) Run(container *Container) error {
} }
templates := container.TemplateLoader(zk.Config.Lang) templates := container.TemplateLoader(zk.Config.Lang)
styler := container.TTY styler := container.Terminal
format := opt.NewNotEmptyString(cmd.Format) format := opt.NewNotEmptyString(cmd.Format)
formatter, err := note.NewFormatter(zk.Path, wd, format, templates, styler) formatter, err := note.NewFormatter(zk.Path, wd, format, templates, styler)
if err != nil { if err != nil {

@ -14,6 +14,7 @@ require (
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/lestrrat-go/strftime v1.0.3 github.com/lestrrat-go/strftime v1.0.3
github.com/mattn/go-isatty v0.0.12
github.com/mattn/go-runewidth v0.0.10 // indirect github.com/mattn/go-runewidth v0.0.10 // indirect
github.com/mattn/go-sqlite3 v1.14.6 github.com/mattn/go-sqlite3 v1.14.6
github.com/mickael-menu/pretty v0.2.3 github.com/mickael-menu/pretty v0.2.3

@ -37,6 +37,6 @@ func main() {
type NoInput bool type NoInput bool
func (f NoInput) BeforeApply(container *cmd.Container) error { func (f NoInput) BeforeApply(container *cmd.Container) error {
container.TTY.NoInput = true container.Terminal.NoInput = true
return nil return nil
} }

@ -16,7 +16,7 @@ import (
osutil "github.com/mickael-menu/zk/util/os" osutil "github.com/mickael-menu/zk/util/os"
) )
// Pager writes text to a TTY using the user's pager. // Pager writes text to a terminal using the user's pager.
type Pager struct { type Pager struct {
io.WriteCloser io.WriteCloser
done chan bool done chan bool
@ -31,7 +31,7 @@ var PassthroughPager = &Pager{
isCloseable: false, isCloseable: false,
} }
// New creates a pager.Pager to be used to write a paginated text to the TTY. // New creates a pager.Pager to be used to write a paginated text to the terminal.
func New(pagerCmd opt.String, logger util.Logger) (*Pager, error) { func New(pagerCmd opt.String, logger util.Logger) (*Pager, error) {
wrap := errors.Wrapper("failed to paginate the output, try again with --no-pager or fix your PAGER environment variable") wrap := errors.Wrapper("failed to paginate the output, try again with --no-pager or fix your PAGER environment variable")

Loading…
Cancel
Save