Initial commit

This commit is contained in:
Donny
2019-04-22 20:46:32 +08:00
commit 49ab8aadd1
25441 changed files with 4055000 additions and 0 deletions

45
vendor/github.com/AlecAivazis/survey/BUILD generated vendored Normal file
View File

@@ -0,0 +1,45 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"confirm.go",
"editor.go",
"input.go",
"multiline.go",
"multiselect.go",
"password.go",
"select.go",
"survey.go",
"transform.go",
"validate.go",
],
data = ["Gopkg.toml"],
importpath = "github.com/AlecAivazis/survey",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/kballard/go-shellquote:go_default_library",
"//vendor/gopkg.in/AlecAivazis/survey.v1/core:go_default_library",
"//vendor/gopkg.in/AlecAivazis/survey.v1/terminal:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

78
vendor/github.com/AlecAivazis/survey/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,78 @@
# Contributing to Survey
🎉🎉 First off, thanks for the interest in contributing to `survey`! 🎉🎉
The following is a set of guidelines to follow when contributing to this package. These are not hard rules, please use common sense and feel free to propose changes to this document in a pull request.
## Table of Contents
1. [Code of Conduct](#code-of-conduct)
1. [Getting Help](#getting-help)
1. [Filing a Bug Report](#how-to-file-a-bug-report)
1. [Suggesting an API change](#suggesting-an-api-change)
1. [Submitting a Contribution](#submitting-a-contribution)
1. [Writing and Running Tests](#writing-and-running-tests)
## Code of Conduct
This project and its contibutors are expected to uphold the [Go Community Code of Conduct](https://golang.org/conduct). By participating, you are expected to follow these guidelines.
## Getting help
Feel free to [open up an issue](https://github.com/AlecAivazis/survey/issues/new) on GitHub when asking a question so others will be able to find it. Please remember to tag the issue with the `Question` label so the maintainers can get to your question as soon as possible. If the question is urgent, feel free to reach out to `@AlecAivazis` directly in the gophers slack channel.
## How to file a bug report
Bugs are tracked using the Github Issue tracker. When filing a bug, please remember to label the issue as a `Bug` and answer/provide the following:
1. What operating system and terminal are you using?
1. An example that showcases the bug.
1. What did you expect to see?
1. What did you see instead?
## 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.
## Submitting a contribution
In order to maintain stability, most features get fully integrated in more than one PR. This allows for more opportunity to think through each API change without amassing large amounts of tech debt and API changes at once. If your feature can be broken into separate chunks, it will be able to be reviewed much quicker. For example, if the PR that implemented the `Validate` field was submitted in a PR separately from one that included `survey.Required`, it would be able to get merge without having to decide how many different `Validators` we want to provide as part of `survey`'s API.
When submitting a contribution,
* Provide a description of the feature or change
* Reference the ticket addressed by the PR if there is one
* Following community standards, add comments for all exported members so that all necessary information is available on godocs
* Remember to update the project README.md with changes to the high-level API
* Include both positive and negative unit tests (when applicable)
* Contributions with visual ramifications or interaction changes should be accompanied with the appropriate `go-expect` tests. For more information on writing these tests, see [Writing and Running Tests](#writing-and-running-tests)
## Writing and running tests
When submitting features, please add as many units tests as necessary to test both positive and negative cases.
Integration tests for survey uses [go-expect](https://github.com/Netflix/go-expect) to expect a match on stdout and respond on stdin. Since `os.Stdout` in a `go test` process is not a TTY, you need a way to interpret terminal / ANSI escape sequences for things like `CursorLocation`. The stdin/stdout handled by `go-expect` is also multiplexed to a [virtual terminal](https://github.com/hinshun/vt10x).
For example, you can extend the tests for Input by specifying the following test case:
```go
{
"Test Input prompt interaction", // Name of the test.
&Input{ // An implementation of the survey.Prompt interface.
Message: "What is your name?",
},
func(c *expect.Console) { // An expect procedure. You can expect strings / regexps and
c.ExpectString("What is your name?") // write back strings / bytes to its psuedoterminal for survey.
c.SendLine("Johnny Appleseed")
c.ExpectEOF() // Nothing is read from the tty without an expect, and once an
// expectation is met, no further bytes are read. End your
// procedure with `c.ExpectEOF()` to read until survey finishes.
},
"Johnny Appleseed", // The expected result.
}
```
If you want to write your own `go-expect` test from scratch, you'll need to instantiate a virtual terminal,
multiplex it into an `*expect.Console`, and hook up its tty with survey's optional stdio. Please see `go-expect`
[documentation](https://godoc.org/github.com/Netflix/go-expect) for more detail.

78
vendor/github.com/AlecAivazis/survey/Gopkg.lock generated vendored Normal file
View File

@@ -0,0 +1,78 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/Netflix/go-expect"
packages = ["."]
revision = "c93bf25de8e869da25cf26bcd2932b36141f61ae"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
[[projects]]
branch = "master"
name = "github.com/hinshun/vt10x"
packages = ["."]
revision = "1954e646417484a2a687ea344edade2c2b6523c8"
[[projects]]
branch = "master"
name = "github.com/kballard/go-shellquote"
packages = ["."]
revision = "95032a82bc518f77982ea72343cc1ade730072f0"
[[projects]]
name = "github.com/kr/pty"
packages = ["."]
revision = "282ce0e5322c82529687d609ee670fac7c7d917c"
version = "v1.1.1"
[[projects]]
name = "github.com/mattn/go-colorable"
packages = ["."]
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
version = "v0.0.9"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
branch = "master"
name = "github.com/mgutz/ansi"
packages = ["."]
revision = "9520e82c474b0a04dd04f8a40959027271bab992"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
name = "github.com/stretchr/testify"
packages = [
"assert",
"require"
]
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
version = "v1.2.1"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "371508ebad4798adc38a118f858b5c17a65b58594203548f9feb74cb781dd907"
solver-name = "gps-cdcl"
solver-version = 1

54
vendor/github.com/AlecAivazis/survey/Gopkg.toml generated vendored Normal file
View File

@@ -0,0 +1,54 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/Netflix/go-expect"
[[constraint]]
branch = "master"
name = "github.com/hinshun/vt10x"
[[constraint]]
name = "github.com/mattn/go-isatty"
version = "0.0.3"
[[constraint]]
branch = "master"
name = "github.com/mgutz/ansi"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.2.1"
[prune]
go-tests = true
unused-packages = true
[[constraint]]
branch = "master"
name = "github.com/kballard/go-shellquote"

21
vendor/github.com/AlecAivazis/survey/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Alec Aivazis
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

395
vendor/github.com/AlecAivazis/survey/README.md generated vendored Normal file
View File

@@ -0,0 +1,395 @@
# Survey
[![Build Status](https://travis-ci.org/AlecAivazis/survey.svg?branch=feature%2Fpretty)](https://travis-ci.org/AlecAivazis/survey)
[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg)](https://godoc.org/gopkg.in/AlecAivazis/survey.v1)
A library for building interactive prompts.
<img width="550" src="https://thumbs.gfycat.com/VillainousGraciousKouprey-size_restricted.gif"/>
```go
package main
import (
"fmt"
"gopkg.in/AlecAivazis/survey.v1"
)
// the questions to ask
var qs = []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{Message: "What is your name?"},
Validate: survey.Required,
Transform: survey.Title,
},
{
Name: "color",
Prompt: &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
Default: "red",
},
},
{
Name: "age",
Prompt: &survey.Input{Message: "How old are you?"},
},
}
func main() {
// the answers will be written to this struct
answers := struct {
Name string // survey will match the question and field names
FavoriteColor string `survey:"color"` // or you can tag fields to match a specific name
Age int // if the types don't match exactly, survey will try to convert for you
}{}
// perform the questions
err := survey.Ask(qs, &answers)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Printf("%s chose %s.", answers.Name, answers.FavoriteColor)
}
```
## Table of Contents
1. [Examples](#examples)
1. [Prompts](#prompts)
1. [Input](#input)
1. [Multiline](#multiline)
1. [Password](#password)
1. [Confirm](#confirm)
1. [Select](#select)
1. [MultiSelect](#multiselect)
1. [Editor](#editor)
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. [Custom Types](#custom-types)
1. [Customizing Output](#customizing-output)
1. [Versioning](#versioning)
1. [Testing](#testing)
## Examples
Examples can be found in the `examples/` directory. Run them
to see basic behavior:
```bash
go get gopkg.in/AlecAivazis/survey.v1
cd $GOPATH/src/gopkg.in/AlecAivazis/survey.v1
go run examples/simple.go
go run examples/validation.go
```
## Prompts
### Input
<img src="https://thumbs.gfycat.com/LankyBlindAmericanpainthorse-size_restricted.gif" width="400px"/>
```golang
name := ""
prompt := &survey.Input{
Message: "ping",
}
survey.AskOne(prompt, &name, nil)
```
### Multiline
<img src="https://thumbs.gfycat.com/ImperfectShimmeringBeagle-size_restricted.gif" width="400px"/>
```golang
text := ""
prompt := &survey.Multiline{
Message: "ping",
}
survey.AskOne(prompt, &text, nil)
```
### Password
<img src="https://thumbs.gfycat.com/CompassionateSevereHypacrosaurus-size_restricted.gif" width="400px" />
```golang
password := ""
prompt := &survey.Password{
Message: "Please type your password",
}
survey.AskOne(prompt, &password, nil)
```
### Confirm
<img src="https://thumbs.gfycat.com/UnkemptCarefulGermanpinscher-size_restricted.gif" width="400px"/>
```golang
name := false
prompt := &survey.Confirm{
Message: "Do you like pie?",
}
survey.AskOne(prompt, &name, nil)
```
### Select
<img src="https://thumbs.gfycat.com/GrimFilthyAmazonparrot-size_restricted.gif" width="450px"/>
```golang
color := ""
prompt := &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
}
survey.AskOne(prompt, &color, nil)
```
The user can filter for options by typing while the prompt is active. 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:
```golang
prompt := &survey.Select{..., PageSize: 10}
```
### MultiSelect
<img src="https://thumbs.gfycat.com/SharpTameAntelope-size_restricted.gif" width="450px"/>
```golang
days := []string{}
prompt := &survey.MultiSelect{
Message: "What days do you prefer:",
Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
}
survey.AskOne(prompt, &days, nil)
```
The user can filter for options by typing while the prompt is active. 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:
```golang
prompt := &survey.MultiSelect{..., PageSize: 10}
```
### Editor
Launches the user's preferred editor (defined by the $EDITOR environment variable) on a
temporary file. Once the user exits their editor, the contents of the temporary file are read in as
the result. If neither of those are present, notepad (on Windows) or vim (Linux or Mac) is used.
## 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:
```golang
q := &survey.Question{
Prompt: &survey.Input{Message: "Hello world validation"},
Validate: func (val interface{}) error {
// since we are validating an Input, the assertion will always succeed
if str, ok := val.(string) ; !ok || len(str) > 10 {
return errors.New("This response cannot be longer than 10 characters.")
}
}
}
```
### Built-in Validators
`survey` comes prepackaged with a few validators to fit common situations. Currently these
validators include:
| name | valid types | description | notes |
| ------------ | ----------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response |
| MinLength(n) | string | Enforces that a response is at least the given length | |
| MaxLength(n) | string | Enforces that a response is no longer than the given length | |
## Help Text
All of the prompts have a `Help` field which can be defined to provide more information to your users:
<img src="https://thumbs.gfycat.com/CloudyRemorsefulFossa-size_restricted.gif" width="400px" style="margin-top: 8px"/>
```golang
&survey.Input{
Message: "What is your phone number:",
Help: "Phone number should include the area code",
}
```
### Changing the input rune
In some situations, `?` is a perfectly valid response. To handle this, you can change the rune that survey
looks for by setting the `HelpInputRune` variable in `survey/core`:
```golang
import (
"gopkg.in/AlecAivazis/survey.v1"
surveyCore "gopkg.in/AlecAivazis/survey.v1/core"
)
number := ""
prompt := &survey.Input{
Message: "If you have this need, please give me a reasonable message.",
Help: "I couldn't come up with one.",
}
surveyCore.HelpIcon = '^'
survey.AskOne(prompt, &number, nil)
```
## Custom Types
survey will assign prompt answers to your custom types if they implement this interface:
```golang
type settable interface {
WriteAnswer(field string, value interface{}) error
}
```
Here is an example how to use them:
```golang
type MyValue struct {
value string
}
func (my *MyValue) WriteAnswer(name string, value interface{}) error {
my.value = value.(string)
}
myval := MyValue{}
survey.AskOne(
&survey.Input{
Message: "Enter something:",
},
&myval,
nil,
)
```
## Customizing Output
Customizing the icons and various parts of survey can easily be done by setting the following variables
in `survey/core`:
| name | default | description |
| ------------------ | ------- | ------------------------------------------------------------- |
| ErrorIcon | X | Before an error |
| HelpIcon | i | Before help text |
| QuestionIcon | ? | Before the message of a prompt |
| SelectFocusIcon | > | Marks the current focus in `Select` and `MultiSelect` prompts |
| UnmarkedOptionIcon | [ ] | Marks an unselected option in a `MultiSelect` prompt |
| MarkedOptionIcon | [x] | Marks a chosen selection in a `MultiSelect` prompt |
## Versioning
This project tries to maintain semantic GitHub releases as closely as possible and relies on [gopkg.in](http://labix.org/gopkg.in)
to maintain those releases. Importing version 1 of survey would look like:
```golang
package main
import "gopkg.in/AlecAivazis/survey.v1"
```
## Testing
You can test your program's interactive prompts using [go-expect](https://github.com/Netflix/go-expect). The library
can be used to expect a match on stdout and respond on stdin. Since `os.Stdout` in a `go test` process is not a TTY,
if you are manipulating the cursor or using `survey`, you will need a way to interpret terminal / ANSI escape sequences
for things like `CursorLocation`. `vt10x.NewVT10XConsole` will create a `go-expect` console that also multiplexes
stdio to an in-memory [virtual terminal](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()))
}
```

23
vendor/github.com/AlecAivazis/survey/_tasks.yml generated vendored Normal file
View File

@@ -0,0 +1,23 @@
autoplay-tests:
summary: Replaying interactive tests
command: |-
cd tests
set -e
for test in autoplay/*.go; do
echo "==> Running $test"
go run $test
done
install-deps:
summary: Install all of package dependencies
command: |-
go get -t {{.files}}
# for autoplay tests
go get github.com/kr/pty
tests:
summary: Run the test suite
command: go test {{.files}}
variables:
files: '$(go list -v ./... | grep -iEv "tests|examples")'

138
vendor/github.com/AlecAivazis/survey/confirm.go generated vendored Normal file
View File

@@ -0,0 +1,138 @@
package survey
import (
"fmt"
"regexp"
"gopkg.in/AlecAivazis/survey.v1/core"
)
// Confirm is a regular text input that accept yes/no answers. Response type is a bool.
type Confirm struct {
core.Renderer
Message string
Default bool
Help string
}
// data available to the templates when processing
type ConfirmTemplateData struct {
Confirm
Answer string
ShowHelp bool
}
// 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"}}
{{- 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}}
{{- color "white"}}{{if .Default}}(Y/n) {{else}}(y/N) {{end}}{{color "reset"}}
{{- end}}`
// the regex for answers
var (
yesRx = regexp.MustCompile("^(?i:y(?:es)?)$")
noRx = regexp.MustCompile("^(?i:n(?:o)?)$")
)
func yesNo(t bool) string {
if t {
return "Yes"
}
return "No"
}
func (c *Confirm) getBool(showHelp bool) (bool, error) {
cursor := c.NewCursor()
rr := c.NewRuneReader()
rr.SetTermMode()
defer rr.RestoreTermMode()
// start waiting for input
for {
line, err := rr.ReadLine(0)
if err != nil {
return false, err
}
// move back up a line to compensate for the \n echoed from terminal
cursor.PreviousLine(1)
val := string(line)
// get the answer that matches the
var answer bool
switch {
case yesRx.Match([]byte(val)):
answer = true
case noRx.Match([]byte(val)):
answer = false
case val == "":
answer = c.Default
case val == string(core.HelpInputRune) && c.Help != "":
err := c.Render(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *c, ShowHelp: true},
)
if err != nil {
// use the default value and bubble up
return c.Default, err
}
showHelp = true
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 {
return c.Default, err
}
err := c.Render(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *c, ShowHelp: showHelp},
)
if err != nil {
// use the default value and bubble up
return c.Default, err
}
continue
}
return answer, nil
}
// should not get here
return c.Default, nil
}
/*
Prompt prompts the user with a simple text field and expects a reply followed
by a carriage return.
likesPie := false
prompt := &survey.Confirm{ Message: "What is your name?" }
survey.AskOne(prompt, &likesPie, nil)
*/
func (c *Confirm) Prompt() (interface{}, error) {
// render the question template
err := c.Render(
ConfirmQuestionTemplate,
ConfirmTemplateData{Confirm: *c},
)
if err != nil {
return "", err
}
// get input and return
return c.getBool(false)
}
// Cleanup overwrite the line with the finalized formatted version
func (c *Confirm) Cleanup(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},
)
}

194
vendor/github.com/AlecAivazis/survey/editor.go generated vendored Normal file
View File

@@ -0,0 +1,194 @@
package survey
import (
"bytes"
"io/ioutil"
"os"
"os/exec"
"runtime"
shellquote "github.com/kballard/go-shellquote"
"gopkg.in/AlecAivazis/survey.v1/core"
"gopkg.in/AlecAivazis/survey.v1/terminal"
)
/*
Editor launches an instance of the users preferred editor on a temporary file.
The editor to use is determined by reading the $VISUAL or $EDITOR environment
variables. If neither of those are present, notepad (on Windows) or vim
(others) is used.
The launch of the editor is triggered by the enter key. Since the response may
be long, it will not be echoed as Input does, instead, it print <Received>.
Response type is a string.
message := ""
prompt := &survey.Editor{ Message: "What is your commit message?" }
survey.AskOne(prompt, &message, nil)
*/
type Editor struct {
core.Renderer
Message string
Default string
Help string
Editor string
HideDefault bool
AppendDefault bool
}
// data available to the templates when processing
type EditorTemplateData struct {
Editor
Answer string
ShowAnswer bool
ShowHelp bool
}
// 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"}}
{{- 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 .Default (not .HideDefault)}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- color "cyan"}}[Enter to launch editor] {{color "reset"}}
{{- end}}`
var (
bom = []byte{0xef, 0xbb, 0xbf}
editor = "vim"
)
func init() {
if runtime.GOOS == "windows" {
editor = "notepad"
}
if v := os.Getenv("VISUAL"); v != "" {
editor = v
} else if e := os.Getenv("EDITOR"); e != "" {
editor = e
}
}
func (e *Editor) Prompt() (interface{}, error) {
// render the template
err := e.Render(
EditorQuestionTemplate,
EditorTemplateData{Editor: *e},
)
if err != nil {
return "", err
}
// start reading runes from the standard in
rr := e.NewRuneReader()
rr.SetTermMode()
defer rr.RestoreTermMode()
cursor := e.NewCursor()
cursor.Hide()
defer cursor.Show()
for {
r, _, err := rr.ReadRune()
if err != nil {
return "", err
}
if r == '\r' || r == '\n' {
break
}
if r == terminal.KeyInterrupt {
return "", terminal.InterruptErr
}
if r == terminal.KeyEndTransmission {
break
}
if r == core.HelpInputRune && e.Help != "" {
err = e.Render(
EditorQuestionTemplate,
EditorTemplateData{Editor: *e, ShowHelp: true},
)
if err != nil {
return "", err
}
}
continue
}
// prepare the temp file
f, err := ioutil.TempFile("", "survey")
if err != nil {
return "", err
}
defer os.Remove(f.Name())
// write utf8 BOM header
// The reason why we do this is because notepad.exe on Windows determines the
// encoding of an "empty" text file by the locale, for example, GBK in China,
// while golang string only handles utf8 well. However, a text file with utf8
// BOM header is not considered "empty" on Windows, and the encoding will then
// be determined utf8 by notepad.exe, instead of GBK or other encodings.
if _, err := f.Write(bom); err != nil {
return "", err
}
// write default value
if e.Default != "" && e.AppendDefault {
if _, err := f.WriteString(e.Default); err != nil {
return "", err
}
}
// close the fd to prevent the editor unable to save file
if err := f.Close(); err != nil {
return "", err
}
// check is input editor exist
if e.Editor != "" {
editor = e.Editor
}
stdio := e.Stdio()
args, err := shellquote.Split(editor)
if err != nil {
return "", err
}
args = append(args, f.Name())
// open the editor
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdin = stdio.In
cmd.Stdout = stdio.Out
cmd.Stderr = stdio.Err
cursor.Show()
if err := cmd.Run(); err != nil {
return "", err
}
// raw is a BOM-unstripped UTF8 byte slice
raw, err := ioutil.ReadFile(f.Name())
if err != nil {
return "", err
}
// strip BOM header
text := string(bytes.TrimPrefix(raw, bom))
// check length, return default value on empty
if len(text) == 0 && !e.AppendDefault {
return e.Default, nil
}
return text, nil
}
func (e *Editor) Cleanup(val interface{}) error {
return e.Render(
EditorQuestionTemplate,
EditorTemplateData{Editor: *e, Answer: "<Received>", ShowAnswer: true},
)
}

97
vendor/github.com/AlecAivazis/survey/input.go generated vendored Normal file
View File

@@ -0,0 +1,97 @@
package survey
import (
"gopkg.in/AlecAivazis/survey.v1/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)
*/
type Input struct {
core.Renderer
Message string
Default string
Help string
}
// data available to the templates when processing
type InputTemplateData struct {
Input
Answer string
ShowAnswer bool
ShowHelp bool
}
// 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"}}
{{- 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 .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- end}}`
func (i *Input) Prompt() (interface{}, error) {
// render the template
err := i.Render(
InputQuestionTemplate,
InputTemplateData{Input: *i},
)
if err != nil {
return "", err
}
// start reading runes from the standard in
rr := i.NewRuneReader()
rr.SetTermMode()
defer rr.RestoreTermMode()
cursor := i.NewCursor()
line := []rune{}
// get the next line
for {
line, err = rr.ReadLine(0)
if err != nil {
return string(line), err
}
// 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 != "" {
err = i.Render(
InputQuestionTemplate,
InputTemplateData{Input: *i, ShowHelp: true},
)
if err != nil {
return "", err
}
continue
}
break
}
// if the line is empty
if line == nil || len(line) == 0 {
// use the default value
return i.Default, err
}
// we're done
return string(line), err
}
func (i *Input) Cleanup(val interface{}) error {
return i.Render(
InputQuestionTemplate,
InputTemplateData{Input: *i, Answer: val.(string), ShowAnswer: true},
)
}

103
vendor/github.com/AlecAivazis/survey/multiline.go generated vendored Normal file
View File

@@ -0,0 +1,103 @@
package survey
import (
"fmt"
"strings"
"gopkg.in/AlecAivazis/survey.v1/core"
"gopkg.in/AlecAivazis/survey.v1/terminal"
)
type Multiline struct {
core.Renderer
Message string
Default string
Help string
}
// data available to the templates when processing
type MultilineTemplateData struct {
Multiline
Answer string
ShowAnswer bool
ShowHelp bool
}
// 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"}}
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
{{- if .ShowAnswer}}
{{- "\n"}}{{color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}}
{{- else }}
{{- if .Default}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}}
{{- color "cyan"}}[Enter 2 empty lines to finish]{{color "reset"}}
{{- end}}`
func (i *Multiline) Prompt() (interface{}, error) {
// render the template
err := i.Render(
MultilineQuestionTemplate,
MultilineTemplateData{Multiline: *i},
)
if err != nil {
return "", err
}
fmt.Println()
// start reading runes from the standard in
rr := i.NewRuneReader()
rr.SetTermMode()
defer rr.RestoreTermMode()
cursor := i.NewCursor()
multiline := make([]string, 0)
emptyOnce := false
// get the next line
for {
line := []rune{}
line, err = rr.ReadLine(0)
if err != nil {
return string(line), err
}
if string(line) == "" {
if emptyOnce {
numLines := len(multiline) + 2
cursor.PreviousLine(numLines)
for j := 0; j < numLines; j++ {
terminal.EraseLine(i.Stdio().Out, terminal.ERASE_LINE_ALL)
cursor.NextLine(1)
}
cursor.PreviousLine(numLines)
break
}
emptyOnce = true
} else {
emptyOnce = false
}
multiline = append(multiline, string(line))
}
val := strings.Join(multiline, "\n")
val = strings.TrimSpace(val)
// if the line is empty
if len(val) == 0 {
// use the default value
return i.Default, err
}
// we're done
return val, err
}
func (i *Multiline) Cleanup(val interface{}) error {
return i.Render(
MultilineQuestionTemplate,
MultilineTemplateData{Multiline: *i, Answer: val.(string), ShowAnswer: true},
)
}

251
vendor/github.com/AlecAivazis/survey/multiselect.go generated vendored Normal file
View File

@@ -0,0 +1,251 @@
package survey
import (
"errors"
"strings"
"gopkg.in/AlecAivazis/survey.v1/core"
"gopkg.in/AlecAivazis/survey.v1/terminal"
)
/*
MultiSelect is a prompt that presents a list of various options to the user
for them to select using the arrow keys and enter. Response type is a slice of strings.
days := []string{}
prompt := &survey.MultiSelect{
Message: "What days do you prefer:",
Options: []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"},
}
survey.AskOne(prompt, &days, nil)
*/
type MultiSelect struct {
core.Renderer
Message string
Options []string
Default []string
Help string
PageSize int
VimMode bool
FilterMessage string
filter string
selectedIndex int
checked map[string]bool
showingHelp bool
}
// data available to the templates when processing
type MultiSelectTemplateData struct {
MultiSelect
Answer string
ShowAnswer bool
Checked map[string]bool
SelectedIndex int
ShowHelp bool
PageEntries []string
}
var MultiSelectQuestionTemplate = `
{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
{{- else }}
{{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ HelpInputRune }} 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}}
{{- color "reset"}}
{{- " "}}{{$option}}{{"\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()
oldFilter := m.filter
if key == terminal.KeyArrowUp || (m.VimMode && key == 'k') {
// if we are at the top of the list
if m.selectedIndex == 0 {
// go to the bottom
m.selectedIndex = len(options) - 1
} else {
// decrement the selected index
m.selectedIndex--
}
} else if key == terminal.KeyArrowDown || (m.VimMode && key == 'j') {
// if we are at the bottom of the list
if m.selectedIndex == len(options)-1 {
// start at the top
m.selectedIndex = 0
} else {
// increment the selected index
m.selectedIndex++
}
// if the user pressed down and there is room to move
} else if key == terminal.KeySpace {
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
} else {
// otherwise just invert the current value
m.checked[options[m.selectedIndex]] = !old
}
m.filter = ""
}
// only show the help message if we have one to show
} else if key == core.HelpInputRune && m.Help != "" {
m.showingHelp = true
} else if key == terminal.KeyEscape {
m.VimMode = !m.VimMode
} else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine {
m.filter = ""
} else if key == terminal.KeyDelete || key == terminal.KeyBackspace {
if m.filter != "" {
m.filter = m.filter[0 : len(m.filter)-1]
}
} else if key >= terminal.KeySpace {
m.filter += string(key)
m.VimMode = false
}
m.FilterMessage = ""
if m.filter != "" {
m.FilterMessage = " " + m.filter
}
if oldFilter != m.filter {
// filter changed
options = m.filterOptions()
if len(options) > 0 && len(options) <= m.selectedIndex {
m.selectedIndex = len(options) - 1
}
}
// paginate the options
// 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)
// render the options
m.Render(
MultiSelectQuestionTemplate,
MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
ShowHelp: m.showingHelp,
PageEntries: opts,
},
)
// if we are not pressing ent
return line, 0, true
}
func (m *MultiSelect) filterOptions() []string {
filter := strings.ToLower(m.filter)
if filter == "" {
return m.Options
}
answer := []string{}
for _, o := range m.Options {
if strings.Contains(strings.ToLower(o), filter) {
answer = append(answer, o)
}
}
return answer
}
func (m *MultiSelect) Prompt() (interface{}, error) {
// compute the default state
m.checked = make(map[string]bool)
// if there is a default
if len(m.Default) > 0 {
for _, dflt := range m.Default {
for _, opt := range m.Options {
// if the option correponds to the default
if opt == dflt {
// we found our initial value
m.checked[opt] = true
// stop looking
break
}
}
}
}
// if there are no options to render
if len(m.Options) == 0 {
// we failed
return "", errors.New("please provide options to select from")
}
// paginate the options
opts, idx := paginate(m.PageSize, m.Options, m.selectedIndex)
cursor := m.NewCursor()
cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done
// ask the question
err := m.Render(
MultiSelectQuestionTemplate,
MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
Checked: m.checked,
PageEntries: opts,
},
)
if err != nil {
return "", err
}
rr := m.NewRuneReader()
rr.SetTermMode()
defer rr.RestoreTermMode()
// start waiting for input
for {
r, _, _ := rr.ReadRune()
if r == '\r' || r == '\n' {
break
}
if r == terminal.KeyInterrupt {
return "", terminal.InterruptErr
}
if r == terminal.KeyEndTransmission {
break
}
m.OnChange(nil, 0, r)
}
m.filter = ""
m.FilterMessage = ""
answers := []string{}
for _, option := range m.Options {
if val, ok := m.checked[option]; ok && val {
answers = append(answers, option)
}
}
return answers, nil
}
// Cleanup removes the options section, and renders the ask like a normal question.
func (m *MultiSelect) Cleanup(val interface{}) error {
// execute the output summary template with the answer
return m.Render(
MultiSelectQuestionTemplate,
MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: m.selectedIndex,
Checked: m.checked,
Answer: strings.Join(val.([]string), ", "),
ShowAnswer: true,
},
)
}

86
vendor/github.com/AlecAivazis/survey/password.go generated vendored Normal file
View File

@@ -0,0 +1,86 @@
package survey
import (
"fmt"
"gopkg.in/AlecAivazis/survey.v1/core"
"gopkg.in/AlecAivazis/survey.v1/terminal"
)
/*
Password is like a normal Input but the text shows up as *'s and there is no default. Response
type is a string.
password := ""
prompt := &survey.Password{ Message: "Please type your password" }
survey.AskOne(prompt, &password, nil)
*/
type Password struct {
core.Renderer
Message string
Help string
}
type PasswordTemplateData struct {
Password
ShowHelp bool
}
// Templates 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"}}
{{- color "default+hb"}}{{ .Message }} {{color "reset"}}
{{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ HelpInputRune }} for help]{{color "reset"}} {{end}}`
func (p *Password) Prompt() (line interface{}, err error) {
// render the question template
out, err := core.RunTemplate(
PasswordQuestionTemplate,
PasswordTemplateData{Password: *p},
)
fmt.Fprint(terminal.NewAnsiStdout(p.Stdio().Out), out)
if err != nil {
return "", err
}
rr := p.NewRuneReader()
rr.SetTermMode()
defer rr.RestoreTermMode()
// no help msg? Just return any response
if p.Help == "" {
line, err := rr.ReadLine('*')
return string(line), err
}
cursor := p.NewCursor()
// process answers looking for help prompt answer
for {
line, err := rr.ReadLine('*')
if err != nil {
return string(line), err
}
if string(line) == string(core.HelpInputRune) {
// 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},
)
if err != nil {
return "", err
}
continue
}
return string(line), err
}
}
// Cleanup hides the string with a fixed number of characters.
func (prompt *Password) Cleanup(val interface{}) error {
return nil
}

271
vendor/github.com/AlecAivazis/survey/select.go generated vendored Normal file
View File

@@ -0,0 +1,271 @@
package survey
import (
"errors"
"strings"
"gopkg.in/AlecAivazis/survey.v1/core"
"gopkg.in/AlecAivazis/survey.v1/terminal"
)
/*
Select is a prompt that presents a list of various options to the user
for them to select using the arrow keys and enter. Response type is a string.
color := ""
prompt := &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
}
survey.AskOne(prompt, &color, nil)
*/
type Select struct {
core.Renderer
Message string
Options []string
Default string
Help string
PageSize int
VimMode bool
FilterMessage string
filter string
selectedIndex int
useDefault bool
showingHelp bool
}
// the data available to the templates when processing
type SelectTemplateData struct {
Select
PageEntries []string
SelectedIndex int
Answer string
ShowAnswer bool
ShowHelp bool
}
var SelectQuestionTemplate = `
{{- if .ShowHelp }}{{- color "cyan"}}{{ HelpIcon }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color "green+hb"}}{{ QuestionIcon }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
{{- else}}
{{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ HelpInputRune }} 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}}
{{- color "reset"}}{{"\n"}}
{{- end}}
{{- end}}`
// OnChange is called on every keypress.
func (s *Select) OnChange(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
options := s.filterOptions()
oldFilter := s.filter
// if the user pressed the enter key
if key == terminal.KeyEnter {
if s.selectedIndex < len(options) {
return []rune(options[s.selectedIndex]), 0, true
}
// if the user pressed the up arrow or 'k' to emulate vim
} else if key == terminal.KeyArrowUp || (s.VimMode && key == 'k') {
s.useDefault = false
// if we are at the top of the list
if s.selectedIndex == 0 {
// start from the button
s.selectedIndex = len(options) - 1
} else {
// otherwise we are not at the top of the list so decrement the selected index
s.selectedIndex--
}
// if the user pressed down or 'j' to emulate vim
} else if key == terminal.KeyArrowDown || (s.VimMode && key == 'j') {
s.useDefault = false
// if we are at the bottom of the list
if s.selectedIndex == len(options)-1 {
// start from the top
s.selectedIndex = 0
} else {
// increment the selected index
s.selectedIndex++
}
// only show the help message if we have one
} else if key == core.HelpInputRune && s.Help != "" {
s.showingHelp = true
// if the user wants to toggle vim mode on/off
} else if key == terminal.KeyEscape {
s.VimMode = !s.VimMode
// if the user hits any of the keys that clear the filter
} else if key == terminal.KeyDeleteWord || key == terminal.KeyDeleteLine {
s.filter = ""
// if the user is deleting a character in the filter
} else if key == terminal.KeyDelete || key == terminal.KeyBackspace {
// if there is content in the filter to delete
if s.filter != "" {
// subtract a line from the current filter
s.filter = s.filter[0 : len(s.filter)-1]
// we removed the last value in the filter
}
} else if key >= terminal.KeySpace {
s.filter += string(key)
// make sure vim mode is disabled
s.VimMode = false
// make sure that we use the current value in the filtered list
s.useDefault = false
}
s.FilterMessage = ""
if s.filter != "" {
s.FilterMessage = " " + s.filter
}
if oldFilter != s.filter {
// filter changed
options = s.filterOptions()
if len(options) > 0 && len(options) <= s.selectedIndex {
s.selectedIndex = len(options) - 1
}
}
// figure out the options and index to render
// 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)
// render the options
s.Render(
SelectQuestionTemplate,
SelectTemplateData{
Select: *s,
SelectedIndex: idx,
ShowHelp: s.showingHelp,
PageEntries: opts,
},
)
// if we are not pressing ent
if len(options) <= s.selectedIndex {
return []rune{}, 0, false
}
return []rune(options[s.selectedIndex]), 0, true
}
func (s *Select) filterOptions() []string {
filter := strings.ToLower(s.filter)
if filter == "" {
return s.Options
}
answer := []string{}
for _, o := range s.Options {
if strings.Contains(strings.ToLower(o), filter) {
answer = append(answer, o)
}
}
return answer
}
func (s *Select) Prompt() (interface{}, error) {
// if there are no options to render
if len(s.Options) == 0 {
// we failed
return "", errors.New("please provide options to select from")
}
// start off with the first option selected
sel := 0
// if there is a default
if s.Default != "" {
// find the choice
for i, opt := range s.Options {
// if the option correponds to the default
if opt == s.Default {
// we found our initial value
sel = i
// stop looking
break
}
}
}
// save the selected index
s.selectedIndex = sel
// figure out the options and index to render
opts, idx := paginate(s.PageSize, s.Options, sel)
// ask the question
err := s.Render(
SelectQuestionTemplate,
SelectTemplateData{
Select: *s,
PageEntries: opts,
SelectedIndex: idx,
},
)
if err != nil {
return "", err
}
// by default, use the default value
s.useDefault = true
rr := s.NewRuneReader()
rr.SetTermMode()
defer rr.RestoreTermMode()
cursor := s.NewCursor()
cursor.Hide() // hide the cursor
defer cursor.Show() // show the cursor when we're done
// start waiting for input
for {
r, _, err := rr.ReadRune()
if err != nil {
return "", err
}
if r == '\r' || r == '\n' {
break
}
if r == terminal.KeyInterrupt {
return "", terminal.InterruptErr
}
if r == terminal.KeyEndTransmission {
break
}
s.OnChange(nil, 0, r)
}
options := s.filterOptions()
s.filter = ""
s.FilterMessage = ""
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
} else if len(options) > 0 {
// there is no default value so use the first
val = options[0]
}
// otherwise the selected index points to the value
} else if s.selectedIndex < len(options) {
// the
val = options[s.selectedIndex]
}
return val, err
}
func (s *Select) Cleanup(val interface{}) error {
return s.Render(
SelectQuestionTemplate,
SelectTemplateData{
Select: *s,
Answer: val.(string),
ShowAnswer: true,
},
)
}

246
vendor/github.com/AlecAivazis/survey/survey.go generated vendored Normal file
View File

@@ -0,0 +1,246 @@
package survey
import (
"errors"
"io"
"os"
"gopkg.in/AlecAivazis/survey.v1/core"
"gopkg.in/AlecAivazis/survey.v1/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,
},
}
// Validator is a function passed to a Question after a user has provided a response.
// If the function returns an error, then the user will be prompted again for another
// response.
type Validator func(ans interface{}) error
// Transformer is a function passed to a Question after a user has provided a response.
// The function can be used to implement a custom logic that will result to return
// a different representation of the given answer.
//
// Look `TransformString`, `ToLower` `Title` and `ComposeTransformers` for more.
type Transformer func(ans interface{}) (newAns interface{})
// Question is the core data structure for a survey questionnaire.
type Question struct {
Name string
Prompt Prompt
Validate Validator
Transform Transformer
}
// 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
}
// AskOpt allows setting optional ask options.
type AskOpt func(options *AskOptions) error
// AskOptions provides additional options on ask.
type AskOptions struct {
Stdio terminal.Stdio
}
// WithStdio specifies the standard input, output and error files survey
// interacts with. By default, these are os.Stdin, os.Stdout, and os.Stderr.
func WithStdio(in terminal.FileReader, out terminal.FileWriter, err io.Writer) AskOpt {
return func(options *AskOptions) error {
options.Stdio.In = in
options.Stdio.Out = out
options.Stdio.Err = err
return nil
}
}
type wantsStdio interface {
WithStdio(terminal.Stdio)
}
/*
AskOne performs the prompt for a single prompt and asks for validation if required.
Response types should be something that can be casted from the response type designated
in the documentation. For example:
name := ""
prompt := &survey.Input{
Message: "name",
}
survey.AskOne(prompt, &name, nil)
*/
func AskOne(p Prompt, response interface{}, v Validator, opts ...AskOpt) error {
err := Ask([]*Question{{Prompt: p, Validate: v}}, response, opts...)
if err != nil {
return err
}
return nil
}
/*
Ask performs the prompt loop, asking for validation when appropriate. The response
type can be one of two options. If a struct is passed, the answer will be written to
the field whose name matches the Name field on the corresponding question. Field types
should be something that can be casted from the response type designated in the
documentation. Note, a survey tag can also be used to identify a Otherwise, a
map[string]interface{} can be passed, responses will be written to the key with the
matching name. For example:
qs := []*survey.Question{
{
Name: "name",
Prompt: &survey.Input{Message: "What is your name?"},
Validate: survey.Required,
Transform: survey.Title,
},
}
answers := struct{ Name string }{}
err := survey.Ask(qs, &answers)
*/
func Ask(qs []*Question, response interface{}, opts ...AskOpt) error {
options := DefaultAskOptions
for _, opt := range opts {
if err := opt(&options); err != nil {
return err
}
}
// if we weren't passed a place to record the answers
if response == nil {
// we can't go any further
return errors.New("cannot call Ask() with a nil reference to record the answers")
}
// go over every question
for _, q := range qs {
// If Prompt implements controllable stdio, pass in specified stdio.
if p, ok := q.Prompt.(wantsStdio); ok {
p.WithStdio(options.Stdio)
}
// grab the user input and save it
ans, err := q.Prompt.Prompt()
// if there was a problem
if err != nil {
return err
}
// if there is a validate handler for this question
if q.Validate != nil {
// wait for a valid response
for invalid := q.Validate(ans); invalid != nil; invalid = q.Validate(ans) {
err := q.Prompt.Error(invalid)
// if there was a problem
if err != nil {
return err
}
// ask for more input
ans, err = q.Prompt.Prompt()
// if there was a problem
if err != nil {
return err
}
}
}
if q.Transform != nil {
// check if we have a transformer available, if so
// then try to acquire the new representation of the
// answer, if the resulting answer is not nil.
if newAns := q.Transform(ans); newAns != nil {
ans = newAns
}
}
// tell the prompt to cleanup with the validated value
q.Prompt.Cleanup(ans)
// if something went wrong
if err != nil {
// stop listening
return err
}
// add it to the map
err = core.WriteAnswer(response, q.Name, ans)
// if something went wrong
if err != nil {
return err
}
}
// return the response
return nil
}
// 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
}
var start, end, cursor int
if len(choices) < pageSize {
// if we dont have enough options to fill a page
start = 0
end = len(choices)
cursor = sel
} else if sel < pageSize/2 {
// if we are in the first half page
start = 0
end = pageSize
cursor = sel
} else if len(choices)-sel-1 < pageSize/2 {
// if we are in the last half page
start = len(choices) - pageSize
end = len(choices)
cursor = sel - start
} else {
// somewhere in the middle
above := pageSize / 2
below := pageSize - above
cursor = pageSize / 2
start = sel - above
end = sel + below
}
// return the subset we care about and the index
return choices[start:end], cursor
}

76
vendor/github.com/AlecAivazis/survey/transform.go generated vendored Normal file
View File

@@ -0,0 +1,76 @@
package survey
import (
"reflect"
"strings"
)
// TransformString returns a `Transformer` based on the "f"
// function which accepts a string representation of the answer
// and returns a new one, transformed, answer.
// Take for example the functions inside the std `strings` package,
// they can be converted to a compatible `Transformer` by using this function,
// i.e: `TransformString(strings.Title)`, `TransformString(strings.ToUpper)`.
//
// Note that `TransformString` is just a helper, `Transformer` can be used
// to transform any type of answer.
func TransformString(f func(s string) string) Transformer {
return func(ans interface{}) interface{} {
// if the answer value passed in is the zero value of the appropriate type
if isZero(reflect.ValueOf(ans)) {
// skip this `Transformer` by returning a nil value.
// The original answer will be not affected,
// see survey.go#L125.
return nil
}
// "ans" is never nil here, so we don't have to check that
// see survey.go#L97 for more.
// Make sure that the the answer's value was a typeof string.
s, ok := ans.(string)
if !ok {
return nil
}
return f(s)
}
}
// ToLower is a `Transformer`.
// It receives an answer value
// and returns a copy of the "ans"
// with all Unicode letters mapped to their lower case.
//
// Note that if "ans" is not a string then it will
// return a nil value, meaning that the above answer
// will not be affected by this call at all.
func ToLower(ans interface{}) interface{} {
transformer := TransformString(strings.ToLower)
return transformer(ans)
}
// Title is a `Transformer`.
// It receives an answer value
// and returns a copy of the "ans"
// with all Unicode letters that begin words
// mapped to their title case.
//
// Note that if "ans" is not a string then it will
// return a nil value, meaning that the above answer
// will not be affected by this call at all.
func Title(ans interface{}) interface{} {
transformer := TransformString(strings.Title)
return transformer(ans)
}
// ComposeTransformers is a variadic function used to create one transformer from many.
func ComposeTransformers(transformers ...Transformer) Transformer {
// return a transformer that calls each one sequentially
return func(ans interface{}) interface{} {
// execute each transformer
for _, t := range transformers {
ans = t(ans)
}
return ans
}
}

87
vendor/github.com/AlecAivazis/survey/validate.go generated vendored Normal file
View File

@@ -0,0 +1,87 @@
package survey
import (
"errors"
"fmt"
"reflect"
)
// Required does not allow an empty value
func Required(val interface{}) error {
// the reflect value of the result
value := reflect.ValueOf(val)
// if the value passed in is the zero value of the appropriate type
if isZero(value) && value.Kind() != reflect.Bool {
return errors.New("Value is required")
}
return nil
}
// MaxLength requires that the string is no longer than the specified value
func MaxLength(length int) Validator {
// return a validator that checks the length of the string
return func(val interface{}) error {
if str, ok := val.(string); ok {
// if the string is longer than the given value
if len([]rune(str)) > length {
// yell loudly
return fmt.Errorf("value is too long. Max length is %v", length)
}
} else {
// otherwise we cannot convert the value into a string and cannot enforce length
return fmt.Errorf("cannot enforce length on response of type %v", reflect.TypeOf(val).Name())
}
// the input is fine
return nil
}
}
// MinLength requires that the string is longer or equal in length to the specified value
func MinLength(length int) Validator {
// return a validator that checks the length of the string
return func(val interface{}) error {
if str, ok := val.(string); ok {
// if the string is shorter than the given value
if len([]rune(str)) < length {
// yell loudly
return fmt.Errorf("value is too short. Min length is %v", length)
}
} else {
// otherwise we cannot convert the value into a string and cannot enforce length
return fmt.Errorf("cannot enforce length on response of type %v", reflect.TypeOf(val).Name())
}
// the input is fine
return nil
}
}
// ComposeValidators is a variadic function used to create one validator from many.
func ComposeValidators(validators ...Validator) Validator {
// return a validator that calls each one sequentially
return func(val interface{}) error {
// execute each validator
for _, validator := range validators {
// if the answer's value is not valid
if err := validator(val); err != nil {
// return the error
return err
}
}
// we passed all validators, the answer is valid
return nil
}
}
// isZero returns true if the passed value is the zero object
func isZero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Slice, reflect.Map:
return v.Len() == 0
}
// compare the types directly with more general coverage
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}