• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..19-Feb-2022-

core/H19-Feb-2022-449308

terminal/H03-May-2022-1,393989

.travis.ymlH A D19-Feb-2022264 2316

CONTRIBUTING.mdH A D19-Feb-20224.8 KiB7854

Gopkg.lockH A D19-Feb-20221.9 KiB7965

Gopkg.tomlH A D19-Feb-20221.1 KiB5546

LICENSEH A D19-Feb-20221 KiB2217

README.mdH A D19-Feb-202215.2 KiB473363

_tasks.hclH A D19-Feb-2022361 2016

confirm.goH A D19-Feb-20223.6 KiB154116

editor.goH A D19-Feb-20225.2 KiB223165

filter.goH A D19-Feb-202215 21

go.modH A D19-Feb-2022719 2017

go.sumH A D19-Feb-20223 KiB3231

input.goH A D19-Feb-20225.3 KiB210171

multiline.goH A D19-Feb-20222.4 KiB11188

multiselect.goH A D19-Feb-20228.7 KiB328236

password.goH A D19-Feb-20222.4 KiB10272

renderer.goH A D19-Feb-20224 KiB158105

select.goH A D19-Feb-20228.6 KiB329218

survey.goH A D19-Feb-202210.1 KiB401241

transform.goH A D19-Feb-20222.3 KiB7733

validate.goH A D19-Feb-20222.5 KiB8854

README.md

