README

Survey

Build Status GoDoc

A library for building interactive prompts.

package main

import (
    "fmt"
    "gopkg.in/AlecAivazis/survey.v1"
)

// the questions to ask
var qs = []*survey.Question{
    {
        Name:     "name",
        Prompt:   &survey.Input{Message: "What is your name?"},
        Validate: survey.Required,
        Transform: survey.Title,
    },
    {
        Name: "color",
        Prompt: &survey.Select{
            Message: "Choose a color:",
            Options: []string{"red", "blue", "green"},
            Default: "red",
        },
    },
    {
        Name: "age",
        Prompt:   &survey.Input{Message: "How old are you?"},
    },
}

func main() {
    // the answers will be written to this struct
    answers := struct {
        Name          string                  // survey will match the question and field names
        FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name
        Age           int                     // if the types don't match exactly, survey will try to convert for you
    }{}

    // perform the questions
    err := survey.Ask(qs, &answers)
    if err != nil {
        fmt.Println(err.Error())
        return
    }

    fmt.Printf("%s chose %s.", answers.Name, answers.FavoriteColor)
}

Table of Contents

  1. Examples
  2. Prompts
    1. Input
    2. Multiline
    3. Password
    4. Confirm
    5. Select
    6. MultiSelect
    7. Editor
  3. Validation
    1. Built-in Validators
  4. Help Text
    1. Changing the input rune
  5. Custom Types
  6. Customizing Output
  7. Versioning
  8. Testing

Examples

Examples can be found in the examples/ directory. Run them to see basic behavior:

go get gopkg.in/AlecAivazis/survey.v1

cd $GOPATH/src/gopkg.in/AlecAivazis/survey.v1

go run examples/simple.go
go run examples/validation.go

Prompts

Input
name := ""
prompt := &survey.Input{
    Message: "ping",
}
survey.AskOne(prompt, &name, nil)
Multiline
text := ""
prompt := &survey.Multiline{
    Message: "ping",
}
survey.AskOne(prompt, &text, nil)
Password

password := ""
prompt := &survey.Password{
    Message: "Please type your password",
}
survey.AskOne(prompt, &password, nil)
Confirm
name := false
prompt := &survey.Confirm{
    Message: "Do you like pie?",
}
survey.AskOne(prompt, &name, nil)
Select
color := ""
prompt := &survey.Select{
    Message: "Choose a color:",
    Options: []string{"red", "blue", "green"},
}
survey.AskOne(prompt, &color, nil)

The 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.

By default, the select prompt is limited to showing 7 options at a time and will paginate lists of options longer than that. To increase, you can either change the global survey.PageSize, or set the PageSize field on the prompt:

prompt := &survey.Select{..., PageSize: 10}
MultiSelect
days := []string{}
prompt := &survey.MultiSelect{
    Message: "What days do you prefer:",
    Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
}
survey.AskOne(prompt, &days, nil)

The 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.

By default, the MultiSelect prompt is limited to showing 7 options at a time and will paginate lists of options longer than that. To increase, you can either change the global survey.PageSize, or set the PageSize field on the prompt:

prompt := &survey.MultiSelect{..., PageSize: 10}
Editor

Launches the user's preferred editor (defined by the $EDITOR environment variable) on a temporary file. Once the user exits their editor, the contents of the temporary file are read in as the result. If neither of those are present, notepad (on Windows) or vim (Linux or Mac) is used.

Filtering options in Select and MultiSelect

The user can filter for options by typing while the prompt is active. This will filter out all options that don't contain the typed string anywhere in their name, ignoring case. This default filtering behavior is provided by the DefaultFilterFn function.

A custom filter function can also be provided to change this default behavior by providing a value for the FilterFn field:

&Select{
    Message: "Choose a color:",
    Options: []string{"red", "blue", "green"},
    FilterFn: func(filter string, options []string) (filtered []string) {
        result := DefaultFilterFn(filter, options)
        for _, v := range result {
            if len(v) >= 5 {
                filtered = append(filtered, v)
            }
        }
        return
    },
}

While the example above is contrived, this allows for use cases where "smarter" filtering might be useful, for example, when options are backed by more complex types and filtering might need to occur on more metadata than just the displayed name.

Validation

Validating individual responses for a particular question can be done by defining a Validate field on the survey.Question to be validated. This function takes an interface{} type and returns an error to show to the user, prompting them for another response:

