Repository: AlecAivazis/survey
Pull request #217 (2019-06-02 13:06:51)

fix(cursor): fix terminal cursor move display
### fix terminal cursor move display

**I only completed the test on my terminal (iTerm2).** 

![2019-06-02 20 48 53](https://user-images.githubusercontent.com/13043245/58761738-10733200-857a-11e9-83ae-354c95c4a8a9.gif)


In the test I found that Goland's terminal seems to have problems with emoji:

Use the test string: `啊啊啊啊aaaa😀😀😀😀啊啊啊啊啊啊`

When the cursor is in the first position of emoji, the cursor appears to be abnormally jumped when trying to delete the letter `a`

- Goland
![2019-06-02 21 05 23](https://user-images.githubusercontent.com/13043245/58761761-3ef10d00-857a-11e9-9961-5a576c8670bf.gif)

- iTerm2
![2019-06-02 21 05 44](https://user-images.githubusercontent.com/13043245/58761764-40bad080-857a-11e9-9a1f-2351f77f350b.gif)


Signed-off-by: mritd <mritd@linux.com>

mritd: issue: refs #126
pr: refs #180
AlecAivazis: Thanks for re-opening this @mritd! Just to be clear, does your PR fix the issue that you are highlighting or is the issue still present?
mritd: @AlecAivazis Thank you for your reply, currently passed in the iTerm2 terminal test, some special terminals (like Goland termianl) have problems with emoji (I think this is Goland's problem)
mritd: Passed the test on the mac native terminal.

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bf6562e..a84e060 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -32,7 +32,7 @@ Bugs are tracked using the Github Issue tracker. When filing a bug, please remem
 
 ## Suggesting an API change
 
-If you have an idea, I'm more than happy to discuss it. Please open an issue labeled `Discussion` and we can work through it. In order to maintain some sense of stability, additions to the top-level API are taken just as seriously as changes that break it. Adding stuff is much easier than removing it.
+If you have an idea, I'm more than happy to discuss it. Please open an issue and we can work through it. In order to maintain some sense of stability, additions to the top-level API are taken just as seriously as changes that break it. Adding stuff is much easier than removing it.
 
 ## Submitting a contribution
 
diff --git a/README.md b/README.md
index 5ac53ff..cd711d9 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,6 @@
 
 A library for building interactive prompts.
 
-
 <img width="550" src="https://thumbs.gfycat.com/VillainousGraciousKouprey-size_restricted.gif"/>
 
 ```go
@@ -43,7 +42,7 @@ func main() {
     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
+        Age           int                     // if the types don't match, survey will convert it
     }{}
 
     // perform the questions
@@ -57,12 +56,10 @@ func main() {
 }
 ```
 
-*NOTE*: This page describes the API for the upcoming `v2` release which has not been made available yet. For documentation
-for the old `v1` version, see [here](https://godoc.org/gopkg.in/AlecAivazis/survey.v1).
-
 ## Table of Contents
 
 1. [Examples](#examples)
+1. [Running the Prompts](#running-the-prompts)
 1. [Prompts](#prompts)
    1. [Input](#input)
    1. [Multiline](#multiline)
@@ -71,12 +68,13 @@ for the old `v1` version, see [here](https://godoc.org/gopkg.in/AlecAivazis/surv
    1. [Select](#select)
    1. [MultiSelect](#multiselect)
    1. [Editor](#editor)
+1. [Filtering Options](#filtering-options)
 1. [Validation](#validation)
    1. [Built-in Validators](#built-in-validators)
 1. [Help Text](#help-text)
-   1. [Changing the input rune](#changing-the-input-run)
+   1. [Changing the input rune](#changing-the-input-rune)
+1. [Changing the Icons ](#changing-the-icons)
 1. [Custom Types](#custom-types)
-1. [Customizing Output](#customizing-output)
 1. [Testing](#testing)
 
 ## Examples
@@ -85,14 +83,45 @@ Examples can be found in the `examples/` directory. Run them
 to see basic behavior:
 
 ```bash
-go get github.com/AlecAivazis/survey/v2
+go get github.com/AlecAivazis/survey
 
-cd $GOPATH/src/github.com/AlecAivazis/survey/v2
+cd $GOPATH/src/github.com/AlecAivazis/survey
 
 go run examples/simple.go
 go run examples/validation.go
 ```
 
+## Running the Prompts
+
+There are two primary ways to execute prompts and start collecting information from your users: `Ask` and
+`AskOne`. The primary difference is whether you are interested in collecting a single piece of information
+or if you have a list of questions to ask whose answers should be collected in a single struct.
+For most basic usecases, `Ask` should be enough. However, for surveys with complicated branching logic,
+we recommend that you break out your questions into multiple calls to both of these functions to fit your needs.
+
+### Configuring the Prompts
+
+Most prompts take fine-grained configuration through fields on the structs you instantiate. It is also
+possible to change survey's default behaviors by passing `AskOpts` to either `Ask` or `AskOne`. Examples
+in this document will do both interchangeably:
+
+```golang
+prompt := &Select{
+    Message: "Choose a color:",
+    Options: []string{"red", "blue", "green"},
+    // can pass a validator directly
+    Validate: survey.Required,
+}
+
+// or define a default for the single call to `AskOne`
+// the answer will get written to the color variable
+survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
+
+// or define a default for every entry in a list of questions
+// the answer will get copied into the matching field of the struct as shown above
+survey.Ask(questions, &answers, survey.WithValidator(survey.Required))
+```
+
 ## Prompts
 
 ### Input
@@ -104,7 +133,7 @@ name := ""
 prompt := &survey.Input{
     Message: "ping",
 }
-survey.AskOne(prompt, &name, nil)
+survey.AskOne(prompt, &name)
 ```
 
 ### Multiline
@@ -116,7 +145,7 @@ text := ""
 prompt := &survey.Multiline{
     Message: "ping",
 }
-survey.AskOne(prompt, &text, nil)
+survey.AskOne(prompt, &text)
 ```
 
 ### Password
@@ -128,7 +157,7 @@ password := ""
 prompt := &survey.Password{
     Message: "Please type your password",
 }
-survey.AskOne(prompt, &password, nil)
+survey.AskOne(prompt, &password)
 ```
 
 ### Confirm
@@ -140,7 +169,7 @@ name := false
 prompt := &survey.Confirm{
     Message: "Do you like pie?",
 }
-survey.AskOne(prompt, &name, nil)
+survey.AskOne(prompt, &name)
 ```
 
 ### Select
@@ -153,17 +182,24 @@ prompt := &survey.Select{
     Message: "Choose a color:",
     Options: []string{"red", "blue", "green"},
 }
-survey.AskOne(prompt, &color, nil)
+survey.AskOne(prompt, &color)
 ```
 
+Fields and values that come from a `Select` prompt can be one of two different things. If you pass an `int`
+the field will have the value of the selected index. If you instead pass a string, the string value selected
+will be written to the field.
+
 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:
+and will paginate lists of options longer than that. This can be changed a number of ways:
 
 ```golang
-prompt := &survey.Select{..., PageSize: 10}
+// as a field on a single select
+prompt := &survey.MultiSelect{..., PageSize: 10}
+
+// or as an option to Ask or AskOne
+survey.AskOne(prompt, &days, survey.WithPageSize(10))
 ```
 
 ### MultiSelect
@@ -176,57 +212,74 @@ prompt := &survey.MultiSelect{
     Message: "What days do you prefer:",
     Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
 }
-survey.AskOne(prompt, &days, nil)
+survey.AskOne(prompt, &days)
 ```
 
+Fields and values that come from a `MultiSelect` prompt can be one of two different things. If you pass an `int`
+the field will have a slice of the selected indices. If you instead pass a string, a slice of the string values
+selected will be written to the field.
+
 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:
+and will paginate lists of options longer than that. This can be changed a number of ways:
 
 ```golang
+// as a field on a single select
 prompt := &survey.MultiSelect{..., PageSize: 10}
+
+// or as an option to Ask or AskOne
+survey.AskOne(prompt, &days, survey.WithPageSize(10))
 ```
 
 ### Editor
 
-Launches the user's preferred editor (defined by the \$EDITOR environment variable) on a
+Launches the user's preferred editor (defined by the \$VISUAL or \$EDITOR environment variables) 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
+You can also specify a [pattern](https://golang.org/pkg/io/ioutil/#TempFile) for the name of the temporary file. This
+can be useful for ensuring syntax highlighting matches your usecase.
+
+```golang
+prompt := &survey.Editor{
+    Message: "Shell code snippet",
+    FileName: "*.sh",
+}
+
+survey.AskOne(prompt, &content)
+```
+
+## Filtering Options
 
-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 `DefaultFilter` function.
+By default, the user can filter for options in Select and MultiSelects 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.
 
-A custom filter function can also be provided to change this default behavior by providing a value for the `Filter` field:
+A custom filter function can also be provided to change this behavior:
 
 ```golang
+func myFilter(filterValue string, optValue string, optIndex int) include bool {
+    // only include the option if it includes the filter and has length greater than 5
+    return strings.Contains(optValue, filterValue) && len(optValue) >= 5
+}
+
+// configure it for a specific prompt
 &Select{
     Message: "Choose a color:",
     Options: []string{"red", "blue", "green"},
-    Filter: func(filter string, options []string) (filtered []string) {
-        result := DefaultFilter(filter, options)
-        for _, v := range result {
-            if len(v) >= 5 {
-                filtered = append(filtered, v)
-            }
-        }
-        return
-    },
+    Filter: myFilter,
 }
-```
 
-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.
+// or define a default for all of the questions
+survey.AskOne(prompt, &color, survey.WithFilter(myFilter))
+```
 
 ## 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:
+response. Like usual, validators can be provided directly to the prompt or with `survey.WithValidator`:
 
 ```golang
 q := &survey.Question{
@@ -239,6 +292,12 @@ q := &survey.Question{
 	return nil
     },
 }
+
+color := ""
+prompt := &survey.Input{ Message: "Whats your name?" }
+
+// you can pass multiple validators here and survey will make sure each one passes
+survey.AskOne(prompt, &color, survey.WithValidator(survey.Required))
 ```
 
 ### Built-in Validators
@@ -268,12 +327,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 with `WithHelpInput`:
 
 ```golang
 import (
     "github.com/AlecAivazis/survey/v2"
-    surveyCore "github.com/AlecAivazis/survey/v2/core"
 )
 
 number := ""
@@ -282,11 +340,44 @@ prompt := &survey.Input{
     Help:    "I couldn't come up with one.",
 }
 
-surveyCore.HelpInputRune = '^'
+survey.AskOne(prompt, &number, survey.WithHelpInput('^'))
+```
+
+## Changing the Icons
+
+Changing the icons and their color/format can be done by passing the `WithIcons` option. The format
+follows the patterns outlined [here](https://github.com/mgutz/ansi#style-format). For example:
+
+```golang
+import (
+    "github.com/AlecAivazis/survey/v2"
+)
 
-survey.AskOne(prompt, &number, nil)
+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.WithIcons(func(icons *survey.IconSet) {
+    // you can set any icons
+    icons.Question.Text = "⁇"
+    // for more information on formatting the icons, see here: https://github.com/mgutz/ansi#style-format
+    icons.Question.Format = "yellow+hb"
+}))
 ```
 
+The icons and their default text and format are summarized below:
+
+| name           | text | format     | description                                                   |
+| -------------- | ---- | ---------- | ------------------------------------------------------------- |
+| Error          | X    | red        | Before an error                                               |
+| Help           | i    | cyan       | Before help text                                              |
+| Question       | ?    | green+hb   | Before the message of a prompt                                |
+| SelectFocus    | >    | green      | Marks the current focus in `Select` and `MultiSelect` prompts |
+| UnmarkedOption | [ ]  | default+hb | Marks an unselected option in a `MultiSelect` prompt          |
+| MarkedOption   | [x]  | cyan+b     | Marks a chosen selection in a `MultiSelect` prompt            |
+
 ## Custom Types
 
 survey will assign prompt answers to your custom types if they implement this interface:
@@ -312,25 +403,10 @@ survey.AskOne(
     &survey.Input{
         Message: "Enter something:",
     },
-    &myval,
-    nil,
+    &myval
 )
 ```
 
-## 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            |
-
 ## Testing
 
 You can test your program's interactive prompts using [go-expect](https://github.com/Netflix/go-expect). The library
@@ -339,74 +415,4 @@ if you are manipulating the cursor or using `survey`, you will need a way to int
 for things like `CursorLocation`. `vt10x.NewVT10XConsole` will create a `go-expect` console that also multiplexes
 stdio to an in-memory [virtual terminal](https://github.com/hinshun/vt10x).
 
-For example, you can test a binary utilizing `survey` by connecting the Console's tty to a subprocess's stdio.
-
-```go
-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.
-
-```go
-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()))
-}
-```
+For some examples, you can see any of the tests in this repo.
diff --git a/confirm.go b/confirm.go
index d19813c..8e5d7ef 100644
--- a/confirm.go
+++ b/confirm.go
@@ -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
@@ -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 .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 .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}}`
 
@@ -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()
@@ -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 == 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
@@ -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
@@ -110,29 +117,37 @@ by a carriage return.
 
 	likesPie := false
 	prompt := &survey.Confirm{ Message: "What is your name?" }
-	survey.AskOne(prompt, &likesPie, nil)
+	survey.AskOne(prompt, &likesPie)
 */
-func (c *Confirm) Prompt() (interface{}, error) {
+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,
+		},
 	)
 }
diff --git a/core/template.go b/core/template.go
index 7a3a190..f2b89b4 100644
--- a/core/template.go
+++ b/core/template.go
@@ -8,48 +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 = ">"
-)
-
-/*
-  SetFancyIcons changes the err, help, marked, and focus input icons to their
-  fancier forms. These forms may not be compatible with most terminals.
-  This function will not touch the QuestionIcon as its fancy and non fancy form
-  are the same.
-*/
-func SetFancyIcons() {
-	ErrorIcon = "✘"
-	HelpIcon = "ⓘ"
-	// QuestionIcon fancy and non-fancy form are the same
-
-	MarkedOptionIcon = "◉"
-	UnmarkedOptionIcon = "◯"
-
-	SelectFocusIcon = "❯"
-}
-
 var TemplateFuncs = map[string]interface{}{
 	// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
 	"color": func(color string) string {
@@ -58,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 (
@@ -105,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
-}
diff --git a/core/write.go b/core/write.go
index 49443c3..f09db97 100644
--- a/core/write.go
+++ b/core/write.go
@@ -11,11 +11,26 @@ import (
 // the tag used to denote the name of the question
 const tagName = "survey"
 
-// add a few interfaces so users can configure how the prompt values are set
+// Settables allow for configuration when assigning answers
 type Settable interface {
 	WriteAnswer(field string, value interface{}) error
 }
 
+// OptionAnswer is the return type of Selects/MultiSelects that lets the appropriate information
+// get copied to the user's struct
+type OptionAnswer struct {
+	Value string
+	Index int
+}
+
+func OptionAnswerList(incoming []string) []OptionAnswer {
+	list := []OptionAnswer{}
+	for i, opt := range incoming {
+		list = append(list, OptionAnswer{Value: opt, Index: i})
+	}
+	return list
+}
+
 func WriteAnswer(t interface{}, name string, v interface{}) (err error) {
 	// if the field is a custom type
 	if s, ok := t.(Settable); ok {
@@ -39,6 +54,13 @@ func WriteAnswer(t interface{}, name string, v interface{}) (err error) {
 	switch elem.Kind() {
 	// if we are writing to a struct
 	case reflect.Struct:
+		// if we are writing to an option answer than we want to treat
+		// it like a single thing and not a place to deposit answers
+		if elem.Type().Name() == "OptionAnswer" {
+			// copy the value over to the normal struct
+			return copy(elem, value)
+		}
+
 		// get the name of the field that matches the string we  were given
 		fieldIndex, err := findFieldIndex(elem, name)
 		// if something went wrong
@@ -212,6 +234,32 @@ func copy(t reflect.Value, v reflect.Value) (err error) {
 		return
 	}
 
+	// if we are copying from an OptionAnswer to something
+	if v.Type().Name() == "OptionAnswer" {
+		// copying an option answer to a string
+		if t.Kind() == reflect.String {
+			// copies the Value field of the struct
+			t.Set(reflect.ValueOf(v.FieldByName("Value").Interface()))
+			return
+		}
+
+		// copying an option answer to an int
+		if t.Kind() == reflect.Int {
+			// copies the Index field of the struct
+			t.Set(reflect.ValueOf(v.FieldByName("Index").Interface()))
+			return
+		}
+
+		// copying an OptionAnswer to an OptionAnswer
+		if t.Type().Name() == "OptionAnswer" {
+			t.Set(v)
+			return
+		}
+
+		// we're copying an option answer to an incorrect type
+		return fmt.Errorf("Unable to convert from OptionAnswer to type %s", t.Kind())
+	}
+
 	// if we are copying from one slice or array to another
 	if isList(v) && isList(t) {
 		// loop over every item in the desired value
diff --git a/editor.go b/editor.go
index facf1a8..ec6a141 100644
--- a/editor.go
+++ b/editor.go
@@ -7,7 +7,6 @@ import (
 	"os/exec"
 	"runtime"
 
-	"github.com/AlecAivazis/survey/v2/core"
 	"github.com/AlecAivazis/survey/v2/terminal"
 	shellquote "github.com/kballard/go-shellquote"
 )
@@ -23,16 +22,17 @@ Response type is a string.
 
 	message := ""
 	prompt := &survey.Editor{ Message: "What is your commit message?" }
-	survey.AskOne(prompt, &message, nil)
+	survey.AskOne(prompt, &message)
 */
 type Editor struct {
-	core.Renderer
+	Renderer
 	Message       string
 	Default       string
 	Help          string
 	Editor        string
 	HideDefault   bool
 	AppendDefault bool
+	FileName      string
 }
 
 // data available to the templates when processing
@@ -41,17 +41,18 @@ type EditorTemplateData struct {
 	Answer     string
 	ShowAnswer bool
 	ShowHelp   bool
+	Config     *PromptConfig
 }
 
 // Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
 var EditorQuestionTemplate = `
-{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
-{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
+{{- 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 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}}
   {{- if and .Default (not .HideDefault)}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
   {{- color "cyan"}}[Enter to launch editor] {{color "reset"}}
 {{- end}}`
@@ -72,24 +73,27 @@ func init() {
 	}
 }
 
-func (e *Editor) PromptAgain(invalid interface{}, err error) (interface{}, error) {
+func (e *Editor) PromptAgain(config *PromptConfig, invalid interface{}, err error) (interface{}, error) {
 	initialValue := invalid.(string)
-	return e.prompt(initialValue)
+	return e.prompt(initialValue, config)
 }
 
-func (e *Editor) Prompt() (interface{}, error) {
+func (e *Editor) Prompt(config *PromptConfig) (interface{}, error) {
 	initialValue := ""
 	if e.Default != "" && e.AppendDefault {
 		initialValue = e.Default
 	}
-	return e.prompt(initialValue)
+	return e.prompt(initialValue, config)
 }
 
-func (e *Editor) prompt(initialValue string) (interface{}, error) {
+func (e *Editor) prompt(initialValue string, config *PromptConfig) (interface{}, error) {
 	// render the template
 	err := e.Render(
 		EditorQuestionTemplate,
-		EditorTemplateData{Editor: *e},
+		EditorTemplateData{
+			Editor: *e,
+			Config: config,
+		},
 	)
 	if err != nil {
 		return "", err
@@ -118,10 +122,14 @@ func (e *Editor) prompt(initialValue string) (interface{}, error) {
 		if r == terminal.KeyEndTransmission {
 			break
 		}
-		if r == core.HelpInputRune && e.Help != "" {
+		if string(r) == config.HelpInput && e.Help != "" {
 			err = e.Render(
 				EditorQuestionTemplate,
-				EditorTemplateData{Editor: *e, ShowHelp: true},
+				EditorTemplateData{
+					Editor:   *e,
+					ShowHelp: true,
+					Config:   config,
+				},
 			)
 			if err != nil {
 				return "", err
@@ -131,7 +139,11 @@ func (e *Editor) prompt(initialValue string) (interface{}, error) {
 	}
 
 	// prepare the temp file
-	f, err := ioutil.TempFile("", "survey")
+	pattern := e.FileName
+	if pattern == "" {
+		pattern = "survey*.txt"
+	}
+	f, err := ioutil.TempFile("", pattern)
 	if err != nil {
 		return "", err
 	}
@@ -197,9 +209,14 @@ func (e *Editor) prompt(initialValue string) (interface{}, error) {
 	return text, nil
 }
 
-func (e *Editor) Cleanup(val interface{}) error {
+func (e *Editor) Cleanup(config *PromptConfig, val interface{}) error {
 	return e.Render(
 		EditorQuestionTemplate,
-		EditorTemplateData{Editor: *e, Answer: "<Received>", ShowAnswer: true},
+		EditorTemplateData{
+			Editor:     *e,
+			Answer:     "<Received>",
+			ShowAnswer: true,
+			Config:     config,
+		},
 	)
 }
diff --git a/filter.go b/filter.go
index 073d5d0..56f7026 100644
--- a/filter.go
+++ b/filter.go
@@ -1,13 +1,1 @@
 package survey
-
-import "strings"
-
-var DefaultFilter = 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
-}
diff --git a/go.mod b/go.mod
old mode 100755
new mode 100644
index 39e48db..5303f57
--- a/go.mod
+++ b/go.mod
@@ -8,6 +8,7 @@ require (
 	github.com/kr/pty v1.1.4 // indirect
 	github.com/mattn/go-colorable v0.1.2 // indirect
 	github.com/mattn/go-isatty v0.0.8
+	github.com/mattn/go-runewidth v0.0.4
 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/stretchr/testify v1.2.1
diff --git a/go.sum b/go.sum
old mode 100755
new mode 100644
index 69bf673..65f8d2b
--- a/go.sum
+++ b/go.sum
@@ -12,6 +12,8 @@ github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx
 github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
+github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
diff --git a/input.go b/input.go
index 3086e27..958ce71 100644
--- a/input.go
+++ b/input.go
@@ -1,19 +1,15 @@
 package survey
 
-import (
-	"github.com/AlecAivazis/survey/v2/core"
-)
-
 /*
 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)
+	survey.AskOne(prompt, &name)
 */
 type Input struct {
-	core.Renderer
+	Renderer
 	Message string
 	Default string
 	Help    string
@@ -25,25 +21,29 @@ type InputTemplateData struct {
 	Answer     string
 	ShowAnswer bool
 	ShowHelp   bool
+	Config     *PromptConfig
 }
 
 // Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
 var InputQuestionTemplate = `
-{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
-{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
+{{- 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 and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ HelpInputRune }} for help]{{color "reset"}} {{end}}
+  {{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ print .Config.HelpInput }} for help]{{color "reset"}} {{end}}
   {{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
 {{- end}}`
 
-func (i *Input) Prompt() (interface{}, error) {
+func (i *Input) Prompt(config *PromptConfig) (interface{}, error) {
 	// render the template
 	err := i.Render(
 		InputQuestionTemplate,
-		InputTemplateData{Input: *i},
+		InputTemplateData{
+			Input:  *i,
+			Config: config,
+		},
 	)
 	if err != nil {
 		return "", err
@@ -66,10 +66,14 @@ func (i *Input) Prompt() (interface{}, error) {
 		// terminal will echo the \n so we need to jump back up one row
 		cursor.PreviousLine(1)
 
-		if string(line) == string(core.HelpInputRune) && i.Help != "" {
+		if string(line) == config.HelpInput && i.Help != "" {
 			err = i.Render(
 				InputQuestionTemplate,
-				InputTemplateData{Input: *i, ShowHelp: true},
+				InputTemplateData{
+					Input:    *i,
+					ShowHelp: true,
+					Config:   config,
+				},
 			)
 			if err != nil {
 				return "", err
@@ -89,9 +93,14 @@ func (i *Input) Prompt() (interface{}, error) {
 	return string(line), err
 }
 
-func (i *Input) Cleanup(val interface{}) error {
+func (i *Input) Cleanup(config *PromptConfig, val interface{}) error {
 	return i.Render(
 		InputQuestionTemplate,
-		InputTemplateData{Input: *i, Answer: val.(string), ShowAnswer: true},
+		InputTemplateData{
+			Input:      *i,
+			Answer:     val.(string),
+			ShowAnswer: true,
+			Config:     config,
+		},
 	)
 }
diff --git a/multiline.go b/multiline.go
index c33c9dd..8368705 100644
--- a/multiline.go
+++ b/multiline.go
@@ -1,15 +1,13 @@
 package survey
 
 import (
-	"fmt"
 	"strings"
 
-	"github.com/AlecAivazis/survey/v2/core"
 	"github.com/AlecAivazis/survey/v2/terminal"
 )
 
 type Multiline struct {
-	core.Renderer
+	Renderer
 	Message string
 	Default string
 	Help    string
@@ -21,12 +19,13 @@ type MultilineTemplateData struct {
 	Answer     string
 	ShowAnswer bool
 	ShowHelp   bool
+	Config     *PromptConfig
 }
 
 // Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
 var MultilineQuestionTemplate = `
-{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
-{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
+{{- 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}}
   {{- "\n"}}{{color "cyan"}}{{.Answer}}{{color "reset"}}
@@ -36,16 +35,18 @@ var MultilineQuestionTemplate = `
   {{- color "cyan"}}[Enter 2 empty lines to finish]{{color "reset"}}
 {{- end}}`
 
-func (i *Multiline) Prompt() (interface{}, error) {
+func (i *Multiline) Prompt(config *PromptConfig) (interface{}, error) {
 	// render the template
 	err := i.Render(
 		MultilineQuestionTemplate,
-		MultilineTemplateData{Multiline: *i},
+		MultilineTemplateData{
+			Multiline: *i,
+			Config:    config,
+		},
 	)
 	if err != nil {
 		return "", err
 	}
-	fmt.Println()
 
 	// start reading runes from the standard in
 	rr := i.NewRuneReader()
@@ -96,9 +97,14 @@ func (i *Multiline) Prompt() (interface{}, error) {
 	return val, err
 }
 
-func (i *Multiline) Cleanup(val interface{}) error {
+func (i *Multiline) Cleanup(config *PromptConfig, val interface{}) error {
 	return i.Render(
 		MultilineQuestionTemplate,
-		MultilineTemplateData{Multiline: *i, Answer: val.(string), ShowAnswer: true},
+		MultilineTemplateData{
+			Multiline:  *i,
+			Answer:     val.(string),
+			ShowAnswer: true,
+			Config:     config,
+		},
 	)
 }
diff --git a/multiselect.go b/multiselect.go
index 5045d24..7d1f1be 100644
--- a/multiselect.go
+++ b/multiselect.go
@@ -2,7 +2,7 @@ package survey
 
 import (
 	"errors"
-	"strings"
+	"fmt"
 
 	"github.com/AlecAivazis/survey/v2/core"
 	"github.com/AlecAivazis/survey/v2/terminal"
@@ -17,21 +17,21 @@ for them to select using the arrow keys and enter. Response type is a slice of s
 		Message: "What days do you prefer:",
 		Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
 	}
-	survey.AskOne(prompt, &days, nil)
+	survey.AskOne(prompt, &days)
 */
 type MultiSelect struct {
-	core.Renderer
+	Renderer
 	Message       string
 	Options       []string
-	Default       []string
+	Default       interface{}
 	Help          string
 	PageSize      int
 	VimMode       bool
 	FilterMessage string
-	Filter        func(string, []string) []string
+	Filter        func(filter string, value string, index int) bool
 	filter        string
 	selectedIndex int
-	checked       map[string]bool
+	checked       map[int]bool
 	showingHelp   bool
 }
 
@@ -40,31 +40,32 @@ type MultiSelectTemplateData struct {
 	MultiSelect
 	Answer        string
 	ShowAnswer    bool
-	Checked       map[string]bool
+	Checked       map[int]bool
 	SelectedIndex int
 	ShowHelp      bool
-	PageEntries   []string
+	PageEntries   []core.OptionAnswer
+	Config        *PromptConfig
 }
 
 var MultiSelectQuestionTemplate = `
-{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
-{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
+{{- 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 }}{{ .FilterMessage }}{{color "reset"}}
 {{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
 {{- else }}
-	{{- "  "}}{{- color "cyan"}}[Use arrows to move, space to select, type to filter{{- if and .Help (not .ShowHelp)}}, {{ HelpInputRune }} for more help{{end}}]{{color "reset"}}
+	{{- "  "}}{{- color "cyan"}}[Use arrows to move, space to select, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
   {{- "\n"}}
   {{- range $ix, $option := .PageEntries}}
-    {{- if eq $ix $.SelectedIndex}}{{color "cyan"}}{{ SelectFocusIcon }}{{color "reset"}}{{else}} {{end}}
-    {{- if index $.Checked $option}}{{color "green"}} {{ MarkedOptionIcon }} {{else}}{{color "default+hb"}} {{ UnmarkedOptionIcon }} {{end}}
+    {{- if eq $ix $.SelectedIndex }}{{color $.Config.Icons.SelectFocus.Format }}{{ $.Config.Icons.SelectFocus.Text }}{{color "reset"}}{{else}} {{end}}
+    {{- if index $.Checked $ix }}{{color $.Config.Icons.MarkedOption.Format }} {{ $.Config.Icons.MarkedOption.Text }} {{else}}{{color $.Config.Icons.UnmarkedOption.Format }} {{ $.Config.Icons.UnmarkedOption.Text }} {{end}}
     {{- color "reset"}}
-    {{- " "}}{{$option}}{{"\n"}}
+    {{- " "}}{{$option.Value}}{{"\n"}}
   {{- end}}
 {{- end}}`
 
 // OnChange is called on every keypress.
-func (m *MultiSelect) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
-	options := m.filterOptions()
+func (m *MultiSelect) OnChange(key rune, config *PromptConfig) {
+	options := m.filterOptions(config)
 	oldFilter := m.filter
 
 	if key == terminal.KeyArrowUp || (m.VimMode && key == 'k') {
@@ -87,18 +88,22 @@ func (m *MultiSelect) OnChange(line []rune, pos int, key rune) (newLine []rune,
 		}
 		// if the user pressed down and there is room to move
 	} else if key == terminal.KeySpace {
+		// the option they have selected
 		if m.selectedIndex < len(options) {
-			if old, ok := m.checked[options[m.selectedIndex]]; !ok {
-				// otherwise just invert the current value
-				m.checked[options[m.selectedIndex]] = true
+			selectedOpt := options[m.selectedIndex]
+
+			// if we haven't seen this index before
+			if old, ok := m.checked[selectedOpt.Index]; !ok {
+				// set the value to true
+				m.checked[selectedOpt.Index] = true
 			} else {
 				// otherwise just invert the current value
-				m.checked[options[m.selectedIndex]] = !old
+				m.checked[selectedOpt.Index] = !old
 			}
 			m.filter = ""
 		}
 		// only show the help message if we have one to show
-	} else if key == core.HelpInputRune && m.Help != "" {
+	} else if string(key) == config.HelpInput && m.Help != "" {
 		m.showingHelp = true
 	} else if key == terminal.KeyEscape {
 		m.VimMode = !m.VimMode
@@ -119,16 +124,23 @@ func (m *MultiSelect) OnChange(line []rune, pos int, key rune) (newLine []rune,
 	}
 	if oldFilter != m.filter {
 		// filter changed
-		options = m.filterOptions()
+		options = m.filterOptions(config)
 		if len(options) > 0 && len(options) <= m.selectedIndex {
 			m.selectedIndex = len(options) - 1
 		}
 	}
 	// paginate the options
+	// figure out the page size
+	pageSize := m.PageSize
+	// if we dont have a specific one
+	if pageSize == 0 {
+		// grab the global value
+		pageSize = config.PageSize
+	}
 
 	// TODO if we have started filtering and were looking at the end of a list
 	// and we have modified the filter then we should move the page back!
-	opts, idx := paginate(m.PageSize, options, m.selectedIndex)
+	opts, idx := paginate(pageSize, options, m.selectedIndex)
 
 	// render the options
 	m.Render(
@@ -139,38 +151,67 @@ func (m *MultiSelect) OnChange(line []rune, pos int, key rune) (newLine []rune,
 			Checked:       m.checked,
 			ShowHelp:      m.showingHelp,
 			PageEntries:   opts,
+			Config:        config,
 		},
 	)
-
-	// if we are not pressing ent
-	return line, 0, true
 }
 
-func (m *MultiSelect) filterOptions() []string {
+func (m *MultiSelect) filterOptions(config *PromptConfig) []core.OptionAnswer {
+	// the filtered list
+	answers := []core.OptionAnswer{}
+
+	// if there is no filter applied
 	if m.filter == "" {
-		return m.Options
+		// return all of the options
+		return core.OptionAnswerList(m.Options)
+	}
+
+	// the filter to apply
+	filter := m.Filter
+	if filter == nil {
+		filter = config.Filter
 	}
-	if m.Filter != nil {
-		return m.Filter(m.filter, m.Options)
+
+	// apply the filter to each option
+	for i, opt := range m.Options {
+		// i the filter says to include the option
+		if filter(m.filter, opt, i) {
+			answers = append(answers, core.OptionAnswer{
+				Index: i,
+				Value: opt,
+			})
+		}
 	}
-	return DefaultFilter(m.filter, m.Options)
+
+	// we're done here
+	return answers
 }
 
-func (m *MultiSelect) Prompt() (interface{}, error) {
+func (m *MultiSelect) Prompt(config *PromptConfig) (interface{}, error) {
 	// compute the default state
-	m.checked = make(map[string]bool)
+	m.checked = make(map[int]bool)
 	// if there is a default
-	if len(m.Default) > 0 {
-		for _, dflt := range m.Default {
-			for _, opt := range m.Options {
-				// if the option corresponds to the default
-				if opt == dflt {
-					// we found our initial value
-					m.checked[opt] = true
-					// stop looking
-					break
+	if m.Default != nil {
+		// if the default is string values
+		if defaultValues, ok := m.Default.([]string); ok {
+			for _, dflt := range defaultValues {
+				for i, opt := range m.Options {
+					// if the option corresponds to the default
+					if opt == dflt {
+						// we found our initial value
+						m.checked[i] = true
+						// stop looking
+						break
+					}
 				}
 			}
+			// if the default value is index values
+		} else if defaultIndices, ok := m.Default.([]int); ok {
+			// go over every index we need to enable by default
+			for _, idx := range defaultIndices {
+				// and enable it
+				m.checked[idx] = true
+			}
 		}
 	}
 
@@ -180,8 +221,16 @@ func (m *MultiSelect) Prompt() (interface{}, error) {
 		return "", errors.New("please provide options to select from")
 	}
 
+	// figure out the page size
+	pageSize := m.PageSize
+	// if we dont have a specific one
+	if pageSize == 0 {
+		// grab the global value
+		pageSize = config.PageSize
+	}
 	// paginate the options
-	opts, idx := paginate(m.PageSize, m.Options, m.selectedIndex)
+	// build up a list of option answers
+	opts, idx := paginate(pageSize, core.OptionAnswerList(m.Options), m.selectedIndex)
 
 	cursor := m.NewCursor()
 	cursor.Hide()       // hide the cursor
@@ -195,6 +244,7 @@ func (m *MultiSelect) Prompt() (interface{}, error) {
 			SelectedIndex: idx,
 			Checked:       m.checked,
 			PageEntries:   opts,
+			Config:        config,
 		},
 	)
 	if err != nil {
@@ -217,15 +267,15 @@ func (m *MultiSelect) Prompt() (interface{}, error) {
 		if r == terminal.KeyEndTransmission {
 			break
 		}
-		m.OnChange(nil, 0, r)
+		m.OnChange(r, config)
 	}
 	m.filter = ""
 	m.FilterMessage = ""
 
-	answers := []string{}
-	for _, option := range m.Options {
-		if val, ok := m.checked[option]; ok && val {
-			answers = append(answers, option)
+	answers := []core.OptionAnswer{}
+	for i, option := range m.Options {
+		if val, ok := m.checked[i]; ok && val {
+			answers = append(answers, core.OptionAnswer{Value: option, Index: i})
 		}
 	}
 
@@ -233,7 +283,19 @@ func (m *MultiSelect) Prompt() (interface{}, error) {
 }
 
 // Cleanup removes the options section, and renders the ask like a normal question.
-func (m *MultiSelect) Cleanup(val interface{}) error {
+func (m *MultiSelect) Cleanup(config *PromptConfig, val interface{}) error {
+	// the answer to show
+	answer := ""
+	for _, ans := range val.([]core.OptionAnswer) {
+		answer = fmt.Sprintf("%s, %s", answer, ans.Value)
+	}
+
+	// if we answered anything
+	if len(answer) > 2 {
+		// remove the precending commas
+		answer = answer[2:]
+	}
+
 	// execute the output summary template with the answer
 	return m.Render(
 		MultiSelectQuestionTemplate,
@@ -241,8 +303,9 @@ func (m *MultiSelect) Cleanup(val interface{}) error {
 			MultiSelect:   *m,
 			SelectedIndex: m.selectedIndex,
 			Checked:       m.checked,
-			Answer:        strings.Join(val.([]string), ", "),
+			Answer:        answer,
 			ShowAnswer:    true,
+			Config:        config,
 		},
 	)
 }
diff --git a/password.go b/password.go
index ec438b1..d80dc68 100644
--- a/password.go
+++ b/password.go
@@ -13,10 +13,10 @@ type is a string.
 
 	password := ""
 	prompt := &survey.Password{ Message: "Please type your password" }
-	survey.AskOne(prompt, &password, nil)
+	survey.AskOne(prompt, &password)
 */
 type Password struct {
-	core.Renderer
+	Renderer
 	Message string
 	Help    string
 }
@@ -24,20 +24,24 @@ type Password struct {
 type PasswordTemplateData struct {
 	Password
 	ShowHelp bool
+	Config   *PromptConfig
 }
 
-// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
+// PasswordQuestionTemplate is a template with color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
 var PasswordQuestionTemplate = `
-{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
-{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
+{{- 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 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}}`
 
-func (p *Password) Prompt() (line interface{}, err error) {
+func (p *Password) Prompt(config *PromptConfig) (line interface{}, err error) {
 	// render the question template
 	out, err := core.RunTemplate(
 		PasswordQuestionTemplate,
-		PasswordTemplateData{Password: *p},
+		PasswordTemplateData{
+			Password: *p,
+			Config:   config,
+		},
 	)
 	fmt.Fprint(terminal.NewAnsiStdout(p.Stdio().Out), out)
 	if err != nil {
@@ -63,13 +67,17 @@ func (p *Password) Prompt() (line interface{}, err error) {
 			return string(line), err
 		}
 
-		if string(line) == string(core.HelpInputRune) {
+		if string(line) == config.HelpInput {
 			// terminal will echo the \n so we need to jump back up one row
 			cursor.PreviousLine(1)
 
 			err = p.Render(
 				PasswordQuestionTemplate,
-				PasswordTemplateData{Password: *p, ShowHelp: true},
+				PasswordTemplateData{
+					Password: *p,
+					ShowHelp: true,
+					Config:   config,
+				},
 			)
 			if err != nil {
 				return "", err
@@ -81,6 +89,6 @@ func (p *Password) Prompt() (line interface{}, err error) {
 }
 
 // Cleanup hides the string with a fixed number of characters.
-func (prompt *Password) Cleanup(val interface{}) error {
+func (prompt *Password) Cleanup(config *PromptConfig, val interface{}) error {
 	return nil
 }
diff --git a/core/renderer.go b/renderer.go
similarity index 78%
rename from core/renderer.go
rename to renderer.go
index 8680bdb..b367262 100644
--- a/core/renderer.go
+++ b/renderer.go
@@ -1,9 +1,10 @@
-package core
+package survey
 
 import (
 	"fmt"
 	"strings"
 
+	"github.com/AlecAivazis/survey/v2/core"
 	"github.com/AlecAivazis/survey/v2/terminal"
 )
 
@@ -13,7 +14,12 @@ type Renderer struct {
 	errorLineCount int
 }
 
-var ErrorTemplate = `{{color "red"}}{{ ErrorIcon }} Sorry, your reply was invalid: {{.Error}}{{color "reset"}}
+type ErrorTemplateData struct {
+	Error error
+	Icon  Icon
+}
+
+var ErrorTemplate = `{{color .Icon.Format }}{{ .Icon.Text }} Sorry, your reply was invalid: {{ .Error.Error }}{{color "reset"}}
 `
 
 func (r *Renderer) WithStdio(stdio terminal.Stdio) {
@@ -35,13 +41,17 @@ func (r *Renderer) NewCursor() *terminal.Cursor {
 	}
 }
 
-func (r *Renderer) Error(invalid error) error {
+func (r *Renderer) Error(config *PromptConfig, invalid error) error {
 	// since errors are printed on top we need to reset the prompt
 	// as well as any previous error print
 	r.resetPrompt(r.lineCount + r.errorLineCount)
+
 	// we just cleared the prompt lines
 	r.lineCount = 0
-	out, err := RunTemplate(ErrorTemplate, invalid)
+	out, err := core.RunTemplate(ErrorTemplate, &ErrorTemplateData{
+		Error: invalid,
+		Icon:  config.Icons.Error,
+	})
 	if err != nil {
 		return err
 	}
@@ -68,7 +78,7 @@ func (r *Renderer) resetPrompt(lines int) {
 func (r *Renderer) Render(tmpl string, data interface{}) error {
 	r.resetPrompt(r.lineCount)
 	// render the template summarizing the current state
-	out, err := RunTemplate(tmpl, data)
+	out, err := core.RunTemplate(tmpl, data)
 	if err != nil {
 		return err
 	}
diff --git a/select.go b/select.go
index edb07f7..ac369ba 100644
--- a/select.go
+++ b/select.go
@@ -16,52 +16,53 @@ for them to select using the arrow keys and enter. Response type is a string.
 		Message: "Choose a color:",
 		Options: []string{"red", "blue", "green"},
 	}
-	survey.AskOne(prompt, &color, nil)
+	survey.AskOne(prompt, &color)
 */
 type Select struct {
-	core.Renderer
+	Renderer
 	Message       string
 	Options       []string
-	Default       string
+	Default       interface{}
 	Help          string
 	PageSize      int
 	VimMode       bool
 	FilterMessage string
-	Filter        func(string, []string) []string
+	Filter        func(filter string, value string, index int) bool
 	filter        string
 	selectedIndex int
 	useDefault    bool
 	showingHelp   bool
 }
 
-// the data available to the templates when processing
+// SelectTemplateData is the data available to the templates when processing
 type SelectTemplateData struct {
 	Select
-	PageEntries   []string
+	PageEntries   []core.OptionAnswer
 	SelectedIndex int
 	Answer        string
 	ShowAnswer    bool
 	ShowHelp      bool
+	Config        *PromptConfig
 }
 
 var SelectQuestionTemplate = `
-{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
-{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
+{{- 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 }}{{ .FilterMessage }}{{color "reset"}}
 {{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
 {{- else}}
-  {{- "  "}}{{- color "cyan"}}[Use arrows to move, space to select, type to filter{{- if and .Help (not .ShowHelp)}}, {{ HelpInputRune }} for more help{{end}}]{{color "reset"}}
+  {{- "  "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
   {{- "\n"}}
   {{- range $ix, $choice := .PageEntries}}
-    {{- if eq $ix $.SelectedIndex}}{{color "cyan+b"}}{{ SelectFocusIcon }} {{else}}{{color "default+hb"}}  {{end}}
-    {{- $choice}}
+    {{- if eq $ix $.SelectedIndex }}{{color $.Config.Icons.SelectFocus.Format }}{{ $.Config.Icons.SelectFocus.Text }} {{else}}{{color "default"}}  {{end}}
+    {{- $choice.Value}}
     {{- color "reset"}}{{"\n"}}
   {{- end}}
 {{- end}}`
 
 // OnChange is called on every keypress.
-func (s *Select) OnChange(key rune) bool {
-	options := s.filterOptions()
+func (s *Select) OnChange(key rune, config *PromptConfig) bool {
+	options := s.filterOptions(config)
 	oldFilter := s.filter
 
 	// if the user pressed the enter key and the index is a valid option
@@ -101,7 +102,7 @@ func (s *Select) OnChange(key rune) bool {
 			s.selectedIndex++
 		}
 		// only show the help message if we have one
-	} else if key == core.HelpInputRune && s.Help != "" {
+	} else if string(key) == config.HelpInput && s.Help != "" {
 		s.showingHelp = true
 		// if the user wants to toggle vim mode on/off
 	} else if key == terminal.KeyEscape {
@@ -131,17 +132,24 @@ func (s *Select) OnChange(key rune) bool {
 	}
 	if oldFilter != s.filter {
 		// filter changed
-		options = s.filterOptions()
+		options = s.filterOptions(config)
 		if len(options) > 0 && len(options) <= s.selectedIndex {
 			s.selectedIndex = len(options) - 1
 		}
 	}
 
 	// figure out the options and index to render
+	// figure out the page size
+	pageSize := s.PageSize
+	// if we dont have a specific one
+	if pageSize == 0 {
+		// grab the global value
+		pageSize = config.PageSize
+	}
 
 	// TODO if we have started filtering and were looking at the end of a list
 	// and we have modified the filter then we should move the page back!
-	opts, idx := paginate(s.PageSize, options, s.selectedIndex)
+	opts, idx := paginate(pageSize, options, s.selectedIndex)
 
 	// render the options
 	s.Render(
@@ -151,6 +159,7 @@ func (s *Select) OnChange(key rune) bool {
 			SelectedIndex: idx,
 			ShowHelp:      s.showingHelp,
 			PageEntries:   opts,
+			Config:        config,
 		},
 	)
 
@@ -158,17 +167,37 @@ func (s *Select) OnChange(key rune) bool {
 	return false
 }
 
-func (s *Select) filterOptions() []string {
+func (s *Select) filterOptions(config *PromptConfig) []core.OptionAnswer {
+	// the filtered list
+	answers := []core.OptionAnswer{}
+
+	// if there is no filter applied
 	if s.filter == "" {
-		return s.Options
+		return core.OptionAnswerList(s.Options)
 	}
-	if s.Filter != nil {
-		return s.Filter(s.filter, s.Options)
+
+	// the filter to apply
+	filter := s.Filter
+	if filter == nil {
+		filter = config.Filter
 	}
-	return DefaultFilter(s.filter, s.Options)
+
+	//
+	for i, opt := range s.Options {
+		// i the filter says to include the option
+		if filter(s.filter, opt, i) {
+			answers = append(answers, core.OptionAnswer{
+				Index: i,
+				Value: opt,
+			})
+		}
+	}
+
+	// return the list of answers
+	return answers
 }
 
-func (s *Select) Prompt() (interface{}, error) {
+func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
 	// if there are no options to render
 	if len(s.Options) == 0 {
 		// we failed
@@ -193,8 +222,16 @@ func (s *Select) Prompt() (interface{}, error) {
 	// save the selected index
 	s.selectedIndex = sel
 
+	// figure out the page size
+	pageSize := s.PageSize
+	// if we dont have a specific one
+	if pageSize == 0 {
+		// grab the global value
+		pageSize = config.PageSize
+	}
+
 	// figure out the options and index to render
-	opts, idx := paginate(s.PageSize, s.Options, sel)
+	opts, idx := paginate(pageSize, core.OptionAnswerList(s.Options), sel)
 
 	// ask the question
 	err := s.Render(
@@ -203,6 +240,7 @@ func (s *Select) Prompt() (interface{}, error) {
 			Select:        *s,
 			PageEntries:   opts,
 			SelectedIndex: idx,
+			Config:        config,
 		},
 	)
 	if err != nil {
@@ -232,40 +270,59 @@ func (s *Select) Prompt() (interface{}, error) {
 		if r == terminal.KeyEndTransmission {
 			break
 		}
-		if s.OnChange(r) {
+		if s.OnChange(r, config) {
 			break
 		}
 	}
-	options := s.filterOptions()
+	options := s.filterOptions(config)
 	s.filter = ""
 	s.FilterMessage = ""
 
+	// the index to report
 	var val string
 	// if we are supposed to use the default value
 	if s.useDefault || s.selectedIndex >= len(options) {
 		// if there is a default value
-		if s.Default != "" {
-			// use the default value
-			val = s.Default
+		if s.Default != nil {
+			// if the default is a string
+			if defaultString, ok := s.Default.(string); ok {
+				// use the default value
+				val = defaultString
+				// the default value could also be an interpret which is interpretted as the index
+			} else if defaultIndex, ok := s.Default.(int); ok {
+				val = s.Options[defaultIndex]
+			} else {
+				return val, errors.New("default value of select must be an int or string")
+			}
 		} else if len(options) > 0 {
 			// there is no default value so use the first
-			val = options[0]
+			val = options[0].Value
 		}
 		// otherwise the selected index points to the value
 	} else if s.selectedIndex < len(options) {
 		// the
-		val = options[s.selectedIndex]
+		val = options[s.selectedIndex].Value
 	}
-	return val, err
+
+	// now that we have the value lets go hunt down the right index to return
+	idx = -1
+	for i, optionValue := range s.Options {
+		if optionValue == val {
+			idx = i
+		}
+	}
+
+	return core.OptionAnswer{Value: val, Index: idx}, err
 }
 
-func (s *Select) Cleanup(val interface{}) error {
+func (s *Select) Cleanup(config *PromptConfig, val interface{}) error {
 	return s.Render(
 		SelectQuestionTemplate,
 		SelectTemplateData{
 			Select:     *s,
-			Answer:     val.(string),
+			Answer:     val.(core.OptionAnswer).Value,
 			ShowAnswer: true,
+			Config:     config,
 		},
 	)
 }
diff --git a/survey.go b/survey.go
index 9ca1601..816df56 100644
--- a/survey.go
+++ b/survey.go
@@ -4,21 +4,84 @@ import (
 	"errors"
 	"io"
 	"os"
+	"strings"
 
 	"github.com/AlecAivazis/survey/v2/core"
 	"github.com/AlecAivazis/survey/v2/terminal"
 )
 
-// PageSize is the default maximum number of items to show in select/multiselect prompts
-var PageSize = 7
-
 // DefaultAskOptions is the default options on ask, using the OS stdio.
-var DefaultAskOptions = AskOptions{
-	Stdio: terminal.Stdio{
-		In:  os.Stdin,
-		Out: os.Stdout,
-		Err: os.Stderr,
-	},
+func defaultAskOptions() *AskOptions {
+	return &AskOptions{
+		Stdio: terminal.Stdio{
+			In:  os.Stdin,
+			Out: os.Stdout,
+			Err: os.Stderr,
+		},
+		PromptConfig: PromptConfig{
+			PageSize:  7,
+			HelpInput: "?",
+			Icons: IconSet{
+				Error: Icon{
+					Text:   "X",
+					Format: "red",
+				},
+				Help: Icon{
+					Text:   "?",
+					Format: "cyan",
+				},
+				Question: Icon{
+					Text:   "?",
+					Format: "green+hb",
+				},
+				MarkedOption: Icon{
+					Text:   "[x]",
+					Format: "green",
+				},
+				UnmarkedOption: Icon{
+					Text:   "[ ]",
+					Format: "default+hb",
+				},
+				SelectFocus: Icon{
+					Text:   ">",
+					Format: "cyan+b",
+				},
+			},
+			Filter: func(filter string, value string, index int) (include bool) {
+				filter = strings.ToLower(filter)
+
+				// include this option if it matches
+				return strings.Contains(strings.ToLower(value), filter)
+			},
+		},
+	}
+}
+func defaultPromptConfig() *PromptConfig {
+	return &defaultAskOptions().PromptConfig
+}
+
+func defaultIcons() *IconSet {
+	return &defaultPromptConfig().Icons
+}
+
+// OptionAnswer is an ergonomic alias for core.OptionAnswer
+type OptionAnswer = core.OptionAnswer
+
+// Icon holds the text and format to show for a particular icon
+type Icon struct {
+	Text   string
+	Format string
+}
+
+// IconSet holds the icons to use for various prompts
+type IconSet struct {
+	HelpInput      Icon
+	Error          Icon
+	Help           Icon
+	Question       Icon
+	MarkedOption   Icon
+	UnmarkedOption Icon
+	SelectFocus    Icon
 }
 
 // Validator is a function passed to a Question after a user has provided a response.
@@ -41,17 +104,25 @@ type Question struct {
 	Transform Transformer
 }
 
+// PromptConfig holds the global configuration for a prompt
+type PromptConfig struct {
+	PageSize  int
+	Icons     IconSet
+	HelpInput string
+	Filter    func(filter string, option string, index int) bool
+}
+
 // Prompt is the primary interface for the objects that can take user input
 // and return a response.
 type Prompt interface {
-	Prompt() (interface{}, error)
-	Cleanup(interface{}) error
-	Error(error) error
+	Prompt(config *PromptConfig) (interface{}, error)
+	Cleanup(*PromptConfig, interface{}) error
+	Error(*PromptConfig, error) error
 }
 
 // PromptAgainer Interface for Prompts that support prompting again after invalid input
 type PromptAgainer interface {
-	PromptAgain(invalid interface{}, err error) (interface{}, error)
+	PromptAgain(config *PromptConfig, invalid interface{}, err error) (interface{}, error)
 }
 
 // AskOpt allows setting optional ask options.
@@ -59,7 +130,9 @@ type AskOpt func(options *AskOptions) error
 
 // AskOptions provides additional options on ask.
 type AskOptions struct {
-	Stdio terminal.Stdio
+	Stdio        terminal.Stdio
+	Validators   []Validator
+	PromptConfig PromptConfig
 }
 
 // WithStdio specifies the standard input, output and error files survey
@@ -73,10 +146,64 @@ func WithStdio(in terminal.FileReader, out terminal.FileWriter, err io.Writer) A
 	}
 }
 
+// WithFilter specifies the default filter to use when asking questions.
+func WithFilter(filter func(filter string, value string, index int) (include bool)) AskOpt {
+	return func(options *AskOptions) error {
+		// save the filter internally
+		options.PromptConfig.Filter = filter
+
+		return nil
+	}
+}
+
+// WithValidator specifies a validator to use while prompting the user
+func WithValidator(v Validator) AskOpt {
+	return func(options *AskOptions) error {
+		// add the provided validator to the list
+		options.Validators = append(options.Validators, v)
+
+		// nothing went wrong
+		return nil
+	}
+}
+
 type wantsStdio interface {
 	WithStdio(terminal.Stdio)
 }
 
+// WithPageSize sets the default page size used by prompts
+func WithPageSize(pageSize int) AskOpt {
+	return func(options *AskOptions) error {
+		// set the page size
+		options.PromptConfig.PageSize = pageSize
+
+		// nothing went wrong
+		return nil
+	}
+}
+
+// WithHelpInput changes the character that prompts look for to give the user helpful information.
+func WithHelpInput(r rune) AskOpt {
+	return func(options *AskOptions) error {
+		// set the input character
+		options.PromptConfig.HelpInput = string(r)
+
+		// nothing went wrong
+		return nil
+	}
+}
+
+// WithIcons sets the icons that will be used when prompting the user
+func WithIcons(setIcons func(*IconSet)) AskOpt {
+	return func(options *AskOptions) error {
+		// update the default icons with whatever the user says
+		setIcons(&options.PromptConfig.Icons)
+
+		// nothing went wrong
+		return nil
+	}
+}
+
 /*
 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
@@ -87,11 +214,11 @@ in the documentation. For example:
 		Message: "name",
 	}
 
-	survey.AskOne(prompt, &name, nil)
+	survey.AskOne(prompt, &name)
 
 */
-func AskOne(p Prompt, response interface{}, v Validator, opts ...AskOpt) error {
-	err := Ask([]*Question{{Prompt: p, Validate: v}}, response, opts...)
+func AskOne(p Prompt, response interface{}, opts ...AskOpt) error {
+	err := Ask([]*Question{{Prompt: p}}, response, opts...)
 	if err != nil {
 		return err
 	}
@@ -123,10 +250,10 @@ matching name. For example:
 	err := survey.Ask(qs, &answers)
 */
 func Ask(qs []*Question, response interface{}, opts ...AskOpt) error {
-
-	options := DefaultAskOptions
+	// build up the configuration options
+	options := defaultAskOptions()
 	for _, opt := range opts {
-		if err := opt(&options); err != nil {
+		if err := opt(options); err != nil {
 			return err
 		}
 	}
@@ -145,17 +272,29 @@ func Ask(qs []*Question, response interface{}, opts ...AskOpt) error {
 		}
 
 		// grab the user input and save it
-		ans, err := q.Prompt.Prompt()
+		ans, err := q.Prompt.Prompt(&options.PromptConfig)
 		// if there was a problem
 		if err != nil {
 			return err
 		}
 
-		// if there is a validate handler for this question
+		// build up a list of validators that we have to apply to this question
+		validators := []Validator{}
+
+		// make sure to include the question specific one
 		if q.Validate != nil {
+			validators = append(validators, q.Validate)
+		}
+		// add any "global" validators
+		for _, validator := range options.Validators {
+			validators = append(validators, validator)
+		}
+
+		// apply every validator to thte response
+		for _, validator := range validators {
 			// wait for a valid response
-			for invalid := q.Validate(ans); invalid != nil; invalid = q.Validate(ans) {
-				err := q.Prompt.Error(invalid)
+			for invalid := validator(ans); invalid != nil; invalid = validator(ans) {
+				err := q.Prompt.Error(&options.PromptConfig, invalid)
 				// if there was a problem
 				if err != nil {
 					return err
@@ -163,9 +302,9 @@ func Ask(qs []*Question, response interface{}, opts ...AskOpt) error {
 
 				// ask for more input
 				if promptAgainer, ok := q.Prompt.(PromptAgainer); ok {
-					ans, err = promptAgainer.PromptAgain(ans, invalid)
+					ans, err = promptAgainer.PromptAgain(&options.PromptConfig, ans, invalid)
 				} else {
-					ans, err = q.Prompt.Prompt()
+					ans, err = q.Prompt.Prompt(&options.PromptConfig)
 				}
 				// if there was a problem
 				if err != nil {
@@ -184,7 +323,7 @@ func Ask(qs []*Question, response interface{}, opts ...AskOpt) error {
 		}
 
 		// tell the prompt to cleanup with the validated value
-		q.Prompt.Cleanup(ans)
+		q.Prompt.Cleanup(&options.PromptConfig, ans)
 
 		// if something went wrong
 		if err != nil {
@@ -207,19 +346,7 @@ func Ask(qs []*Question, response interface{}, opts ...AskOpt) error {
 
 // paginate returns a single page of choices given the page size, the total list of
 // possible choices, and the current selected index in the total list.
-func paginate(page int, choices []string, sel int) ([]string, int) {
-	// the number of elements to show in a single page
-	var pageSize int
-	// if the select has a specific page size
-	if page != 0 {
-		// use the specified one
-		pageSize = page
-		// otherwise the select does not have a page size
-	} else {
-		// use the package default
-		pageSize = PageSize
-	}
-
+func paginate(pageSize int, choices []core.OptionAnswer, sel int) ([]core.OptionAnswer, int) {
 	var start, end, cursor int
 
 	if len(choices) < pageSize {
diff --git a/terminal/runereader.go b/terminal/runereader.go
index 74f3d83..89d2e29 100644
--- a/terminal/runereader.go
+++ b/terminal/runereader.go
@@ -3,6 +3,8 @@ package terminal
 import (
 	"fmt"
 	"unicode"
+
+	"github.com/mattn/go-runewidth"
 )
 
 type RuneReader struct {
@@ -89,16 +91,22 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
 		if r == KeyBackspace || r == KeyDelete {
 			// and we're not at the beginning of the line
 			if index > 0 && len(line) > 0 {
+
 				// if we are at the end of the word
 				if index == len(line) {
+
+					// get the last character display length
+					rw := runewidth.RuneWidth(line[len(line)-1])
+
 					// just remove the last letter from the internal representation
 					line = line[:len(line)-1]
+
 					// go back one
 					if cursorCurrent.X == 1 {
 						cursor.PreviousLine(1)
 						cursor.Forward(int(terminalSize.X))
 					} else {
-						cursor.Back(1)
+						cursor.Back(rw)
 					}
 
 					// clear the rest of the line
@@ -106,6 +114,9 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
 				} else {
 					// we need to remove a character from the middle of the word
 
+					// first, we need to get the display length of the deleted characters to move the cursor.
+					rw := runewidth.RuneWidth(line[index-1])
+
 					// remove the current index from the list
 					line = append(line[:index-1], line[index:]...)
 
@@ -115,7 +126,7 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
 					cursor.Save()
 
 					// clear the rest of the line
-					cursor.Back(1)
+					cursor.Back(rw)
 
 					// print what comes after
 					for _, char := range line[index-1:] {
@@ -136,7 +147,7 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
 						cursor.PreviousLine(1)
 						cursor.Forward(int(terminalSize.X))
 					} else {
-						cursor.Back(1)
+						cursor.Back(rw)
 					}
 				}
 
@@ -160,7 +171,9 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
 					cursor.PreviousLine(1)
 					cursor.Forward(int(terminalSize.X))
 				} else {
-					cursor.Back(1)
+					// get the length of the left character display
+					rw := runewidth.RuneWidth(line[index-1])
+					cursor.Back(rw)
 				}
 				//decrement the index
 				index--
@@ -183,7 +196,9 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
 				if cursorCurrent.CursorIsAtLineEnd(terminalSize) {
 					cursor.NextLine(1)
 				} else {
-					cursor.Forward(1)
+					// get the length of the left character display
+					rw := runewidth.RuneWidth(line[index])
+					cursor.Forward(rw)
 				}
 				index++
 
@@ -206,7 +221,9 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
 					cursorCurrent.Y--
 
 				} else {
-					cursor.Back(1)
+					// get the display length of the terminal character
+					rw := runewidth.RuneWidth(line[index-1])
+					cursor.Back(rw)
 					cursorCurrent.X--
 				}
 				index--
@@ -221,7 +238,9 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
 					cursorCurrent.Y++
 
 				} else {
-					cursor.Forward(1)
+					// move the cursor to the right to always move the length of the currently displayed character
+					rw := runewidth.RuneWidth(line[index])
+					cursor.Forward(rw)
 					cursorCurrent.X++
 				}
 				index++
@@ -306,7 +325,8 @@ func (rr *RuneReader) ReadLine(mask rune) ([]rune, error) {
 			if cursorCurrent.CursorIsAtLineEnd(terminalSize) {
 				cursor.NextLine(1)
 			} else {
-				cursor.Forward(1)
+				rw := runewidth.RuneWidth(r)
+				cursor.Forward(rw)
 			}
 			// increment the index
 			index++


Test patch:
diff --git a/confirm_test.go b/confirm_test.go
index 1a2576d..8e1ed94 100644
--- a/confirm_test.go
+++ b/confirm_test.go
@@ -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) ", defaultIcons().Question.Text),
 		},
 		{
 			"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) ", defaultIcons().Question.Text),
 		},
 		{
 			"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", defaultIcons().Question.Text),
 		},
 		{
 			"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) ", defaultIcons().Question.Text, string(defaultPromptConfig().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) ", defaultIcons().Help.Text, defaultIcons().Question.Text),
 		},
 	}
 
@@ -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 = defaultPromptConfig()
+
 		err = test.prompt.Render(
 			ConfirmQuestionTemplate,
 			test.data,
@@ -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(defaultPromptConfig().HelpInput),
 					),
 				)
 				c.SendLine("?")
diff --git a/core/write_test.go b/core/write_test.go
index 815c4f5..c89ec82 100644
--- a/core/write_test.go
+++ b/core/write_test.go
@@ -106,6 +106,113 @@ func TestWriteAnswer_canMutateStruct(t *testing.T) {
 	}
 }
 
+func TestWriteAnswer_optionAnswer(t *testing.T) {
+	t.Run("writes index for ints", func (t *testing.T) {
+		val := 0
+
+		// write a value to an existing field
+		err := WriteAnswer(&val, "", OptionAnswer{
+			Index: 10,
+			Value: "string value",
+		})
+
+		if err != nil {
+			t.Errorf("Encountered error while writing answer: %v", err.Error())
+			return
+		}
+
+		if val != 10 {
+			t.Errorf("Wrong value written. Wanted 10, got %v", val)
+			return
+		}
+	})
+
+	t.Run("writes OptionAnswer for OptionAnswer", func (t *testing.T) {
+		val := OptionAnswer{}
+
+		// write a value to an existing field
+		err := WriteAnswer(&val, "", OptionAnswer{
+			Index: 10,
+			Value: "string value",
+		})
+
+		if err != nil {
+			t.Errorf("Encountered error while writing answer: %v", err.Error())
+			return
+		}
+
+		if val.Index != 10 || val.Value != "string value" {
+			t.Errorf("Wrong internal values: %v", val)
+			return
+		}
+	})
+
+	t.Run("writes value for strings", func (t *testing.T) {
+		val := ""
+
+		// write a value to an existing field
+		err := WriteAnswer(&val, "", OptionAnswer{
+			Index: 10,
+			Value: "string value",
+		})
+
+		if err != nil {
+			t.Errorf("Encountered error while writing answer: %v", err.Error())
+			return
+		}
+
+		if val != "string value" {
+			t.Errorf("Wrong value written. Wanted \"100\", got %v", val)
+			return
+		}
+	})
+
+	t.Run("writes slice of indices for slice of ints", func (t *testing.T) {
+		val := []int{}
+
+		// write a value to an existing field
+		err := WriteAnswer(&val, "", []OptionAnswer{
+			{
+				Index: 10,
+				Value: "string value",
+			},
+		})
+
+		if err != nil {
+			t.Errorf("Encountered error while writing answer: %v", err.Error())
+			return
+		}
+
+		if len(val) != 1 || val[0] != 10 {
+			t.Errorf("Wrong value written. Wanted [10], got %v", val)
+			return
+		}
+	})
+
+	t.Run("writes slice of values for slice of strings", func (t *testing.T) {
+		val := []string{}
+
+		// write a value to an existing field
+		err := WriteAnswer(&val, "", []OptionAnswer{
+			{
+				Index: 10,
+				Value: "string value",
+			},
+		})
+
+		if err != nil {
+			t.Errorf("Encountered error while writing answer: %v", err.Error())
+			return
+		}
+
+
+		if len(val) != 1 || val[0] != "string value" {
+			t.Errorf("Wrong value written. Wanted [string value], got %v", val)
+			return
+		}
+	})
+}
+
 func TestWriteAnswer_canMutateMap(t *testing.T) {
 	// the map to hold the answer
 	ptr := make(map[string]interface{})
diff --git a/editor_test.go b/editor_test.go
index 5425257..b670437 100644
--- a/editor_test.go
+++ b/editor_test.go
@@ -31,49 +31,49 @@ func TestEditorRender(t *testing.T) {
 			"Test Editor question output without default",
 			Editor{Message: "What is your favorite month:"},
 			EditorTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: [Enter to launch editor] ", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: [Enter to launch editor] ", defaultIcons().Question.Text),
 		},
 		{
 			"Test Editor question output with default",
 			Editor{Message: "What is your favorite month:", Default: "April"},
 			EditorTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: (April) [Enter to launch editor] ", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: (April) [Enter to launch editor] ", defaultIcons().Question.Text),
 		},
 		{
 			"Test Editor question output with HideDefault",
 			Editor{Message: "What is your favorite month:", Default: "April", HideDefault: true},
 			EditorTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: [Enter to launch editor] ", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: [Enter to launch editor] ", defaultIcons().Question.Text),
 		},
 		{
 			"Test Editor answer output",
 			Editor{Message: "What is your favorite month:"},
 			EditorTemplateData{Answer: "October", ShowAnswer: true},
-			fmt.Sprintf("%s What is your favorite month: October\n", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: October\n", defaultIcons().Question.Text),
 		},
 		{
 			"Test Editor question output without default but with help hidden",
 			Editor{Message: "What is your favorite month:", Help: "This is helpful"},
 			EditorTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: [%s for help] [Enter to launch editor] ", core.QuestionIcon, string(core.HelpInputRune)),
+			fmt.Sprintf("%s What is your favorite month: [%s for help] [Enter to launch editor] ", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
 		},
 		{
 			"Test Editor question output with default and with help hidden",
 			Editor{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"},
 			EditorTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: [%s for help] (April) [Enter to launch editor] ", core.QuestionIcon, string(core.HelpInputRune)),
+			fmt.Sprintf("%s What is your favorite month: [%s for help] (April) [Enter to launch editor] ", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
 		},
 		{
 			"Test Editor question output without default but with help shown",
 			Editor{Message: "What is your favorite month:", Help: "This is helpful"},
 			EditorTemplateData{ShowHelp: true},
-			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: [Enter to launch editor] ", core.HelpIcon, core.QuestionIcon),
+			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: [Enter to launch editor] ", defaultIcons().Help.Text, defaultIcons().Question.Text),
 		},
 		{
 			"Test Editor question output with default and with help shown",
 			Editor{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"},
 			EditorTemplateData{ShowHelp: true},
-			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: (April) [Enter to launch editor] ", core.HelpIcon, core.QuestionIcon),
+			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: (April) [Enter to launch editor] ", defaultIcons().Help.Text, defaultIcons().Question.Text),
 		},
 	}
 
@@ -83,6 +83,10 @@ func TestEditorRender(t *testing.T) {
 
 		test.prompt.WithStdio(terminal.Stdio{Out: w})
 		test.data.Editor = test.prompt
+
+		// set the icon set
+		test.data.Config = defaultPromptConfig()
+
 		err = test.prompt.Render(
 			EditorQuestionTemplate,
 			test.data,
@@ -180,7 +184,7 @@ func TestEditorPrompt(t *testing.T) {
 				c.ExpectString(
 					fmt.Sprintf(
 						"Edit git commit message [%s for help] [Enter to launch editor]",
-						string(core.HelpInputRune),
+						string(defaultPromptConfig().HelpInput),
 					),
 				)
 				c.SendLine("?")
diff --git a/input_test.go b/input_test.go
index 2afd276..564d15b 100644
--- a/input_test.go
+++ b/input_test.go
@@ -7,10 +7,10 @@ import (
 	"os"
 	"testing"
 
-	expect "github.com/Netflix/go-expect"
-	"github.com/stretchr/testify/assert"
 	"github.com/AlecAivazis/survey/v2/core"
 	"github.com/AlecAivazis/survey/v2/terminal"
+	expect "github.com/Netflix/go-expect"
+	"github.com/stretchr/testify/assert"
 )
 
 func init() {
@@ -30,43 +30,43 @@ func TestInputRender(t *testing.T) {
 			"Test Input question output without default",
 			Input{Message: "What is your favorite month:"},
 			InputTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: ", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: ", defaultIcons().Question.Text),
 		},
 		{
 			"Test Input question output with default",
 			Input{Message: "What is your favorite month:", Default: "April"},
 			InputTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: (April) ", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: (April) ", defaultIcons().Question.Text),
 		},
 		{
 			"Test Input answer output",
 			Input{Message: "What is your favorite month:"},
 			InputTemplateData{Answer: "October", ShowAnswer: true},
-			fmt.Sprintf("%s What is your favorite month: October\n", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: October\n", defaultIcons().Question.Text),
 		},
 		{
 			"Test Input question output without default but with help hidden",
 			Input{Message: "What is your favorite month:", Help: "This is helpful"},
 			InputTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: [%s for help] ", core.QuestionIcon, string(core.HelpInputRune)),
+			fmt.Sprintf("%s What is your favorite month: [%s for help] ", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
 		},
 		{
 			"Test Input question output with default and with help hidden",
 			Input{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"},
 			InputTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: [%s for help] (April) ", core.QuestionIcon, string(core.HelpInputRune)),
+			fmt.Sprintf("%s What is your favorite month: [%s for help] (April) ", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
 		},
 		{
 			"Test Input question output without default but with help shown",
 			Input{Message: "What is your favorite month:", Help: "This is helpful"},
 			InputTemplateData{ShowHelp: true},
-			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: ", core.HelpIcon, core.QuestionIcon),
+			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: ", defaultIcons().Help.Text, defaultIcons().Question.Text),
 		},
 		{
 			"Test Input question output with default and with help shown",
 			Input{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"},
 			InputTemplateData{ShowHelp: true},
-			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: (April) ", core.HelpIcon, core.QuestionIcon),
+			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: (April) ", defaultIcons().Help.Text, defaultIcons().Question.Text),
 		},
 	}
 
@@ -76,6 +76,10 @@ func TestInputRender(t *testing.T) {
 
 		test.prompt.WithStdio(terminal.Stdio{Out: w})
 		test.data.Input = test.prompt
+
+		// set the runtime config
+		test.data.Config = defaultPromptConfig()
+
 		err = test.prompt.Render(
 			InputQuestionTemplate,
 			test.data,
diff --git a/multiline_test.go b/multiline_test.go
index 9e46462..fceacfb 100644
--- a/multiline_test.go
+++ b/multiline_test.go
@@ -7,10 +7,10 @@ import (
 	"os"
 	"testing"
 
-	expect "github.com/Netflix/go-expect"
-	"github.com/stretchr/testify/assert"
 	"github.com/AlecAivazis/survey/v2/core"
 	"github.com/AlecAivazis/survey/v2/terminal"
+	expect "github.com/Netflix/go-expect"
+	"github.com/stretchr/testify/assert"
 )
 
 func init() {
@@ -30,43 +30,43 @@ func TestMultilineRender(t *testing.T) {
 			"Test Multiline question output without default",
 			Multiline{Message: "What is your favorite month:"},
 			MultilineTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: [Enter 2 empty lines to finish]", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: [Enter 2 empty lines to finish]", defaultIcons().Question.Text),
 		},
 		{
 			"Test Multiline question output with default",
 			Multiline{Message: "What is your favorite month:", Default: "April"},
 			MultilineTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: (April) [Enter 2 empty lines to finish]", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: (April) [Enter 2 empty lines to finish]", defaultIcons().Question.Text),
 		},
 		{
 			"Test Multiline answer output",
 			Multiline{Message: "What is your favorite month:"},
 			MultilineTemplateData{Answer: "October", ShowAnswer: true},
-			fmt.Sprintf("%s What is your favorite month: \nOctober", core.QuestionIcon),
+			fmt.Sprintf("%s What is your favorite month: \nOctober", defaultIcons().Question.Text),
 		},
 		{
 			"Test Multiline question output without default but with help hidden",
 			Multiline{Message: "What is your favorite month:", Help: "This is helpful"},
 			MultilineTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: [Enter 2 empty lines to finish]", string(core.HelpInputRune)),
+			fmt.Sprintf("%s What is your favorite month: [Enter 2 empty lines to finish]", string(defaultPromptConfig().HelpInput)),
 		},
 		{
 			"Test Multiline question output with default and with help hidden",
 			Multiline{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"},
 			MultilineTemplateData{},
-			fmt.Sprintf("%s What is your favorite month: (April) [Enter 2 empty lines to finish]", string(core.HelpInputRune)),
+			fmt.Sprintf("%s What is your favorite month: (April) [Enter 2 empty lines to finish]", string(defaultPromptConfig().HelpInput)),
 		},
 		{
 			"Test Multiline question output without default but with help shown",
 			Multiline{Message: "What is your favorite month:", Help: "This is helpful"},
 			MultilineTemplateData{ShowHelp: true},
-			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: [Enter 2 empty lines to finish]", core.HelpIcon, core.QuestionIcon),
+			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: [Enter 2 empty lines to finish]", defaultIcons().Help.Text, defaultIcons().Question.Text),
 		},
 		{
 			"Test Multiline question output with default and with help shown",
 			Multiline{Message: "What is your favorite month:", Default: "April", Help: "This is helpful"},
 			MultilineTemplateData{ShowHelp: true},
-			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: (April) [Enter 2 empty lines to finish]", core.HelpIcon, core.QuestionIcon),
+			fmt.Sprintf("%s This is helpful\n%s What is your favorite month: (April) [Enter 2 empty lines to finish]", defaultIcons().Help.Text, defaultIcons().Question.Text),
 		},
 	}
 
@@ -76,6 +76,9 @@ func TestMultilineRender(t *testing.T) {
 
 		test.prompt.WithStdio(terminal.Stdio{Out: w})
 		test.data.Multiline = test.prompt
+		// set the icon set
+		test.data.Config = defaultPromptConfig()
+
 		err = test.prompt.Render(
 			MultilineQuestionTemplate,
 			test.data,
diff --git a/multiselect_test.go b/multiselect_test.go
index f0cedb1..2fae154 100644
--- a/multiselect_test.go
+++ b/multiselect_test.go
@@ -38,69 +38,69 @@ func TestMultiSelectRender(t *testing.T) {
 		expected string
 	}{
 		{
-			"Test MultiSelect question output",
+			"question output",
 			prompt,
 			MultiSelectTemplateData{
 				SelectedIndex: 2,
-				PageEntries:   prompt.Options,
-				Checked:       map[string]bool{"bar": true, "buz": true},
+				PageEntries:   core.OptionAnswerList(prompt.Options),
+				Checked:       map[int]bool{1: true, 3: true},
 			},
 			strings.Join(
 				[]string{
-					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, type to filter]", core.QuestionIcon),
-					fmt.Sprintf("  %s  foo", core.UnmarkedOptionIcon),
-					fmt.Sprintf("  %s  bar", core.MarkedOptionIcon),
-					fmt.Sprintf("%s %s  baz", core.SelectFocusIcon, core.UnmarkedOptionIcon),
-					fmt.Sprintf("  %s  buz\n", core.MarkedOptionIcon),
+					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, type to filter]", defaultIcons().Question.Text),
+					fmt.Sprintf("  %s  foo", defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  bar", defaultIcons().MarkedOption.Text),
+					fmt.Sprintf("%s %s  baz", defaultIcons().SelectFocus.Text, defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  buz\n", defaultIcons().MarkedOption.Text),
 				},
 				"\n",
 			),
 		},
 		{
-			"Test MultiSelect answer output",
+			"answer output",
 			prompt,
 			MultiSelectTemplateData{
 				Answer:     "foo, buz",
 				ShowAnswer: true,
 			},
-			fmt.Sprintf("%s Pick your words: foo, buz\n", core.QuestionIcon),
+			fmt.Sprintf("%s Pick your words: foo, buz\n", defaultIcons().Question.Text),
 		},
 		{
-			"Test MultiSelect question output with help hidden",
+			"help hidden",
 			helpfulPrompt,
 			MultiSelectTemplateData{
 				SelectedIndex: 2,
-				PageEntries:   prompt.Options,
-				Checked:       map[string]bool{"bar": true, "buz": true},
+				PageEntries:   core.OptionAnswerList(prompt.Options),
+				Checked:       map[int]bool{1: true, 3: true},
 			},
 			strings.Join(
 				[]string{
-					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, type to filter, %s for more help]", core.QuestionIcon, string(core.HelpInputRune)),
-					fmt.Sprintf("  %s  foo", core.UnmarkedOptionIcon),
-					fmt.Sprintf("  %s  bar", core.MarkedOptionIcon),
-					fmt.Sprintf("%s %s  baz", core.SelectFocusIcon, core.UnmarkedOptionIcon),
-					fmt.Sprintf("  %s  buz\n", core.MarkedOptionIcon),
+					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, type to filter, %s for more help]", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
+					fmt.Sprintf("  %s  foo", defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  bar", defaultIcons().MarkedOption.Text),
+					fmt.Sprintf("%s %s  baz", defaultIcons().SelectFocus.Text, defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  buz\n", defaultIcons().MarkedOption.Text),
 				},
 				"\n",
 			),
 		},
 		{
-			"Test MultiSelect question output with help shown",
+			"question outputhelp shown",
 			helpfulPrompt,
 			MultiSelectTemplateData{
 				SelectedIndex: 2,
-				PageEntries:   prompt.Options,
-				Checked:       map[string]bool{"bar": true, "buz": true},
+				PageEntries:   core.OptionAnswerList(prompt.Options),
+				Checked:       map[int]bool{1: true, 3: true},
 				ShowHelp:      true,
 			},
 			strings.Join(
 				[]string{
-					fmt.Sprintf("%s This is helpful", core.HelpIcon),
-					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, type to filter]", core.QuestionIcon),
-					fmt.Sprintf("  %s  foo", core.UnmarkedOptionIcon),
-					fmt.Sprintf("  %s  bar", core.MarkedOptionIcon),
-					fmt.Sprintf("%s %s  baz", core.SelectFocusIcon, core.UnmarkedOptionIcon),
-					fmt.Sprintf("  %s  buz\n", core.MarkedOptionIcon),
+					fmt.Sprintf("%s This is helpful", defaultIcons().Help.Text),
+					fmt.Sprintf("%s Pick your words:  [Use arrows to move, space to select, type to filter]", defaultIcons().Question.Text),
+					fmt.Sprintf("  %s  foo", defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  bar", defaultIcons().MarkedOption.Text),
+					fmt.Sprintf("%s %s  baz", defaultIcons().SelectFocus.Text, defaultIcons().UnmarkedOption.Text),
+					fmt.Sprintf("  %s  buz\n", defaultIcons().MarkedOption.Text),
 				},
 				"\n",
 			),
@@ -113,6 +113,10 @@ func TestMultiSelectRender(t *testing.T) {
 
 		test.prompt.WithStdio(terminal.Stdio{Out: w})
 		test.data.MultiSelect = test.prompt
+
+		// set the icon set
+		test.data.Config = defaultPromptConfig()
+
 		err = test.prompt.Render(
 			MultiSelectQuestionTemplate,
 			test.data,
@@ -130,7 +134,7 @@ func TestMultiSelectRender(t *testing.T) {
 func TestMultiSelectPrompt(t *testing.T) {
 	tests := []PromptTest{
 		{
-			"Test MultiSelect prompt interaction",
+			"basic interaction",
 			&MultiSelect{
 				Message: "What days do you prefer:",
 				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
@@ -142,10 +146,10 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine(" ")
 				c.ExpectEOF()
 			},
-			[]string{"Monday"},
+			[]core.OptionAnswer{core.OptionAnswer{Value: "Monday", Index: 1}},
 		},
 		{
-			"Test MultiSelect prompt interaction with default",
+			"default value as []string",
 			&MultiSelect{
 				Message: "What days do you prefer:",
 				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
@@ -156,10 +160,30 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine("")
 				c.ExpectEOF()
 			},
-			[]string{"Tuesday", "Thursday"},
+			[]core.OptionAnswer{
+				core.OptionAnswer{Value: "Tuesday", Index: 2},
+				core.OptionAnswer{Value: "Thursday", Index: 4},
+			},
+		},
+		{
+			"default value as []int",
+			&MultiSelect{
+				Message: "What days do you prefer:",
+				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
+				Default: []int{2, 4},
+			},
+			func(c *expect.Console) {
+				c.ExpectString("What days do you prefer:  [Use arrows to move, space to select, type to filter]")
+				c.SendLine("")
+				c.ExpectEOF()
+			},
+			[]core.OptionAnswer{
+				core.OptionAnswer{Value: "Tuesday", Index: 2},
+				core.OptionAnswer{Value: "Thursday", Index: 4},
+			},
 		},
 		{
-			"Test MultiSelect prompt interaction overriding default",
+			"overriding default",
 			&MultiSelect{
 				Message: "What days do you prefer:",
 				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
@@ -173,10 +197,10 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine(" ")
 				c.ExpectEOF()
 			},
-			[]string{"Thursday"},
+			[]core.OptionAnswer{core.OptionAnswer{Value: "Thursday", Index: 4}},
 		},
 		{
-			"Test MultiSelect prompt interaction and prompt for help",
+			"prompt for help",
 			&MultiSelect{
 				Message: "What days do you prefer:",
 				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
@@ -191,10 +215,10 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine(" ")
 				c.ExpectEOF()
 			},
-			[]string{"Saturday"},
+			[]core.OptionAnswer{core.OptionAnswer{Value: "Saturday", Index: 6}},
 		},
 		{
-			"Test MultiSelect prompt interaction with page size",
+			"page size",
 			&MultiSelect{
 				Message:  "What days do you prefer:",
 				Options:  []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
@@ -207,10 +231,10 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine(" ")
 				c.ExpectEOF()
 			},
-			[]string{"Monday"},
+			[]core.OptionAnswer{core.OptionAnswer{Value: "Monday", Index: 1}},
 		},
 		{
-			"Test MultiSelect prompt interaction with vim mode",
+			"vim mode",
 			&MultiSelect{
 				Message: "What days do you prefer:",
 				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
@@ -227,10 +251,14 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine("")
 				c.ExpectEOF()
 			},
-			[]string{"Tuesday", "Thursday", "Saturday"},
+			[]core.OptionAnswer{
+				core.OptionAnswer{Value: "Tuesday", Index: 2},
+				core.OptionAnswer{Value: "Thursday", Index: 4},
+				core.OptionAnswer{Value: "Saturday", Index: 6},
+			},
 		},
 		{
-			"Test MultiSelect prompt interaction with filter",
+			"filter interaction",
 			&MultiSelect{
 				Message: "What days do you prefer:",
 				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
@@ -244,10 +272,10 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine("")
 				c.ExpectEOF()
 			},
-			[]string{"Tuesday"},
+			[]core.OptionAnswer{core.OptionAnswer{Value: "Tuesday", Index: 2}},
 		},
 		{
-			"Test MultiSelect prompt interaction with filter is case-insensitive",
+			"filter is case-insensitive",
 			&MultiSelect{
 				Message: "What days do you prefer:",
 				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
@@ -261,21 +289,15 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine("")
 				c.ExpectEOF()
 			},
-			[]string{"Tuesday"},
+			[]core.OptionAnswer{core.OptionAnswer{Value: "Tuesday", Index: 2}},
 		},
 		{
-			"Test MultiSelect prompt interaction with custom filter",
+			"custom filter",
 			&MultiSelect{
 				Message: "What days do you prefer:",
 				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
-				Filter: func(filter string, options []string) (filtered []string) {
-					result := DefaultFilter(filter, options)
-					for _, v := range result {
-						if len(v) >= 7 {
-							filtered = append(filtered, v)
-						}
-					}
-					return
+				Filter: func(filterValue string, optValue string, index int) bool {
+					return strings.Contains(optValue, filterValue) && len(optValue) >= 7
 				},
 			},
 			func(c *expect.Console) {
@@ -287,10 +309,10 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine(" ")
 				c.ExpectEOF()
 			},
-			[]string{"Wednesday"},
+			[]core.OptionAnswer{core.OptionAnswer{Value: "Wednesday", Index: 3}},
 		},
 		{
-			"Test MultiSelect clears input on select",
+			"clears input on select",
 			&MultiSelect{
 				Message: "What days do you prefer:",
 				Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
@@ -308,7 +330,7 @@ func TestMultiSelectPrompt(t *testing.T) {
 				c.SendLine("")
 				c.ExpectEOF()
 			},
-			[]string{},
+			[]core.OptionAnswer{},
 		},
 	}
 
diff --git a/password_test.go b/password_test.go
index 9003b4e..ba86a4f 100644
--- a/password_test.go
+++ b/password_test.go
@@ -26,24 +26,28 @@ func TestPasswordRender(t *testing.T) {
 			"Test Password question output",
 			Password{Message: "Tell me your secret:"},
 			PasswordTemplateData{},
-			fmt.Sprintf("%s Tell me your secret: ", core.QuestionIcon),
+			fmt.Sprintf("%s Tell me your secret: ", defaultIcons().Question.Text),
 		},
 		{
 			"Test Password question output with help hidden",
 			Password{Message: "Tell me your secret:", Help: "This is helpful"},
 			PasswordTemplateData{},
-			fmt.Sprintf("%s Tell me your secret: [%s for help] ", core.QuestionIcon, string(core.HelpInputRune)),
+			fmt.Sprintf("%s Tell me your secret: [%s for help] ", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
 		},
 		{
 			"Test Password question output with help shown",
 			Password{Message: "Tell me your secret:", Help: "This is helpful"},
 			PasswordTemplateData{ShowHelp: true},
-			fmt.Sprintf("%s This is helpful\n%s Tell me your secret: ", core.HelpIcon, core.QuestionIcon),
+			fmt.Sprintf("%s This is helpful\n%s Tell me your secret: ", defaultIcons().Help.Text, defaultIcons().Question.Text),
 		},
 	}
 
 	for _, test := range tests {
 		test.data.Password = test.prompt
+
+		// set the icon set
+		test.data.Config = defaultPromptConfig()
+
 		actual, err := core.RunTemplate(
 			PasswordQuestionTemplate,
 			&test.data,
diff --git a/renderer_test.go b/renderer_test.go
new file mode 100755
index 0000000..c8aec90
--- /dev/null
+++ b/renderer_test.go
@@ -0,0 +1,30 @@
+package survey
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/AlecAivazis/survey/v2/core"
+)
+
+func TestValidationError(t *testing.T) {
+
+	err := fmt.Errorf("Football is not a valid month")
+
+	actual, err := core.RunTemplate(
+		ErrorTemplate,
+		&ErrorTemplateData{
+			Error: err,
+			Icon:  defaultIcons().Error,
+		},
+	)
+	if err != nil {
+		t.Errorf("Failed to run template to format error: %s", err)
+	}
+
+	expected := fmt.Sprintf("%s Sorry, your reply was invalid: Football is not a valid month\n", defaultIcons().Error.Text)
+
+	if actual != expected {
+		t.Errorf("Formatted error was not formatted correctly. Found:\n%s\nExpected:\n%s", actual, expected)
+	}
+}
diff --git a/select_test.go b/select_test.go
index 913b739..fc3b985 100644
--- a/select_test.go
+++ b/select_test.go
@@ -40,13 +40,13 @@ func TestSelectRender(t *testing.T) {
 		{
 			"Test Select question output",
 			prompt,
-			SelectTemplateData{SelectedIndex: 2, PageEntries: prompt.Options},
+			SelectTemplateData{SelectedIndex: 2, PageEntries: core.OptionAnswerList(prompt.Options)},
 			strings.Join(
 				[]string{
-					fmt.Sprintf("%s Pick your word:  [Use arrows to move, space to select, type to filter]", core.QuestionIcon),
+					fmt.Sprintf("%s Pick your word:  [Use arrows to move, type to filter]", defaultIcons().Question.Text),
 					"  foo",
 					"  bar",
-					fmt.Sprintf("%s baz", core.SelectFocusIcon),
+					fmt.Sprintf("%s baz", defaultIcons().SelectFocus.Text),
 					"  buz\n",
 				},
 				"\n",
@@ -55,19 +55,19 @@ func TestSelectRender(t *testing.T) {
 		{
 			"Test Select answer output",
 			prompt,
-			SelectTemplateData{Answer: "buz", ShowAnswer: true, PageEntries: prompt.Options},
-			fmt.Sprintf("%s Pick your word: buz\n", core.QuestionIcon),
+			SelectTemplateData{Answer: "buz", ShowAnswer: true, PageEntries: core.OptionAnswerList(prompt.Options)},
+			fmt.Sprintf("%s Pick your word: buz\n", defaultIcons().Question.Text),
 		},
 		{
 			"Test Select question output with help hidden",
 			helpfulPrompt,
-			SelectTemplateData{SelectedIndex: 2, PageEntries: prompt.Options},
+			SelectTemplateData{SelectedIndex: 2, PageEntries: core.OptionAnswerList(prompt.Options)},
 			strings.Join(
 				[]string{
-					fmt.Sprintf("%s Pick your word:  [Use arrows to move, space to select, type to filter, %s for more help]", core.QuestionIcon, string(core.HelpInputRune)),
+					fmt.Sprintf("%s Pick your word:  [Use arrows to move, type to filter, %s for more help]", defaultIcons().Question.Text, string(defaultPromptConfig().HelpInput)),
 					"  foo",
 					"  bar",
-					fmt.Sprintf("%s baz", core.SelectFocusIcon),
+					fmt.Sprintf("%s baz", defaultIcons().SelectFocus.Text),
 					"  buz\n",
 				},
 				"\n",
@@ -76,14 +76,14 @@ func TestSelectRender(t *testing.T) {
 		{
 			"Test Select question output with help shown",
 			helpfulPrompt,
-			SelectTemplateData{SelectedIndex: 2, ShowHelp: true, PageEntries: prompt.Options},
+			SelectTemplateData{SelectedIndex: 2, ShowHelp: true, PageEntries: core.OptionAnswerList(prompt.Options)},
 			strings.Join(
 				[]string{
-					fmt.Sprintf("%s This is helpful", core.HelpIcon),
-					fmt.Sprintf("%s Pick your word:  [Use arrows to move, space to select, type to filter]", core.QuestionIcon),
+					fmt.Sprintf("%s This is helpful", defaultIcons().Help.Text),
+					fmt.Sprintf("%s Pick your word:  [Use arrows to move, type to filter]", defaultIcons().Question.Text),
 					"  foo",
 					"  bar",
-					fmt.Sprintf("%s baz", core.SelectFocusIcon),
+					fmt.Sprintf("%s baz", defaultIcons().SelectFocus.Text),
 					"  buz\n",
 				},
 				"\n",
@@ -97,11 +97,18 @@ func TestSelectRender(t *testing.T) {
 
 		test.prompt.WithStdio(terminal.Stdio{Out: w})
 		test.data.Select = test.prompt
+
+		// set the icon set
+		test.data.Config = defaultPromptConfig()
+
 		err = test.prompt.Render(
 			SelectQuestionTemplate,
 			test.data,
 		)
-		assert.Nil(t, err, test.title)
+		if !assert.Nil(t, err, test.title) {
+			fmt.Println(err.Error())
+			return
+		}
 
 		w.Close()
 		var buf bytes.Buffer
@@ -114,7 +121,7 @@ func TestSelectRender(t *testing.T) {
 func TestSelectPrompt(t *testing.T) {
 	tests := []PromptTest{
 		{
-			"Test Select prompt interaction",
+			"basic interaction",
 			&Select{
 				Message: "Choose a color:",
 				Options: []string{"red", "blue", "green"},
@@ -125,10 +132,10 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine(string(terminal.KeyArrowDown))
 				c.ExpectEOF()
 			},
-			"blue",
+			core.OptionAnswer{Index: 1, Value: "blue"},
 		},
 		{
-			"Test Select prompt interaction with default",
+			"default value",
 			&Select{
 				Message: "Choose a color:",
 				Options: []string{"red", "blue", "green"},
@@ -140,10 +147,25 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine("")
 				c.ExpectEOF()
 			},
-			"green",
+			core.OptionAnswer{Index: 2, Value: "green"},
+		},
+		{
+			"default index",
+			&Select{
+				Message: "Choose a color:",
+				Options: []string{"red", "blue", "green"},
+				Default: 2,
+			},
+			func(c *expect.Console) {
+				c.ExpectString("Choose a color:")
+				// Select green.
+				c.SendLine("")
+				c.ExpectEOF()
+			},
+			core.OptionAnswer{Index: 2, Value: "green"},
 		},
 		{
-			"Test Select prompt interaction overriding default",
+			"overriding default",
 			&Select{
 				Message: "Choose a color:",
 				Options: []string{"red", "blue", "green"},
@@ -155,10 +177,10 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine(string(terminal.KeyArrowUp))
 				c.ExpectEOF()
 			},
-			"red",
+			core.OptionAnswer{Index: 0, Value: "red"},
 		},
 		{
-			"Test Select prompt interaction and prompt for help",
+			"prompt for help",
 			&Select{
 				Message: "Choose a color:",
 				Options: []string{"red", "blue", "green"},
@@ -172,10 +194,10 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine("")
 				c.ExpectEOF()
 			},
-			"red",
+			core.OptionAnswer{Index: 0, Value: "red"},
 		},
 		{
-			"Test Select prompt interaction with page size",
+			"PageSize",
 			&Select{
 				Message:  "Choose a color:",
 				Options:  []string{"red", "blue", "green"},
@@ -187,10 +209,10 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine(string(terminal.KeyArrowUp))
 				c.ExpectEOF()
 			},
-			"green",
+			core.OptionAnswer{Index: 2, Value: "green"},
 		},
 		{
-			"Test Select prompt interaction with vim mode",
+			"vim mode",
 			&Select{
 				Message: "Choose a color:",
 				Options: []string{"red", "blue", "green"},
@@ -202,10 +224,10 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine("j")
 				c.ExpectEOF()
 			},
-			"blue",
+			core.OptionAnswer{Index: 1, Value: "blue"},
 		},
 		{
-			"Test Select prompt interaction with filter",
+			"filter",
 			&Select{
 				Message: "Choose a color:",
 				Options: []string{"red", "blue", "green"},
@@ -218,10 +240,10 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine(string(terminal.KeyArrowDown))
 				c.ExpectEOF()
 			},
-			"green",
+			core.OptionAnswer{Index: 2, Value: "green"},
 		},
 		{
-			"Test Select prompt interaction with filter is case-insensitive",
+			"filter is case-insensitive",
 			&Select{
 				Message: "Choose a color:",
 				Options: []string{"red", "blue", "green"},
@@ -234,7 +256,7 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine(string(terminal.KeyArrowDown))
 				c.ExpectEOF()
 			},
-			"green",
+			core.OptionAnswer{Index: 2, Value: "green"},
 		},
 		{
 			"Can select the first result in a filtered list if there is a default",
@@ -249,21 +271,15 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine("red")
 				c.ExpectEOF()
 			},
-			"red",
+			core.OptionAnswer{Index: 0, Value: "red"},
 		},
 		{
-			"Test Select prompt interaction with custom filter",
+			"custom filter",
 			&Select{
 				Message: "Choose a color:",
 				Options: []string{"red", "blue", "green"},
-				Filter: func(filter string, options []string) (filtered []string) {
-					result := DefaultFilter(filter, options)
-					for _, v := range result {
-						if len(v) >= 5 {
-							filtered = append(filtered, v)
-						}
-					}
-					return
+				Filter: func(filter string, optValue string, optIndex int) (filtered bool) {
+					return len(optValue) >= 5
 				},
 			},
 			func(c *expect.Console) {
@@ -272,10 +288,10 @@ func TestSelectPrompt(t *testing.T) {
 				c.SendLine("re")
 				c.ExpectEOF()
 			},
-			"green",
+			core.OptionAnswer{Index: 2, Value: "green"},
 		},
 		{
-			"Test Select prompt with answers filtered out",
+			"answers filtered out",
 			&Select{
 				Message: "Choose a color:",
 				Options: []string{"red", "blue", "green"},
@@ -293,7 +309,7 @@ func TestSelectPrompt(t *testing.T) {
 				// press enter
 				c.SendLine(string(terminal.KeyEnter))
 			},
-			"red",
+			core.OptionAnswer{Index: 0, Value: "red"},
 		},
 	}
 
diff --git a/survey_posix_test.go b/survey_posix_test.go
index 32df32c..95dea35 100644
--- a/survey_posix_test.go
+++ b/survey_posix_test.go
@@ -6,10 +6,10 @@ import (
 	"bytes"
 	"testing"
 
+	"github.com/AlecAivazis/survey/v2/terminal"
 	expect "github.com/Netflix/go-expect"
 	"github.com/hinshun/vt10x"
 	"github.com/stretchr/testify/require"
-	"github.com/AlecAivazis/survey/v2/terminal"
 )
 
 func RunTest(t *testing.T, procedure func(*expect.Console), test func(terminal.Stdio) error) {
diff --git a/survey_test.go b/survey_test.go
index b482542..658ddbb 100644
--- a/survey_test.go
+++ b/survey_test.go
@@ -36,13 +36,89 @@ func RunPromptTest(t *testing.T, test PromptTest) {
 		if p, ok := test.prompt.(wantsStdio); ok {
 			p.WithStdio(stdio)
 		}
-		answer, err = test.prompt.Prompt()
+
+		answer, err = test.prompt.Prompt(defaultPromptConfig())
 		return err
 	})
 	require.Equal(t, test.expected, answer)
 }
 
+func TestPagination_tooFew(t *testing.T) {
+	// a small list of options
+	choices := core.OptionAnswerList([]string{"choice1", "choice2", "choice3"})
+
+	// a page bigger than the total number
+	pageSize := 4
+	// the current selection
+	sel := 3
+
+	// compute the page info
+	page, idx := paginate(pageSize, choices, sel)
+
+	// make sure we see the full list of options
+	assert.Equal(t, choices, page)
+	// with the second index highlighted (no change)
+	assert.Equal(t, 3, idx)
+}
+
+func TestPagination_firstHalf(t *testing.T) {
+	// the choices for the test
+	choices := core.OptionAnswerList([]string{"choice1", "choice2", "choice3", "choice4", "choice5", "choice6"})
+
+	// section the choices into groups of 4 so the choice is somewhere in the middle
+	// to verify there is no displacement of the page
+	pageSize := 4
+	// test the second item
+	sel := 2
+
+	// compute the page info
+	page, idx := paginate(pageSize, choices, sel)
+
+	// we should see the first three options
+	assert.Equal(t, choices[0:4], page)
+	// with the second index highlighted
+	assert.Equal(t, 2, idx)
+}
+
+func TestPagination_middle(t *testing.T) {
+	// the choices for the test
+	choices := core.OptionAnswerList([]string{"choice0", "choice1", "choice2", "choice3", "choice4", "choice5"})
+
+	// section the choices into groups of 3
+	pageSize := 2
+	// test the second item so that we can verify we are in the middle of the list
+	sel := 3
+
+	// compute the page info
+	page, idx := paginate(pageSize, choices, sel)
+
+	// we should see the first three options
+	assert.Equal(t, choices[2:4], page)
+	// with the second index highlighted
+	assert.Equal(t, 1, idx)
+}
+
+func TestPagination_lastHalf(t *testing.T) {
+	// the choices for the test
+	choices := core.OptionAnswerList([]string{"choice0", "choice1", "choice2", "choice3", "choice4", "choice5"})
+
+	// section the choices into groups of 3
+	pageSize := 3
+	// test the last item to verify we're not in the middle
+	sel := 5
+
+	// compute the page info
+	page, idx := paginate(pageSize, choices, sel)
+
+	// we should see the first three options
+	assert.Equal(t, choices[3:6], page)
+	// we should be at the bottom of the list
+	assert.Equal(t, 2, idx)
+}
+
 func TestAsk(t *testing.T) {
+	t.Skip()
+	return
 	tests := []struct {
 		name      string
 		questions []*Question
@@ -151,7 +227,7 @@ func TestAsk(t *testing.T) {
 				c.SendLine("")
 
 				// Select
-				c.ExpectString("Choose a color:  [Use arrows to move, space to select, type to filter]")
+				c.ExpectString("Choose a color:  [Use arrows to move, type to filter]")
 				c.SendLine("yellow")
 				c.ExpectEOF()
 			},
@@ -222,25 +298,6 @@ func TestAsk(t *testing.T) {
 	}
 }
 
-func TestValidationError(t *testing.T) {
-
-	err := fmt.Errorf("Football is not a valid month")
-
-	actual, err := core.RunTemplate(
-		core.ErrorTemplate,
-		err,
-	)
-	if err != nil {
-		t.Errorf("Failed to run template to format error: %s", err)
-	}
-
-	expected := fmt.Sprintf("%s Sorry, your reply was invalid: Football is not a valid month\n", core.ErrorIcon)
-
-	if actual != expected {
-		t.Errorf("Formatted error was not formatted correctly. Found:\n%s\nExpected:\n%s", actual, expected)
-	}
-}
-
 func TestAsk_returnsErrorIfTargetIsNil(t *testing.T) {
 	// pass an empty place to leave the answers
 	err := Ask([]*Question{}, nil)
@@ -251,76 +308,3 @@ func TestAsk_returnsErrorIfTargetIsNil(t *testing.T) {
 		t.Error("Did not encounter error when asking with no where to record.")
 	}
 }
-
-func TestPagination_tooFew(t *testing.T) {
-	// a small list of options
-	choices := []string{"choice1", "choice2", "choice3"}
-
-	// a page bigger than the total number
-	pageSize := 4
-	// the current selection
-	sel := 3
-
-	// compute the page info
-	page, idx := paginate(pageSize, choices, sel)
-
-	// make sure we see the full list of options
-	assert.Equal(t, choices, page)
-	// with the second index highlighted (no change)
-	assert.Equal(t, 3, idx)
-}
-
-func TestPagination_firstHalf(t *testing.T) {
-	// the choices for the test
-	choices := []string{"choice1", "choice2", "choice3", "choice4", "choice5", "choice6"}
-
-	// section the choices into groups of 4 so the choice is somewhere in the middle
-	// to verify there is no displacement of the page
-	pageSize := 4
-	// test the second item
-	sel := 2
-
-	// compute the page info
-	page, idx := paginate(pageSize, choices, sel)
-
-	// we should see the first three options
-	assert.Equal(t, choices[0:4], page)
-	// with the second index highlighted
-	assert.Equal(t, 2, idx)
-}
-
-func TestPagination_middle(t *testing.T) {
-	// the choices for the test
-	choices := []string{"choice0", "choice1", "choice2", "choice3", "choice4", "choice5"}
-
-	// section the choices into groups of 3
-	pageSize := 2
-	// test the second item so that we can verify we are in the middle of the list
-	sel := 3
-
-	// compute the page info
-	page, idx := paginate(pageSize, choices, sel)
-
-	// we should see the first three options
-	assert.Equal(t, choices[2:4], page)
-	// with the second index highlighted
-	assert.Equal(t, 1, idx)
-}
-
-func TestPagination_lastHalf(t *testing.T) {
-	// the choices for the test
-	choices := []string{"choice0", "choice1", "choice2", "choice3", "choice4", "choice5"}
-
-	// section the choices into groups of 3
-	pageSize := 3
-	// test the last item to verify we're not in the middle
-	sel := 5
-
-	// compute the page info
-	page, idx := paginate(pageSize, choices, sel)
-
-	// we should see the first three options
-	assert.Equal(t, choices[3:6], page)
-	// we should be at the bottom of the list
-	assert.Equal(t, 2, idx)
-}
diff --git a/survey_windows_test.go b/survey_windows_test.go
index 502af6f..e22022c 100644
--- a/survey_windows_test.go
+++ b/survey_windows_test.go
@@ -3,8 +3,8 @@ package survey
 import (
 	"testing"
 
-	expect "github.com/Netflix/go-expect"
 	"github.com/AlecAivazis/survey/v2/terminal"
+	expect "github.com/Netflix/go-expect"
 )
 
 func RunTest(t *testing.T, procedure func(*expect.Console), test func(terminal.Stdio) error) {
diff --git a/tests/ask.go b/tests/ask.go
index 3a5797a..aa8f2f6 100644
--- a/tests/ask.go
+++ b/tests/ask.go
@@ -42,7 +42,7 @@ func main() {
 
 	fmt.Println("Asking one.")
 	answer := ""
-	err = survey.AskOne(simpleQs[0].Prompt, &answer, nil)
+	err = survey.AskOne(simpleQs[0].Prompt, &answer)
 	if err != nil {
 		fmt.Println(err.Error())
 		return
@@ -51,7 +51,7 @@ func main() {
 
 	fmt.Println("Asking one with validation.")
 	vAns := ""
-	err = survey.AskOne(&survey.Input{Message: "What is your name?"}, &vAns, survey.Required)
+	err = survey.AskOne(&survey.Input{Message: "What is your name?"}, &vAns, survey.WithValidator(survey.Required))
 	if err != nil {
 		fmt.Println(err.Error())
 		return
diff --git a/tests/longSelect.go b/tests/longSelect.go
index c40a618..325681b 100644
--- a/tests/longSelect.go
+++ b/tests/longSelect.go
@@ -19,5 +19,5 @@ func main() {
 			"j",
 		},
 	}
-	survey.AskOne(prompt, &color, nil)
+	survey.AskOne(prompt, &color)
 }
diff --git a/tests/util/test.go b/tests/util/test.go
index 1ce209d..9cbf4cd 100644
--- a/tests/util/test.go
+++ b/tests/util/test.go
@@ -26,7 +26,7 @@ func RunTable(table []TestTableEntry) {
 		// tell the user what we are going to ask them
 		fmt.Println(entry.Name)
 		// perform the ask
-		err := survey.AskOne(entry.Prompt, entry.Value, entry.Validate)
+		err := survey.AskOne(entry.Prompt, entry.Value)
 		if err != nil {
 			fmt.Printf("AskOne on %v's prompt failed: %v.", entry.Name, err.Error())
 			break
@@ -42,7 +42,7 @@ func RunErrorTable(table []TestTableEntry) {
 		// tell the user what we are going to ask them
 		fmt.Println(entry.Name)
 		// perform the ask
-		err := survey.AskOne(entry.Prompt, entry.Value, entry.Validate)
+		err := survey.AskOne(entry.Prompt, entry.Value, survey.WithValidator(entry.Validate))
 		if err == nil {
 			fmt.Printf("AskOne on %v's prompt didn't fail.", entry.Name)
 			break