tea/vendor/github.com/AlecAivazis/survey/v2/input.go

211 lines
5.3 KiB
Go
Raw Normal View History

package survey
import (
"github.com/AlecAivazis/survey/v2/core"
"github.com/AlecAivazis/survey/v2/terminal"
)
/*
Input is a regular text input that prints each character the user types on the screen
and accepts the input with the enter key. Response type is a string.
name := ""
prompt := &survey.Input{ Message: "What is your name?" }
survey.AskOne(prompt, &name)
*/
type Input struct {
Renderer
Message string
Default string
Help string
Suggest func(toComplete string) []string
typedAnswer string
answer string
options []core.OptionAnswer
selectedIndex int
showingHelp bool
}
// data available to the templates when processing
type InputTemplateData struct {
Input
ShowAnswer bool
ShowHelp bool
Answer string
PageEntries []core.OptionAnswer
SelectedIndex int
Config *PromptConfig
}
// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
var InputQuestionTemplate = `
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
{{- if .ShowAnswer}}
{{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
{{- else if .PageEntries -}}
{{- .Answer}} [Use arrows to move, enter to select, type to continue]
{{- "\n"}}
{{- range $ix, $choice := .PageEntries}}
{{- if eq $ix $.SelectedIndex }}{{color $.Config.Icons.SelectFocus.Format }}{{ $.Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}}
{{- $choice.Value}}
{{- color "reset"}}{{"\n"}}
{{- end}}
{{- else }}
{{- if or (and .Help (not .ShowHelp)) .Suggest }}{{color "cyan"}}[
{{- if and .Help (not .ShowHelp)}}{{ print .Config.HelpInput }} for help {{- if and .Suggest}}, {{end}}{{end -}}
{{- if and .Suggest }}{{color "cyan"}}{{ print .Config.SuggestInput }} for suggestions{{end -}}
]{{color "reset"}} {{end}}
{{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- .Answer -}}
{{- end}}`
func (i *Input) OnChange(key rune, config *PromptConfig) (bool, error) {
if key == terminal.KeyEnter || key == '\n' {
if i.answer != config.HelpInput || i.Help == "" {
// we're done
return true, nil
} else {
i.answer = ""
i.showingHelp = true
}
} else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine {
i.answer = ""
} else if key == terminal.KeyEscape && i.Suggest != nil {
if len(i.options) > 0 {
i.answer = i.typedAnswer
}
i.options = nil
} else if key == terminal.KeyArrowUp && len(i.options) > 0 {
if i.selectedIndex == 0 {
i.selectedIndex = len(i.options) - 1
} else {
i.selectedIndex--
}
i.answer = i.options[i.selectedIndex].Value
} else if (key == terminal.KeyArrowDown || key == terminal.KeyTab) && len(i.options) > 0 {
if i.selectedIndex == len(i.options)-1 {
i.selectedIndex = 0
} else {
i.selectedIndex++
}
i.answer = i.options[i.selectedIndex].Value
} else if key == terminal.KeyTab && i.Suggest != nil {
options := i.Suggest(i.answer)
i.selectedIndex = 0
i.typedAnswer = i.answer
if len(options) > 0 {
i.answer = options[0]
if len(options) == 1 {
i.options = nil
} else {
i.options = core.OptionAnswerList(options)
}
}
} else if key == terminal.KeyDelete || key == terminal.KeyBackspace {
if i.answer != "" {
runeAnswer := []rune(i.answer)
i.answer = string(runeAnswer[0 : len(runeAnswer)-1])
}
} else if key >= terminal.KeySpace {
i.answer += string(key)
i.typedAnswer = i.answer
i.options = nil
}
pageSize := config.PageSize
opts, idx := paginate(pageSize, i.options, i.selectedIndex)
err := i.Render(
InputQuestionTemplate,
InputTemplateData{
Input: *i,
Answer: i.answer,
ShowHelp: i.showingHelp,
SelectedIndex: idx,
PageEntries: opts,
Config: config,
},
)
return err != nil, err
}
func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {
// render the template
err := i.Render(
InputQuestionTemplate,
InputTemplateData{
Input: *i,
Config: config,
},
)
if err != nil {
return "", err
}
// start reading runes from the standard in
rr := i.NewRuneReader()
rr.SetTermMode()
defer rr.RestoreTermMode()
cursor := i.NewCursor()
cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done
// start waiting for input
for {
r, _, err := rr.ReadRune()
if err != nil {
return "", err
}
if r == terminal.KeyInterrupt {
return "", terminal.InterruptErr
}
if r == terminal.KeyEndTransmission {
break
}
b, err := i.OnChange(r, config)
if err != nil {
return "", err
}
if b {
break
}
}
// if the line is empty
if len(i.answer) == 0 {
// use the default value
return i.Default, err
}
lineStr := i.answer
i.AppendRenderedText(lineStr)
// we're done
return lineStr, err
}
func (i *Input) Cleanup(config *PromptConfig, val interface{}) error {
// use the default answer when cleaning up the prompt if necessary
ans := i.answer
if ans == "" && i.Default != "" {
ans = i.Default
}
// render the cleanup
return i.Render(
InputQuestionTemplate,
InputTemplateData{
Input: *i,
ShowAnswer: true,
Config: config,
Answer: ans,
},
)
}