q := &survey.Question{
    Prompt: &survey.Input{Message: "Hello world validation"},
    Validate: func (val interface{}) error {
        // since we are validating an Input, the assertion will always succeed
        if str, ok := val.(string) ; !ok || len(str) > 10 {
            return errors.New("This response cannot be longer than 10 characters.")
        }
	return nil
    },
}
Built-in Validators

survey comes prepackaged with a few validators to fit common situations. Currently these validators include:

name valid types description notes
Required any Rejects zero values of the response type Boolean values pass straight through since the zero value (false) is a valid response
MinLength(n) string Enforces that a response is at least the given length
MaxLength(n) string Enforces that a response is no longer than the given length

Help Text

All of the prompts have a Help field which can be defined to provide more information to your users:

&survey.Input{
    Message: "What is your phone number:",
    Help:    "Phone number should include the area code",
}
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:

import (
    "gopkg.in/AlecAivazis/survey.v1"
    surveyCore "gopkg.in/AlecAivazis/survey.v1/core"
)

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

surveyCore.HelpInputRune = '^'

survey.AskOne(prompt, &number, nil)

Custom Types

survey will assign prompt answers to your custom types if they implement this interface:

type settable interface {
    WriteAnswer(field string, value interface{}) error
}

Here is an example how to use them:

type MyValue struct {
    value string
}
func (my *MyValue) WriteAnswer(name string, value interface{}) error {
     my.value = value.(string)
}

myval := MyValue{}
survey.AskOne(
    &survey.Input{
        Message: "Enter something:",
    },
    &myval,
    nil,
)

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

Versioning

This project tries to maintain semantic GitHub releases as closely as possible and relies on gopkg.in to maintain those releases. Importing version 1 of survey would look like:

package main

import "gopkg.in/AlecAivazis/survey.v1"

Testing

You can test your program's interactive prompts using go-expect. The library can be used to expect a match on stdout and respond on stdin. Since os.Stdout in a go test process is not a TTY, if you are manipulating the cursor or using survey, you will need a way to interpret terminal / ANSI escape sequences for things like CursorLocation. vt10x.NewVT10XConsole will create a go-expect console that also multiplexes stdio to an in-memory virtual terminal.

For example, you can test a binary utilizing survey by connecting the Console's tty to a subprocess's stdio.

func TestCLI(t *testing.T) {
 	// Multiplex stdin/stdout to a virtual terminal to respond to ANSI escape
 	// sequences (i.e. cursor position report).
 	c, state, err := vt10x.NewVT10XConsole()
	require.Nil(t, err)
	defer c.Close()

	donec := make(chan struct{})
	go func() {
		defer close(donec)
    		c.ExpectString("What is your name?")
    		c.SendLine("Johnny Appleseed")
    		c.ExpectEOF()
  	}()

	cmd := exec.Command("your-cli")
  	cmd.Stdin = c.Tty()
  	cmd.Stdout = c.Tty()
  	cmd.Stderr = c.Tty()

  	err = cmd.Run()
  	require.Nil(t, err)

  	// Close the slave end of the pty, and read the remaining bytes from the master end.
  	c.Tty().Close()
  	<-donec

  	// Dump the terminal's screen.
  	t.Log(expect.StripTrailingEmptyLines(state.String()))
}

If your application is decoupled from os.Stdout and os.Stdin, you can even test through the tty alone. survey itself is tested in this manner.