1# Survey
2
3[![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey)
4[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://pkg.go.dev/github.com/AlecAivazis/survey/v2)
5
6A library for building interactive prompts.
7
8<img width="550" src="https://thumbs.gfycat.com/VillainousGraciousKouprey-size_restricted.gif"/>
9
10```go
11package main
12
13import (
14    "fmt"
15    "github.com/AlecAivazis/survey/v2"
16)
17
18// the questions to ask
19var qs = []*survey.Question{
20    {
21        Name:     "name",
22        Prompt:   &survey.Input{Message: "What is your name?"},
23        Validate: survey.Required,
24        Transform: survey.Title,
25    },
26    {
27        Name: "color",
28        Prompt: &survey.Select{
29            Message: "Choose a color:",
30            Options: []string{"red", "blue", "green"},
31            Default: "red",
32        },
33    },
34    {
35        Name: "age",
36        Prompt:   &survey.Input{Message: "How old are you?"},
37    },
38}
39
40func main() {
41    // the answers will be written to this struct
42    answers := struct {
43        Name          string                  // survey will match the question and field names
44        FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name
45        Age           int                     // if the types don't match, survey will convert it
46    }{}
47
48    // perform the questions
49    err := survey.Ask(qs, &answers)
50    if err != nil {
51        fmt.Println(err.Error())
52        return
53    }
54
55    fmt.Printf("%s chose %s.", answers.Name, answers.FavoriteColor)
56}
57```
58
59## Table of Contents
60
611. [Examples](#examples)
621. [Running the Prompts](#running-the-prompts)
631. [Prompts](#prompts)
64   1. [Input](#input)
65      1. [Suggestion Options](#suggestion-options)
66   1. [Multiline](#multiline)
67   1. [Password](#password)
68   1. [Confirm](#confirm)
69   1. [Select](#select)
70   1. [MultiSelect](#multiselect)
71   1. [Editor](#editor)
721. [Filtering Options](#filtering-options)
731. [Validation](#validation)
74   1. [Built-in Validators](#built-in-validators)
751. [Help Text](#help-text)
76   1. [Changing the input rune](#changing-the-input-rune)
771. [Changing the Icons ](#changing-the-icons)
781. [Custom Types](#custom-types)
791. [Testing](#testing)
801. [FAQ](#faq)
81
82## Examples
83
84Examples can be found in the `examples/` directory. Run them
85to see basic behavior:
86
87```bash
88go get github.com/AlecAivazis/survey/v2
89
90cd $GOPATH/src/github.com/AlecAivazis/survey
91
92go run examples/simple.go
93go run examples/validation.go
94```
95
96## Running the Prompts
97
98There are two primary ways to execute prompts and start collecting information from your users: `Ask` and
99`AskOne`. The primary difference is whether you are interested in collecting a single piece of information
100or if you have a list of questions to ask whose answers should be collected in a single struct.
101For most basic usecases, `Ask` should be enough. However, for surveys with complicated branching logic,
102we recommend that you break out your questions into multiple calls to both of these functions to fit your needs.
103
104### Configuring the Prompts
105
106Most prompts take fine-grained configuration through fields on the structs you instantiate. It is also
107possible to change survey's default behaviors by passing `AskOpts` to either `Ask` or `AskOne`. Examples
108in this document will do both interchangeably:
109
110```golang
111prompt := &Select{
112    Message: "Choose a color:",
113    Options: []string{"red", "blue", "green"},
114    // can pass a validator directly
115    Validate: survey.Required,
116}
117
118// or define a default for the single call to `AskOne`
119// the answer will get written to the color variable
120survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
121
122// or define a default for every entry in a list of questions
123// the answer will get copied into the matching field of the struct as shown above
124survey.Ask(questions, &answers, survey.WithValidator(survey.Required))
125```
126
127## Prompts
128
129### Input
130
131<img src="https://thumbs.gfycat.com/LankyBlindAmericanpainthorse-size_restricted.gif" width="400px"/>
132
133```golang
134name := ""
135prompt := &survey.Input{
136    Message: "ping",
137}
138survey.AskOne(prompt, &name)
139```
140
141#### Suggestion Options
142
143<img src="https://i.imgur.com/Q7POpA1.gif" width="800px"/>
144
145```golang
146file := ""
147prompt := &survey.Input{
148    Message: "inform a file to save:",
149    Suggest: func (toComplete string) []string {
150        files, _ := filepath.Glob(toComplete + "*")
151        return files
152    },
153}
154}
155survey.AskOne(prompt, &file)
156```
157
158### Multiline
159
160<img src="https://thumbs.gfycat.com/ImperfectShimmeringBeagle-size_restricted.gif" width="400px"/>
161
162```golang
163text := ""
164prompt := &survey.Multiline{
165    Message: "ping",
166}
167survey.AskOne(prompt, &text)
168```
169
170### Password
171
172<img src="https://thumbs.gfycat.com/CompassionateSevereHypacrosaurus-size_restricted.gif" width="400px" />
173
174```golang
175password := ""
176prompt := &survey.Password{
177    Message: "Please type your password",
178}
179survey.AskOne(prompt, &password)
180```
181
182### Confirm
183
184<img src="https://thumbs.gfycat.com/UnkemptCarefulGermanpinscher-size_restricted.gif" width="400px"/>
185
186```golang
187name := false
188prompt := &survey.Confirm{
189    Message: "Do you like pie?",
190}
191survey.AskOne(prompt, &name)
192```
193
194### Select
195
196<img src="https://thumbs.gfycat.com/GrimFilthyAmazonparrot-size_restricted.gif" width="450px"/>
197
198```golang
199color := ""
200prompt := &survey.Select{
201    Message: "Choose a color:",
202    Options: []string{"red", "blue", "green"},
203}
204survey.AskOne(prompt, &color)
205```
206
207Fields and values that come from a `Select` prompt can be one of two different things. If you pass an `int`
208the field will have the value of the selected index. If you instead pass a string, the string value selected
209will be written to the field.
210
211The user can also press `esc` to toggle the ability cycle through the options with the j and k keys to do down and up respectively.
212
213By default, the select prompt is limited to showing 7 options at a time
214and will paginate lists of options longer than that. This can be changed a number of ways:
215
216```golang
217// as a field on a single select
218prompt := &survey.MultiSelect{..., PageSize: 10}
219
220// or as an option to Ask or AskOne
221survey.AskOne(prompt, &days, survey.WithPageSize(10))
222```
223
224### MultiSelect
225
226![Example](img/multi-select-all-none.gif)
227
228```golang
229days := []string{}
230prompt := &survey.MultiSelect{
231    Message: "What days do you prefer:",
232    Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
233}
234survey.AskOne(prompt, &days)
235```
236
237Fields and values that come from a `MultiSelect` prompt can be one of two different things. If you pass an `int`
238the field will have a slice of the selected indices. If you instead pass a string, a slice of the string values
239selected will be written to the field.
240
241The user can also press `esc` to toggle the ability cycle through the options with the j and k keys to do down and up respectively.
242
243By default, the MultiSelect prompt is limited to showing 7 options at a time
244and will paginate lists of options longer than that. This can be changed a number of ways:
245
246```golang
247// as a field on a single select
248prompt := &survey.MultiSelect{..., PageSize: 10}
249
250// or as an option to Ask or AskOne
251survey.AskOne(prompt, &days, survey.WithPageSize(10))
252```
253
254### Editor
255
256Launches the user's preferred editor (defined by the \$VISUAL or \$EDITOR environment variables) on a
257temporary file. Once the user exits their editor, the contents of the temporary file are read in as
258the result. If neither of those are present, notepad (on Windows) or vim (Linux or Mac) is used.
259
260You can also specify a [pattern](https://golang.org/pkg/io/ioutil/#TempFile) for the name of the temporary file. This
261can be useful for ensuring syntax highlighting matches your usecase.
262
263```golang
264prompt := &survey.Editor{
265    Message: "Shell code snippet",
266    FileName: "*.sh",
267}
268
269survey.AskOne(prompt, &content)
270```
271
272## Filtering Options
273
274By default, the user can filter for options in Select and MultiSelects by typing while the prompt
275is active. This will filter out all options that don't contain the typed string anywhere in their name, ignoring case.
276
277A custom filter function can also be provided to change this behavior:
278
279```golang
280func myFilter(filterValue string, optValue string, optIndex int) bool {
281    // only include the option if it includes the filter and has length greater than 5
282    return strings.Contains(optValue, filterValue) && len(optValue) >= 5
283}
284
285// configure it for a specific prompt
286&Select{
287    Message: "Choose a color:",
288    Options: []string{"red", "blue", "green"},
289    Filter: myFilter,
290}
291
292// or define a default for all of the questions
293survey.AskOne(prompt, &color, survey.WithFilter(myFilter))
294```
295
296## Keeping the filter active
297
298By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone.
299
300However the user can prevent this from happening and keep the filter active for multiple selections in a e.g. MultiSelect:
301
302```golang
303// configure it for a specific prompt
304&Select{
305    Message:    "Choose a color:",
306    Options:    []string{"light-green", "green", "dark-green", "red"},
307    KeepFilter: true,
308}
309
310// or define a default for all of the questions
311survey.AskOne(prompt, &color, survey.WithKeepFilter(true))
312```
313
314## Validation
315
316Validating individual responses for a particular question can be done by defining a
317`Validate` field on the `survey.Question` to be validated. This function takes an
318`interface{}` type and returns an error to show to the user, prompting them for another
319response. Like usual, validators can be provided directly to the prompt or with `survey.WithValidator`:
320
321```golang
322q := &survey.Question{
323    Prompt: &survey.Input{Message: "Hello world validation"},
324    Validate: func (val interface{}) error {
325        // since we are validating an Input, the assertion will always succeed
326        if str, ok := val.(string) ; !ok || len(str) > 10 {
327            return errors.New("This response cannot be longer than 10 characters.")
328        }
329	return nil
330    },
331}
332
333color := ""
334prompt := &survey.Input{ Message: "Whats your name?" }
335
336// you can pass multiple validators here and survey will make sure each one passes
337survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
338```
339
340### Built-in Validators
341
342`survey` comes prepackaged with a few validators to fit common situations. Currently these
343validators include:
344
345| name         | valid types | description                                                 | notes                                                                                 |
346| ------------ | ----------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------- |
347| Required     | any         | Rejects zero values of the response type                    | Boolean values pass straight through since the zero value (false) is a valid response |
348| MinLength(n) | string      | Enforces that a response is at least the given length       |                                                                                       |
349| MaxLength(n) | string      | Enforces that a response is no longer than the given length |                                                                                       |
350
351## Help Text
352
353All of the prompts have a `Help` field which can be defined to provide more information to your users:
354
355<img src="https://thumbs.gfycat.com/CloudyRemorsefulFossa-size_restricted.gif" width="400px" style="margin-top: 8px"/>
356
357```golang
358&survey.Input{
359    Message: "What is your phone number:",
360    Help:    "Phone number should include the area code",
361}
362```
363
364### Changing the input rune
365
366In some situations, `?` is a perfectly valid response. To handle this, you can change the rune that survey
367looks for with `WithHelpInput`:
368
369```golang
370import (
371    "github.com/AlecAivazis/survey/v2"
372)
373
374number := ""
375prompt := &survey.Input{
376    Message: "If you have this need, please give me a reasonable message.",
377    Help:    "I couldn't come up with one.",
378}
379
380survey.AskOne(prompt, &number, survey.WithHelpInput('^'))
381```
382
383## Changing the Icons
384
385Changing the icons and their color/format can be done by passing the `WithIcons` option. The format
386follows the patterns outlined [here](https://github.com/mgutz/ansi#style-format). For example:
387
388```golang
389import (
390    "github.com/AlecAivazis/survey/v2"
391)
392
393number := ""
394prompt := &survey.Input{
395    Message: "If you have this need, please give me a reasonable message.",
396    Help:    "I couldn't come up with one.",
397}
398
399survey.AskOne(prompt, &number, survey.WithIcons(func(icons *survey.IconSet) {
400    // you can set any icons
401    icons.Question.Text = "⁇"
402    // for more information on formatting the icons, see here: https://github.com/mgutz/ansi#style-format
403    icons.Question.Format = "yellow+hb"
404}))
405```
406
407The icons and their default text and format are summarized below:
408
409| name           | text | format     | description                                                   |
410| -------------- | ---- | ---------- | ------------------------------------------------------------- |
411| Error          | X    | red        | Before an error                                               |
412| Help           | i    | cyan       | Before help text                                              |
413| Question       | ?    | green+hb   | Before the message of a prompt                                |
414| SelectFocus    | >    | green      | Marks the current focus in `Select` and `MultiSelect` prompts |
415| UnmarkedOption | [ ]  | default+hb | Marks an unselected option in a `MultiSelect` prompt          |
416| MarkedOption   | [x]  | cyan+b     | Marks a chosen selection in a `MultiSelect` prompt            |
417
418## Custom Types
419
420survey will assign prompt answers to your custom types if they implement this interface:
421
422```golang
423type Settable interface {
424    WriteAnswer(field string, value interface{}) error
425}
426```
427
428Here is an example how to use them:
429
430```golang
431type MyValue struct {
432    value string
433}
434func (my *MyValue) WriteAnswer(name string, value interface{}) error {
435     my.value = value.(string)
436}
437
438myval := MyValue{}
439survey.AskOne(
440    &survey.Input{
441        Message: "Enter something:",
442    },
443    &myval
444)
445```
446
447## Testing
448
449You can test your program's interactive prompts using [go-expect](https://github.com/Netflix/go-expect). The library
450can be used to expect a match on stdout and respond on stdin. Since `os.Stdout` in a `go test` process is not a TTY,
451if you are manipulating the cursor or using `survey`, you will need a way to interpret terminal / ANSI escape sequences
452for things like `CursorLocation`. `vt10x.NewVT10XConsole` will create a `go-expect` console that also multiplexes
453stdio to an in-memory [virtual terminal](https://github.com/hinshun/vt10x).
454
455For some examples, you can see any of the tests in this repo.
456
457## FAQ
458
459### Why isn't sending a SIGINT (aka. CTRL-C) signal working?
460
461When you send an interrupt signal to the process, it only interrupts the current prompt instead of the entire process. This manifests in a `github.com/AlecAivazis/survey/v2/terminal.InterruptErr` being returned from `Ask` and `AskOne`. If you want to stop the process, handle the returned error in your code:
462
463```go
464err := survey.AskOne(prompt, &myVar)
465if err == terminal.InterruptErr {
466	fmt.Println("interrupted")
467
468	os.Exit(0)
469} else if err != nil {
470	panic(err)
471}
472```
473