Skip to content
This repository has been archived by the owner on Apr 19, 2024. It is now read-only.

Added missing AskOpts #216

Merged
merged 21 commits into from
Jun 3, 2019
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 32 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,11 @@ All of the prompts have a `Help` field which can be defined to provide more info
### Changing the input rune

In some situations, `?` is a perfectly valid response. To handle this, you can change the rune that survey
looks for by setting the `HelpInputRune` variable in `survey/core`:
looks for by passing an `AskOpt` to `Ask` or `AskOne`:

```golang
import (
"github.com/AlecAivazis/survey/v2"
surveyCore "github.com/AlecAivazis/survey/v2/core"
)

number := ""
Expand All @@ -303,9 +302,7 @@ prompt := &survey.Input{
Help: "I couldn't come up with one.",
}

surveyCore.HelpInputRune = '^'

survey.AskOne(prompt, &number)
survey.AskOne(prompt, &number, survey.WithHelpInput('^'))
```

## Custom Types
Expand Down Expand Up @@ -340,17 +337,36 @@ survey.AskOne(

## Customizing Output

Customizing the icons and various parts of survey can easily be done by setting the following variables
in `survey/core`:

| name | default | description |
| ------------------ | ------- | ------------------------------------------------------------- |
| ErrorIcon | X | Before an error |
| HelpIcon | i | Before help text |
| QuestionIcon | ? | Before the message of a prompt |
| SelectFocusIcon | > | Marks the current focus in `Select` and `MultiSelect` prompts |
| UnmarkedOptionIcon | [ ] | Marks an unselected option in a `MultiSelect` prompt |
| MarkedOptionIcon | [x] | Marks a chosen selection in a `MultiSelect` prompt |
Customizing the icons and various parts of survey can easily be done by passing the `WithIcons` option
to `Ask` or `AskOne`:

```golang
import (
"github.com/AlecAivazis/survey/v2"
)

number := ""
prompt := &survey.Input{
Message: "If you have this need, please give me a reasonable message.",
Help: "I couldn't come up with one.",
}

survey.AskOne(prompt, &number, survey.WithIconSet(function(icons *survey.IconSet) {
// you can set any icons
icons.Question = "⁇"
}))
```

The icons available for updating are:

| name | default | description |
| -------------- | ------- | ------------------------------------------------------------- |
| Error | X | Before an error |
| Help | i | Before help text |
| Question | ? | Before the message of a prompt |
| SelectFocus | > | Marks the current focus in `Select` and `MultiSelect` prompts |
| UnmarkedOption | [ ] | Marks an unselected option in a `MultiSelect` prompt |
| MarkedOption | [x] | Marks a chosen selection in a `MultiSelect` prompt |

## Testing

Expand Down
45 changes: 30 additions & 15 deletions confirm.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package survey
import (
"fmt"
"regexp"

"github.com/AlecAivazis/survey/v2/core"
)

// Confirm is a regular text input that accept yes/no answers. Response type is a bool.
type Confirm struct {
core.Renderer
Renderer
Message string
Default bool
Help string
Expand All @@ -20,17 +18,18 @@ type ConfirmTemplateData struct {
Confirm
Answer string
ShowHelp bool
Config *PromptConfig
}

// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
var ConfirmQuestionTemplate = `
{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
{{- if .ShowHelp }}{{- color "cyan"}}{{ .Config.Icons.Help }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color "green+hb"}}{{ .Config.Icons.Question }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
{{- if .Answer}}
{{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
{{- else }}
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ HelpInputRune }} for help]{{color "reset"}} {{end}}
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}}
{{- color "white"}}{{if .Default}}(Y/n) {{else}}(y/N) {{end}}{{color "reset"}}
{{- end}}`

Expand All @@ -47,7 +46,7 @@ func yesNo(t bool) string {
return "No"
}

func (c *Confirm) getBool(showHelp bool) (bool, error) {
func (c *Confirm) getBool(showHelp bool, config *PromptConfig) (bool, error) {
cursor := c.NewCursor()
rr := c.NewRuneReader()
rr.SetTermMode()
Expand All @@ -72,10 +71,14 @@ func (c *Confirm) getBool(showHelp bool) (bool, error) {
answer = false
case val == "":
answer = c.Default
case val == string(core.HelpInputRune) && c.Help != "":
case val == string(config.HelpInput) && c.Help != "":
err := c.Render(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *c, ShowHelp: true},
ConfirmTemplateData{
Confirm: *c,
ShowHelp: true,
Config: config,
},
)
if err != nil {
// use the default value and bubble up
Expand All @@ -85,12 +88,16 @@ func (c *Confirm) getBool(showHelp bool) (bool, error) {
continue
default:
// we didnt get a valid answer, so print error and prompt again
if err := c.Error(fmt.Errorf("%q is not a valid answer, please try again.", val)); err != nil {
if err := c.Error(config, fmt.Errorf("%q is not a valid answer, please try again.", val)); err != nil {
return c.Default, err
}
err := c.Render(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *c, ShowHelp: showHelp},
ConfirmTemplateData{
Confirm: *c,
ShowHelp: showHelp,
Config: config,
},
)
if err != nil {
// use the default value and bubble up
Expand All @@ -116,23 +123,31 @@ func (c *Confirm) Prompt(config *PromptConfig) (interface{}, error) {
// render the question template
err := c.Render(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *c},
ConfirmTemplateData{
Confirm: *c,
Config: config,
},
)
if err != nil {
return "", err
}

// get input and return
return c.getBool(false)
return c.getBool(false, config)
}

// Cleanup overwrite the line with the finalized formatted version
func (c *Confirm) Cleanup(val interface{}) error {
func (c *Confirm) Cleanup(config *PromptConfig, val interface{}) error {
// if the value was previously true
ans := yesNo(val.(bool))

// render the template
return c.Render(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *c, Answer: ans},
ConfirmTemplateData{
Confirm: *c,
Answer: ans,
Config: config,
},
)
}
16 changes: 10 additions & 6 deletions confirm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,31 @@ func TestConfirmRender(t *testing.T) {
"Test Confirm question output with default true",
Confirm{Message: "Is pizza your favorite food?", Default: true},
ConfirmTemplateData{},
fmt.Sprintf("%s Is pizza your favorite food? (Y/n) ", core.QuestionIcon),
fmt.Sprintf("%s Is pizza your favorite food? (Y/n) ", defaultAskOptions().PromptConfig.Icons.Question),
},
{
"Test Confirm question output with default false",
Confirm{Message: "Is pizza your favorite food?", Default: false},
ConfirmTemplateData{},
fmt.Sprintf("%s Is pizza your favorite food? (y/N) ", core.QuestionIcon),
fmt.Sprintf("%s Is pizza your favorite food? (y/N) ", defaultAskOptions().PromptConfig.Icons.Question),
},
{
"Test Confirm answer output",
Confirm{Message: "Is pizza your favorite food?"},
ConfirmTemplateData{Answer: "Yes"},
fmt.Sprintf("%s Is pizza your favorite food? Yes\n", core.QuestionIcon),
fmt.Sprintf("%s Is pizza your favorite food? Yes\n", defaultAskOptions().PromptConfig.Icons.Question),
},
{
"Test Confirm with help but help message is hidden",
Confirm{Message: "Is pizza your favorite food?", Help: "This is helpful"},
ConfirmTemplateData{},
fmt.Sprintf("%s Is pizza your favorite food? [%s for help] (y/N) ", core.QuestionIcon, string(core.HelpInputRune)),
fmt.Sprintf("%s Is pizza your favorite food? [%s for help] (y/N) ", defaultAskOptions().PromptConfig.Icons.Question, string(defaultAskOptions().PromptConfig.HelpInput)),
},
{
"Test Confirm help output with help message shown",
Confirm{Message: "Is pizza your favorite food?", Help: "This is helpful"},
ConfirmTemplateData{ShowHelp: true},
fmt.Sprintf("%s This is helpful\n%s Is pizza your favorite food? (y/N) ", core.HelpIcon, core.QuestionIcon),
fmt.Sprintf("%s This is helpful\n%s Is pizza your favorite food? (y/N) ", defaultAskOptions().PromptConfig.Icons.Help, defaultAskOptions().PromptConfig.Icons.Question),
},
}

Expand All @@ -64,6 +64,10 @@ func TestConfirmRender(t *testing.T) {

test.prompt.WithStdio(terminal.Stdio{Out: w})
test.data.Confirm = test.prompt

// set the runtime config
test.data.Config = &defaultAskOptions().PromptConfig

err = test.prompt.Render(
ConfirmQuestionTemplate,
test.data,
Expand Down Expand Up @@ -128,7 +132,7 @@ func TestConfirmPrompt(t *testing.T) {
c.ExpectString(
fmt.Sprintf(
"Is pizza your favorite food? [%s for help] (y/N)",
string(core.HelpInputRune),
string(defaultAskOptions().PromptConfig.HelpInput),
),
)
c.SendLine("?")
Expand Down
71 changes: 14 additions & 57 deletions core/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,9 @@ import (
"github.com/mgutz/ansi"
)

// DisableColor can be used to make testing reliable
var DisableColor = false

var (
// HelpInputRune is the rune which the user should enter to trigger
// more detailed question help
HelpInputRune = '?'

// ErrorIcon will be be shown before an error
ErrorIcon = "X"

// HelpIcon will be shown before more detailed question help
HelpIcon = "?"
// QuestionIcon will be shown before a question Message
QuestionIcon = "?"

// MarkedOptionIcon will be prepended before a selected multiselect option
MarkedOptionIcon = "[x]"
// UnmarkedOptionIcon will be prepended before an unselected multiselect option
UnmarkedOptionIcon = "[ ]"

// SelectFocusIcon is prepended to an option to signify the user is
// currently focusing that option
SelectFocusIcon = ">"
)

var TemplateFuncs = map[string]interface{}{
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
"color": func(color string) string {
Expand All @@ -41,27 +19,19 @@ var TemplateFuncs = map[string]interface{}{
}
return ansi.ColorCode(color)
},
"HelpInputRune": func() string {
return string(HelpInputRune)
},
"ErrorIcon": func() string {
return ErrorIcon
},
"HelpIcon": func() string {
return HelpIcon
},
"QuestionIcon": func() string {
return QuestionIcon
},
"MarkedOptionIcon": func() string {
return MarkedOptionIcon
},
"UnmarkedOptionIcon": func() string {
return UnmarkedOptionIcon
},
"SelectFocusIcon": func() string {
return SelectFocusIcon
},
}

func RunTemplate(tmpl string, data interface{}) (string, error) {
t, err := getTemplate(tmpl)
if err != nil {
return "", err
}
buf := bytes.NewBufferString("")
err = t.Execute(buf, data)
if err != nil {
return "", err
}
return buf.String(), err
}

var (
Expand All @@ -88,16 +58,3 @@ func getTemplate(tmpl string) (*template.Template, error) {
memoMutex.Unlock()
return t, nil
}

func RunTemplate(tmpl string, data interface{}) (string, error) {
t, err := getTemplate(tmpl)
if err != nil {
return "", err
}
buf := bytes.NewBufferString("")
err = t.Execute(buf, data)
if err != nil {
return "", err
}
return buf.String(), err
}
Loading