func TestCLI(t *testing.T) {
  	// Multiplex stdin/stdout to a virtual terminal to respond to ANSI escape
  	// sequences (i.e. cursor position report).
	c, state, err := vt10x.NewVT10XConsole()
	require.Nil(t, err)
  	defer c.Close()

  	donec := make(chan struct{})
	go func() {
    		defer close(donec)
    		c.ExpectString("What is your name?")
    		c.SendLine("Johnny Appleseed")
    		c.ExpectEOF()
	}()

  	prompt := &Input{
    		Message: "What is your name?",
  	}
  	prompt.WithStdio(Stdio(c))

  	answer, err := prompt.Prompt()
  	require.Nil(t, err)
  	require.Equal(t, "Johnny Appleseed", answer)

  	// Close the slave end of the pty, and read the remaining bytes from the master end.
  	c.Tty().Close()
  	<-donec

  	// Dump the terminal's screen.
  	t.Log(expect.StripTrailingEmptyLines(state.String()))
}
Expand ▾ Collapse ▴

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ConfirmQuestionTemplate = `` /* 497-byte string literal not displayed */

    Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format

    View Source
    var DefaultAskOptions = AskOptions{
    	Stdio: terminal.Stdio{
    		In:  os.Stdin,
    		Out: os.Stdout,
    		Err: os.Stderr,
    	},
    }

      DefaultAskOptions is the default options on ask, using the OS stdio.

      View Source
      var DefaultFilterFn = func(filter string, options []string) (answer []string) {
      	filter = strings.ToLower(filter)
      	for _, o := range options {
      		if strings.Contains(strings.ToLower(o), filter) {
      			answer = append(answer, o)
      		}
      	}
      	return answer
      }
      View Source
      var EditorQuestionTemplate = `` /* 582-byte string literal not displayed */
      

        Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format

        View Source
        var InputQuestionTemplate = `` /* 496-byte string literal not displayed */
        

          Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format

          View Source
          var MultiSelectQuestionTemplate = `` /* 875-byte string literal not displayed */
          
          View Source
          var MultilineQuestionTemplate = `` /* 496-byte string literal not displayed */
          

            Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format

            View Source
            var PageSize = 7

              PageSize is the default maximum number of items to show in select/multiselect prompts

              View Source
              var PasswordQuestionTemplate = `` /* 318-byte string literal not displayed */
              

                Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format

                View Source
                var SelectQuestionTemplate = `` /* 736-byte string literal not displayed */
                

                Functions

                func Ask

                func Ask(qs []*Question, response interface{}, opts ...AskOpt) error

                  Ask performs the prompt loop, asking for validation when appropriate. The response type can be one of two options. If a struct is passed, the answer will be written to the field whose name matches the Name field on the corresponding question. Field types should be something that can be casted from the response type designated in the documentation. Note, a survey tag can also be used to identify a Otherwise, a map[string]interface{} can be passed, responses will be written to the key with the matching name. For example:

                  qs := []*survey.Question{
                  	{
                  		Name:     "name",
                  		Prompt:   &survey.Input{Message: "What is your name?"},
                  		Validate: survey.Required,
                  		Transform: survey.Title,
                  	},
                  }
                  
                  answers := struct{ Name string }{}
                  
                  err := survey.Ask(qs, &answers)
                  

                  func AskOne

                  func AskOne(p Prompt, response interface{}, v Validator, opts ...AskOpt) error

                    AskOne performs the prompt for a single prompt and asks for validation if required. Response types should be something that can be casted from the response type designated in the documentation. For example:

                    name := ""
                    prompt := &survey.Input{
                    	Message: "name",
                    }
                    
                    survey.AskOne(prompt, &name, nil)
                    

                    func Required

                    func Required(val interface{}) error

                      Required does not allow an empty value

                      func Title

                      func Title(ans interface{}) interface{}

                        Title is a `Transformer`. It receives an answer value and returns a copy of the "ans" with all Unicode letters that begin words mapped to their title case.

                        Note that if "ans" is not a string then it will return a nil value, meaning that the above answer will not be affected by this call at all.

                        func ToLower

                        func ToLower(ans interface{}) interface{}

                          ToLower is a `Transformer`. It receives an answer value and returns a copy of the "ans" with all Unicode letters mapped to their lower case.

                          Note that if "ans" is not a string then it will return a nil value, meaning that the above answer will not be affected by this call at all.

                          Types

                          type AskOpt

                          type AskOpt func(options *AskOptions) error

                            AskOpt allows setting optional ask options.

                            func WithStdio

                            func WithStdio(in terminal.FileReader, out terminal.FileWriter, err io.Writer) AskOpt

                              WithStdio specifies the standard input, output and error files survey interacts with. By default, these are os.Stdin, os.Stdout, and os.Stderr.

                              type AskOptions

                              type AskOptions struct {
                              	Stdio terminal.Stdio
                              }

                                AskOptions provides additional options on ask.

                                type Confirm

                                type Confirm struct {
                                	core.Renderer
                                	Message string
                                	Default bool
                                	Help    string
                                }

                                  Confirm is a regular text input that accept yes/no answers. Response type is a bool.

                                  func (*Confirm) Cleanup

                                  func (c *Confirm) Cleanup(val interface{}) error

                                    Cleanup overwrite the line with the finalized formatted version

                                    func (*Confirm) Prompt

                                    func (c *Confirm) Prompt() (interface{}, error)

                                      Prompt prompts the user with a simple text field and expects a reply followed by a carriage return.

                                      likesPie := false
                                      prompt := &survey.Confirm{ Message: "What is your name?" }
                                      survey.AskOne(prompt, &likesPie, nil)
                                      

                                      type ConfirmTemplateData

                                      type ConfirmTemplateData struct {
                                      	Confirm
                                      	Answer   string
                                      	ShowHelp bool
                                      }

                                        data available to the templates when processing

                                        type Editor

                                        type Editor struct {
                                        	core.Renderer
                                        	Message       string
                                        	Default       string
                                        	Help          string
                                        	Editor        string
                                        	HideDefault   bool
                                        	AppendDefault bool
                                        }

                                          Editor launches an instance of the users preferred editor on a temporary file. The editor to use is determined by reading the $VISUAL or $EDITOR environment variables. If neither of those are present, notepad (on Windows) or vim (others) is used. The launch of the editor is triggered by the enter key. Since the response may be long, it will not be echoed as Input does, instead, it print <Received>. Response type is a string.

                                          message := ""
                                          prompt := &survey.Editor{ Message: "What is your commit message?" }
                                          survey.AskOne(prompt, &message, nil)
                                          

                                          func (*Editor) Cleanup

                                          func (e *Editor) Cleanup(val interface{}) error

                                          func (*Editor) Prompt

                                          func (e *Editor) Prompt() (interface{}, error)

                                          func (*Editor) PromptAgain

                                          func (e *Editor) PromptAgain(invalid interface{}, err error) (interface{}, error)

                                          type EditorTemplateData

                                          type EditorTemplateData struct {
                                          	Editor
                                          	Answer     string
                                          	ShowAnswer bool
                                          	ShowHelp   bool
                                          }

                                            data available to the templates when processing

                                            type Input

                                            type Input struct {
                                            	core.Renderer
                                            	Message string
                                            	Default string
                                            	Help    string
                                            }

                                              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, nil)
                                              

                                              func (*Input) Cleanup

                                              func (i *Input) Cleanup(val interface{}) error

                                              func (*Input) Prompt

                                              func (i *Input) Prompt() (interface{}, error)

                                              type InputTemplateData

                                              type InputTemplateData struct {
                                              	Input
                                              	Answer     string
                                              	ShowAnswer bool
                                              	ShowHelp   bool
                                              }

                                                data available to the templates when processing

                                                type MultiSelect

                                                type MultiSelect struct {
                                                	core.Renderer
                                                	Message       string
                                                	Options       []string
                                                	Default       []string
                                                	Help          string
                                                	PageSize      int
                                                	VimMode       bool
                                                	FilterMessage string
                                                	FilterFn      func(string, []string) []string
                                                	// contains filtered or unexported fields
                                                }

                                                  MultiSelect is a prompt that presents a list of various options to the user for them to select using the arrow keys and enter. Response type is a slice of strings.

                                                  days := []string{}
                                                  prompt := &survey.MultiSelect{
                                                  	Message: "What days do you prefer:",
                                                  	Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
                                                  }
                                                  survey.AskOne(prompt, &days, nil)
                                                  

                                                  func (*MultiSelect) Cleanup

                                                  func (m *MultiSelect) Cleanup(val interface{}) error

                                                    Cleanup removes the options section, and renders the ask like a normal question.

                                                    func (*MultiSelect) OnChange

                                                    func (m *MultiSelect) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)

                                                      OnChange is called on every keypress.

                                                      func (*MultiSelect) Prompt

                                                      func (m *MultiSelect) Prompt() (interface{}, error)

                                                      type MultiSelectTemplateData

                                                      type MultiSelectTemplateData struct {
                                                      	MultiSelect
                                                      	Answer        string
                                                      	ShowAnswer    bool
                                                      	Checked       map[string]bool
                                                      	SelectedIndex int
                                                      	ShowHelp      bool
                                                      	PageEntries   []string
                                                      }

                                                        data available to the templates when processing

                                                        type Multiline

                                                        type Multiline struct {
                                                        	core.Renderer
                                                        	Message string
                                                        	Default string
                                                        	Help    string
                                                        }

                                                        func (*Multiline) Cleanup

                                                        func (i *Multiline) Cleanup(val interface{}) error

                                                        func (*Multiline) Prompt

                                                        func (i *Multiline) Prompt() (interface{}, error)

                                                        type MultilineTemplateData

                                                        type MultilineTemplateData struct {
                                                        	Multiline
                                                        	Answer     string
                                                        	ShowAnswer bool
                                                        	ShowHelp   bool
                                                        }

                                                          data available to the templates when processing

                                                          type Password

                                                          type Password struct {
                                                          	core.Renderer
                                                          	Message string
                                                          	Help    string
                                                          }

                                                            Password is like a normal Input but the text shows up as *'s and there is no default. Response type is a string.

                                                            password := ""
                                                            prompt := &survey.Password{ Message: "Please type your password" }
                                                            survey.AskOne(prompt, &password, nil)
                                                            

                                                            func (*Password) Cleanup

                                                            func (prompt *Password) Cleanup(val interface{}) error

                                                              Cleanup hides the string with a fixed number of characters.

                                                              func (*Password) Prompt

                                                              func (p *Password) Prompt() (line interface{}, err error)

                                                              type PasswordTemplateData

                                                              type PasswordTemplateData struct {
                                                              	Password
                                                              	ShowHelp bool
                                                              }

                                                              type Prompt

                                                              type Prompt interface {
                                                              	Prompt() (interface{}, error)
                                                              	Cleanup(interface{}) error
                                                              	Error(error) error
                                                              }

                                                                Prompt is the primary interface for the objects that can take user input and return a response.

                                                                type PromptAgainer

                                                                type PromptAgainer interface {
                                                                	PromptAgain(invalid interface{}, err error) (interface{}, error)
                                                                }

                                                                  PromptAgainer Interface for Prompts that support prompting again after invalid input

                                                                  type Question

                                                                  type Question struct {
                                                                  	Name      string
                                                                  	Prompt    Prompt
                                                                  	Validate  Validator
                                                                  	Transform Transformer
                                                                  }

                                                                    Question is the core data structure for a survey questionnaire.

                                                                    type Select

                                                                    type Select struct {
                                                                    	core.Renderer
                                                                    	Message       string
                                                                    	Options       []string
                                                                    	Default       string
                                                                    	Help          string
                                                                    	PageSize      int
                                                                    	VimMode       bool
                                                                    	FilterMessage string
                                                                    	FilterFn      func(string, []string) []string
                                                                    	// contains filtered or unexported fields
                                                                    }

                                                                      Select is a prompt that presents a list of various options to the user for them to select using the arrow keys and enter. Response type is a string.

                                                                      color := ""
                                                                      prompt := &survey.Select{
                                                                      	Message: "Choose a color:",
                                                                      	Options: []string{"red", "blue", "green"},
                                                                      }
                                                                      survey.AskOne(prompt, &color, nil)
                                                                      

                                                                      func (*Select) Cleanup

                                                                      func (s *Select) Cleanup(val interface{}) error

                                                                      func (*Select) OnChange

                                                                      func (s *Select) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool)

                                                                        OnChange is called on every keypress.

                                                                        func (*Select) Prompt

                                                                        func (s *Select) Prompt() (interface{}, error)

                                                                        type SelectTemplateData

                                                                        type SelectTemplateData struct {
                                                                        	Select
                                                                        	PageEntries   []string
                                                                        	SelectedIndex int
                                                                        	Answer        string
                                                                        	ShowAnswer    bool
                                                                        	ShowHelp      bool
                                                                        }

                                                                          the data available to the templates when processing

                                                                          type Transformer

                                                                          type Transformer func(ans interface{}) (newAns interface{})

                                                                            Transformer is a function passed to a Question after a user has provided a response. The function can be used to implement a custom logic that will result to return a different representation of the given answer.

                                                                            Look `TransformString`, `ToLower` `Title` and `ComposeTransformers` for more.

                                                                            func ComposeTransformers

                                                                            func ComposeTransformers(transformers ...Transformer) Transformer

                                                                              ComposeTransformers is a variadic function used to create one transformer from many.

                                                                              func TransformString

                                                                              func TransformString(f func(s string) string) Transformer

                                                                                TransformString returns a `Transformer` based on the "f" function which accepts a string representation of the answer and returns a new one, transformed, answer. Take for example the functions inside the std `strings` package, they can be converted to a compatible `Transformer` by using this function, i.e: `TransformString(strings.Title)`, `TransformString(strings.ToUpper)`.

                                                                                Note that `TransformString` is just a helper, `Transformer` can be used to transform any type of answer.

                                                                                type Validator

                                                                                type Validator func(ans interface{}) error

                                                                                  Validator is a function passed to a Question after a user has provided a response. If the function returns an error, then the user will be prompted again for another response.

                                                                                  func ComposeValidators

                                                                                  func ComposeValidators(validators ...Validator) Validator

                                                                                    ComposeValidators is a variadic function used to create one validator from many.

                                                                                    func MaxLength

                                                                                    func MaxLength(length int) Validator

                                                                                      MaxLength requires that the string is no longer than the specified value

                                                                                      func MinLength

                                                                                      func MinLength(length int) Validator

                                                                                        MinLength requires that the string is longer or equal in length to the specified value

                                                                                        Directories

                                                                                        Path Synopsis