1package survey 2 3import ( 4 "github.com/AlecAivazis/survey/v2/core" 5 "github.com/AlecAivazis/survey/v2/terminal" 6) 7 8/* 9Input is a regular text input that prints each character the user types on the screen 10and accepts the input with the enter key. Response type is a string. 11 12 name := "" 13 prompt := &survey.Input{ Message: "What is your name?" } 14 survey.AskOne(prompt, &name) 15*/ 16type Input struct { 17 Renderer 18 Message string 19 Default string 20 Help string 21 Suggest func(toComplete string) []string 22 typedAnswer string 23 answer string 24 options []core.OptionAnswer 25 selectedIndex int 26 showingHelp bool 27} 28 29// data available to the templates when processing 30type InputTemplateData struct { 31 Input 32 ShowAnswer bool 33 ShowHelp bool 34 Answer string 35 PageEntries []core.OptionAnswer 36 SelectedIndex int 37 Config *PromptConfig 38} 39 40// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format 41var InputQuestionTemplate = ` 42{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} 43{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} 44{{- color "default+hb"}}{{ .Message }} {{color "reset"}} 45{{- if .ShowAnswer}} 46 {{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}} 47{{- else if .PageEntries -}} 48 {{- .Answer}} [Use arrows to move, enter to select, type to continue] 49 {{- "\n"}} 50 {{- range $ix, $choice := .PageEntries}} 51 {{- if eq $ix $.SelectedIndex }}{{color $.Config.Icons.SelectFocus.Format }}{{ $.Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}} {{end}} 52 {{- $choice.Value}} 53 {{- color "reset"}}{{"\n"}} 54 {{- end}} 55{{- else }} 56 {{- if or (and .Help (not .ShowHelp)) .Suggest }}{{color "cyan"}}[ 57 {{- if and .Help (not .ShowHelp)}}{{ print .Config.HelpInput }} for help {{- if and .Suggest}}, {{end}}{{end -}} 58 {{- if and .Suggest }}{{color "cyan"}}{{ print .Config.SuggestInput }} for suggestions{{end -}} 59 ]{{color "reset"}} {{end}} 60 {{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}} 61 {{- .Answer -}} 62{{- end}}` 63 64func (i *Input) OnChange(key rune, config *PromptConfig) (bool, error) { 65 if key == terminal.KeyEnter || key == '\n' { 66 if i.answer != config.HelpInput || i.Help == "" { 67 // we're done 68 return true, nil 69 } else { 70 i.answer = "" 71 i.showingHelp = true 72 } 73 } else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine { 74 i.answer = "" 75 } else if key == terminal.KeyEscape && i.Suggest != nil { 76 if len(i.options) > 0 { 77 i.answer = i.typedAnswer 78 } 79 i.options = nil 80 } else if key == terminal.KeyArrowUp && len(i.options) > 0 { 81 if i.selectedIndex == 0 { 82 i.selectedIndex = len(i.options) - 1 83 } else { 84 i.selectedIndex-- 85 } 86 i.answer = i.options[i.selectedIndex].Value 87 } else if (key == terminal.KeyArrowDown || key == terminal.KeyTab) && len(i.options) > 0 { 88 if i.selectedIndex == len(i.options)-1 { 89 i.selectedIndex = 0 90 } else { 91 i.selectedIndex++ 92 } 93 i.answer = i.options[i.selectedIndex].Value 94 } else if key == terminal.KeyTab && i.Suggest != nil { 95 options := i.Suggest(i.answer) 96 i.selectedIndex = 0 97 i.typedAnswer = i.answer 98 if len(options) > 0 { 99 i.answer = options[0] 100 if len(options) == 1 { 101 i.options = nil 102 } else { 103 i.options = core.OptionAnswerList(options) 104 } 105 } 106 } else if key == terminal.KeyDelete || key == terminal.KeyBackspace { 107 if i.answer != "" { 108 runeAnswer := []rune(i.answer) 109 i.answer = string(runeAnswer[0 : len(runeAnswer)-1]) 110 } 111 } else if key >= terminal.KeySpace { 112 i.answer += string(key) 113 i.typedAnswer = i.answer 114 i.options = nil 115 } 116 117 pageSize := config.PageSize 118 opts, idx := paginate(pageSize, i.options, i.selectedIndex) 119 err := i.Render( 120 InputQuestionTemplate, 121 InputTemplateData{ 122 Input: *i, 123 Answer: i.answer, 124 ShowHelp: i.showingHelp, 125 SelectedIndex: idx, 126 PageEntries: opts, 127 Config: config, 128 }, 129 ) 130 131 return err != nil, err 132} 133 134func (i *Input) Prompt(config *PromptConfig) (interface{}, error) { 135 // render the template 136 err := i.Render( 137 InputQuestionTemplate, 138 InputTemplateData{ 139 Input: *i, 140 Config: config, 141 }, 142 ) 143 if err != nil { 144 return "", err 145 } 146 147 // start reading runes from the standard in 148 rr := i.NewRuneReader() 149 rr.SetTermMode() 150 defer rr.RestoreTermMode() 151 152 cursor := i.NewCursor() 153 cursor.Hide() // hide the cursor 154 defer cursor.Show() // show the cursor when we're done 155 156 // start waiting for input 157 for { 158 r, _, err := rr.ReadRune() 159 if err != nil { 160 return "", err 161 } 162 if r == terminal.KeyInterrupt { 163 return "", terminal.InterruptErr 164 } 165 if r == terminal.KeyEndTransmission { 166 break 167 } 168 169 b, err := i.OnChange(r, config) 170 if err != nil { 171 return "", err 172 } 173 174 if b { 175 break 176 } 177 } 178 179 // if the line is empty 180 if len(i.answer) == 0 { 181 // use the default value 182 return i.Default, err 183 } 184 185 lineStr := i.answer 186 187 i.AppendRenderedText(lineStr) 188 189 // we're done 190 return lineStr, err 191} 192 193func (i *Input) Cleanup(config *PromptConfig, val interface{}) error { 194 // use the default answer when cleaning up the prompt if necessary 195 ans := i.answer 196 if ans == "" && i.Default != "" { 197 ans = i.Default 198 } 199 200 // render the cleanup 201 return i.Render( 202 InputQuestionTemplate, 203 InputTemplateData{ 204 Input: *i, 205 ShowAnswer: true, 206 Config: config, 207 Answer: ans, 208 }, 209 ) 210} 211