diff options
Diffstat (limited to 'vendor/github.com/manifoldco/promptui')
22 files changed, 2301 insertions, 0 deletions
diff --git a/vendor/github.com/manifoldco/promptui/.gitignore b/vendor/github.com/manifoldco/promptui/.gitignore new file mode 100644 index 000000000..8ee1778b5 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/.gitignore @@ -0,0 +1,3 @@ +vendor +all-cover.txt +bin/ diff --git a/vendor/github.com/manifoldco/promptui/.golangci.yml b/vendor/github.com/manifoldco/promptui/.golangci.yml new file mode 100644 index 000000000..e232bfbe5 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/.golangci.yml @@ -0,0 +1,26 @@ +run: + deadline: 5m + +issues: + # Disable maximums so we see all issues + max-per-linter: 0 + max-same-issues: 0 + + # golangci-lint ignores missing docstrings by default. That's no good! + exclude-use-default: false + +linters: + disable-all: true + enable: + - misspell + - golint + - goimports + - ineffassign + - deadcode + - gofmt + - govet + - structcheck + - unconvert + - megacheck + - typecheck + - varcheck diff --git a/vendor/github.com/manifoldco/promptui/.travis.yml b/vendor/github.com/manifoldco/promptui/.travis.yml new file mode 100644 index 000000000..01c16c440 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/.travis.yml @@ -0,0 +1,14 @@ +dist: bionic +language: go + +go: + - "1.12.x" + - "1.13.x" + +branches: + only: + - master + +after_success: + # only report coverage for go-version 1.11 + - if [[ $TRAVIS_GO_VERSION =~ ^1\.11 ]] ; then bash <(curl -s https://codecov.io/bash) -f all-cover.txt; fi diff --git a/vendor/github.com/manifoldco/promptui/CHANGELOG.md b/vendor/github.com/manifoldco/promptui/CHANGELOG.md new file mode 100644 index 000000000..563e9d00a --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/CHANGELOG.md @@ -0,0 +1,123 @@ +# CHANGELOG + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +## Unreleased + +## [0.8.0] - 2020-09-28 + +### Added + +- Support ctrl-h for backspace +- Allow hiding entered data after submit +- Allow masking input with an empty rune to hide input length + +### Fixed + +- Fix echo of cursor after input is finished +- Better support for keycodes on Windows + + +## [0.7.0] - 2020-01-11 + +### Added + +- Add support for configurable Stdin/Stdout on Prompt +- Add support for setting initial cursor position +- Switch to golangci-lint for linting + +### Removed + +- Removed support for Go 1.11 + +### Fixed + +- Reduce tool-based deps, hopefully fixing any install issues + +## [0.6.0] - 2019-11-29 + +### Added + +- Support configurable stdin + +### Fixed + +- Correct the dep on go-i18n + +## [0.5.0] - 2019-11-29 + +### Added + +- Now building and testing on go 1.11, go 1.12, and go 1.13 + +### Removed + +- Removed support for Go versions that don't include modules. + +## [0.4.0] - 2019-02-19 + +### Added + +- The text displayed when an item was successfully selected can be hidden + +## [0.3.2] - 2018-11-26 + +### Added + +- Support Go modules + +### Fixed + +- Fix typos in PromptTemplates documentation + +## [0.3.1] - 2018-07-26 + +### Added + +- Improved documentation for GoDoc +- Navigation keys information for Windows + +### Fixed + +- `success` template was not properly displayed after a successful prompt. + +## [0.3.0] - 2018-05-22 + +### Added + +- Background colors codes and template helpers +- `AllowEdit` for prompt to prevent deletion of the default value by any key +- Added `StartInSearchMode` to allow starting the prompt in search mode + +### Fixed + +- `<Enter>` key press on Windows +- `juju/ansiterm` dependency +- `chzyer/readline#136` new api with ReadCloser +- Deleting UTF-8 characters sequence + +## [0.2.1] - 2017-11-30 + +### Fixed + +- `SelectWithAdd` panicking on `.Run` due to lack of keys setup +- Backspace key on Windows + +## [0.2.0] - 2017-11-16 + +### Added + +- `Select` items can now be searched + +## [0.1.0] - 2017-11-02 + +### Added + +- extract `promptui` from [torus](https://github.com/manifoldco/torus-cli) as a + standalone lib. +- `promptui.Prompt` provides a single input line to capture user information. +- `promptui.Select` provides a list of options to choose from. Users can + navigate through the list either one item at time or by pagination diff --git a/vendor/github.com/manifoldco/promptui/CODE_OF_CONDUCT.md b/vendor/github.com/manifoldco/promptui/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..cc58cce02 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, +body size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual +identity and orientation. + +## Our Standards + +Examples of behaviour that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behaviour by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behaviour and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behaviour. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviours that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an +appointed representative at an online or offline event. Representation of a +project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at +[hello@manifold.co](mailto:hello@manifold.co). All complaints will be reviewed +and investigated and will result in a response that is deemed necessary and +appropriate to the circumstances. The project team is obligated to maintain +confidentiality with regard to the reporter of an incident. Further details of +specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the Contributor Covenant, version 1.4, +available at +[http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4). diff --git a/vendor/github.com/manifoldco/promptui/LICENSE.md b/vendor/github.com/manifoldco/promptui/LICENSE.md new file mode 100644 index 000000000..3ae687b2c --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/LICENSE.md @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2017, Arigato Machine Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/manifoldco/promptui/Makefile b/vendor/github.com/manifoldco/promptui/Makefile new file mode 100644 index 000000000..078a5613a --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/Makefile @@ -0,0 +1,49 @@ +export GO111MODULE := on +export PATH := ./bin:$(PATH) + +ci: bootstrap lint cover +.PHONY: ci + +################################################# +# Bootstrapping for base golang package and tool deps +################################################# + +bootstrap: + curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s v1.21.0 +.PHONY: bootstrap + +mod-update: + go get -u -m + go mod tidy + +mod-tidy: + go mod tidy + +.PHONY: $(CMD_PKGS) +.PHONY: mod-update mod-tidy + +################################################# +# Test and linting +################################################# +# Run all the linters +lint: + bin/golangci-lint run ./... +.PHONY: lint + +test: + CGO_ENABLED=0 go test $$(go list ./... | grep -v generated) +.PHONY: test + +COVER_TEST_PKGS:=$(shell find . -type f -name '*_test.go' | rev | cut -d "/" -f 2- | rev | grep -v generated | sort -u) +$(COVER_TEST_PKGS:=-cover): %-cover: all-cover.txt + @CGO_ENABLED=0 go test -v -coverprofile=$@.out -covermode=atomic ./$* + @if [ -f $@.out ]; then \ + grep -v "mode: atomic" < $@.out >> all-cover.txt; \ + rm $@.out; \ + fi + +all-cover.txt: + echo "mode: atomic" > all-cover.txt + +cover: all-cover.txt $(COVER_TEST_PKGS:=-cover) +.PHONY: cover all-cover.txt diff --git a/vendor/github.com/manifoldco/promptui/README.md b/vendor/github.com/manifoldco/promptui/README.md new file mode 100644 index 000000000..4fd4dc6d4 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/README.md @@ -0,0 +1,107 @@ +# promptui + +Interactive prompt for command-line applications. + +We built Promptui because we wanted to make it easy and fun to explore cloud +services with [manifold cli](https://github.com/manifoldco/manifold-cli). + +[Code of Conduct](./CODE_OF_CONDUCT.md) | +[Contribution Guidelines](./.github/CONTRIBUTING.md) + +[![GitHub release](https://img.shields.io/github/tag/manifoldco/promptui.svg?label=latest)](https://github.com/manifoldco/promptui/releases) +[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/manifoldco/promptui) +[![Travis](https://img.shields.io/travis/manifoldco/promptui/master.svg)](https://travis-ci.org/manifoldco/promptui) +[![Go Report Card](https://goreportcard.com/badge/github.com/manifoldco/promptui)](https://goreportcard.com/report/github.com/manifoldco/promptui) +[![License](https://img.shields.io/badge/license-BSD-blue.svg)](./LICENSE.md) + +## Overview + +![promptui](https://media.giphy.com/media/xUNda0Ngb5qsogLsBi/giphy.gif) + +Promptui is a library providing a simple interface to create command-line +prompts for go. It can be easily integrated into +[spf13/cobra](https://github.com/spf13/cobra), +[urfave/cli](https://github.com/urfave/cli) or any cli go application. + +Promptui has two main input modes: + +- `Prompt` provides a single line for user input. Prompt supports + optional live validation, confirmation and masking the input. + +- `Select` provides a list of options to choose from. Select supports + pagination, search, detailed view and custom templates. + +For a full list of options check [GoDoc](https://godoc.org/github.com/manifoldco/promptui). + +## Basic Usage + +### Prompt + +```go +package main + +import ( + "errors" + "fmt" + "strconv" + + "github.com/manifoldco/promptui" +) + +func main() { + validate := func(input string) error { + _, err := strconv.ParseFloat(input, 64) + if err != nil { + return errors.New("Invalid number") + } + return nil + } + + prompt := promptui.Prompt{ + Label: "Number", + Validate: validate, + } + + result, err := prompt.Run() + + if err != nil { + fmt.Printf("Prompt failed %v\n", err) + return + } + + fmt.Printf("You choose %q\n", result) +} +``` + +### Select + +```go +package main + +import ( + "fmt" + + "github.com/manifoldco/promptui" +) + +func main() { + prompt := promptui.Select{ + Label: "Select Day", + Items: []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", + "Saturday", "Sunday"}, + } + + _, result, err := prompt.Run() + + if err != nil { + fmt.Printf("Prompt failed %v\n", err) + return + } + + fmt.Printf("You choose %q\n", result) +} +``` + +### More Examples + +See full list of [examples](https://github.com/manifoldco/promptui/tree/master/_examples) diff --git a/vendor/github.com/manifoldco/promptui/codes.go b/vendor/github.com/manifoldco/promptui/codes.go new file mode 100644 index 000000000..8138c40d6 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/codes.go @@ -0,0 +1,120 @@ +package promptui + +import ( + "fmt" + "strconv" + "strings" + "text/template" +) + +const esc = "\033[" + +type attribute int + +// The possible state of text inside the application, either Bold, faint, italic or underline. +// +// These constants are called through the use of the Styler function. +const ( + reset attribute = iota + + FGBold + FGFaint + FGItalic + FGUnderline +) + +// The possible colors of text inside the application. +// +// These constants are called through the use of the Styler function. +const ( + FGBlack attribute = iota + 30 + FGRed + FGGreen + FGYellow + FGBlue + FGMagenta + FGCyan + FGWhite +) + +// The possible background colors of text inside the application. +// +// These constants are called through the use of the Styler function. +const ( + BGBlack attribute = iota + 40 + BGRed + BGGreen + BGYellow + BGBlue + BGMagenta + BGCyan + BGWhite +) + +// ResetCode is the character code used to reset the terminal formatting +var ResetCode = fmt.Sprintf("%s%dm", esc, reset) + +const ( + hideCursor = esc + "?25l" + showCursor = esc + "?25h" + clearLine = esc + "2K" +) + +// FuncMap defines template helpers for the output. It can be extended as a regular map. +// +// The functions inside the map link the state, color and background colors strings detected in templates to a Styler +// function that applies the given style using the corresponding constant. +var FuncMap = template.FuncMap{ + "black": Styler(FGBlack), + "red": Styler(FGRed), + "green": Styler(FGGreen), + "yellow": Styler(FGYellow), + "blue": Styler(FGBlue), + "magenta": Styler(FGMagenta), + "cyan": Styler(FGCyan), + "white": Styler(FGWhite), + "bgBlack": Styler(BGBlack), + "bgRed": Styler(BGRed), + "bgGreen": Styler(BGGreen), + "bgYellow": Styler(BGYellow), + "bgBlue": Styler(BGBlue), + "bgMagenta": Styler(BGMagenta), + "bgCyan": Styler(BGCyan), + "bgWhite": Styler(BGWhite), + "bold": Styler(FGBold), + "faint": Styler(FGFaint), + "italic": Styler(FGItalic), + "underline": Styler(FGUnderline), +} + +func upLine(n uint) string { + return movementCode(n, 'A') +} + +func movementCode(n uint, code rune) string { + return esc + strconv.FormatUint(uint64(n), 10) + string(code) +} + +// Styler is a function that accepts multiple possible styling transforms from the state, +// color and background colors constants and transforms them into a templated string +// to apply those styles in the CLI. +// +// The returned styling function accepts a string that will be extended with +// the wrapping function's styling attributes. +func Styler(attrs ...attribute) func(interface{}) string { + attrstrs := make([]string, len(attrs)) + for i, v := range attrs { + attrstrs[i] = strconv.Itoa(int(v)) + } + + seq := strings.Join(attrstrs, ";") + + return func(v interface{}) string { + end := "" + s, ok := v.(string) + if !ok || !strings.HasSuffix(s, ResetCode) { + end = ResetCode + } + return fmt.Sprintf("%s%sm%v%s", esc, seq, v, end) + } +} diff --git a/vendor/github.com/manifoldco/promptui/cursor.go b/vendor/github.com/manifoldco/promptui/cursor.go new file mode 100644 index 000000000..7f0961bdb --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/cursor.go @@ -0,0 +1,232 @@ +package promptui + +import ( + "fmt" + "strings" +) + +// Pointer is A specific type that translates a given set of runes into a given +// set of runes pointed at by the cursor. +type Pointer func(to []rune) []rune + +func defaultCursor(ignored []rune) []rune { + return []rune("\u2588") +} + +func blockCursor(input []rune) []rune { + return []rune(fmt.Sprintf("\\e[7m%s\\e[0m", string(input))) +} + +func pipeCursor(input []rune) []rune { + marker := []rune("|") + out := []rune{} + out = append(out, marker...) + out = append(out, input...) + return out +} + +var ( + // DefaultCursor is a big square block character. Obscures whatever was + // input. + DefaultCursor Pointer = defaultCursor + // BlockCursor is a cursor which highlights a character by inverting colors + // on it. + BlockCursor Pointer = blockCursor + // PipeCursor is a pipe character "|" which appears before the input + // character. + PipeCursor Pointer = pipeCursor +) + +// Cursor tracks the state associated with the movable cursor +// The strategy is to keep the prompt, input pristine except for requested +// modifications. The insertion of the cursor happens during a `format` call +// and we read in new input via an `Update` call +type Cursor struct { + // shows where the user inserts/updates text + Cursor Pointer + // what the user entered, and what we will echo back to them, after + // insertion of the cursor and prefixing with the prompt + input []rune + // Put the cursor before this slice + Position int + erase bool +} + +// NewCursor create a new cursor, with the DefaultCursor, the specified input, +// and position at the end of the specified starting input. +func NewCursor(startinginput string, pointer Pointer, eraseDefault bool) Cursor { + if pointer == nil { + pointer = defaultCursor + } + cur := Cursor{Cursor: pointer, Position: len(startinginput), input: []rune(startinginput), erase: eraseDefault} + if eraseDefault { + cur.Start() + } else { + cur.End() + } + return cur +} + +func (c *Cursor) String() string { + return fmt.Sprintf( + "Cursor: %s, input %s, Position %d", + string(c.Cursor([]rune(""))), string(c.input), c.Position) +} + +// End is a convenience for c.Place(len(c.input)) so you don't have to know how I +// indexed. +func (c *Cursor) End() { + c.Place(len(c.input)) +} + +// Start is convenience for c.Place(0) so you don't have to know how I +// indexed. +func (c *Cursor) Start() { + c.Place(0) +} + +// ensures we are in bounds. +func (c *Cursor) correctPosition() { + if c.Position > len(c.input) { + c.Position = len(c.input) + } + + if c.Position < 0 { + c.Position = 0 + } +} + +// insert the cursor rune array into r before the provided index +func format(a []rune, c *Cursor) string { + i := c.Position + var b []rune + + out := make([]rune, 0) + if i < len(a) { + b = c.Cursor(a[i : i+1]) + out = append(out, a[:i]...) // does not include i + out = append(out, b...) // add the cursor + out = append(out, a[i+1:]...) // add the rest after i + } else { + b = c.Cursor([]rune{}) + out = append(out, a...) + out = append(out, b...) + } + return string(out) +} + +// Format renders the input with the Cursor appropriately positioned. +func (c *Cursor) Format() string { + r := c.input + // insert the cursor + return format(r, c) +} + +// FormatMask replaces all input runes with the mask rune. +func (c *Cursor) FormatMask(mask rune) string { + if mask == ' ' { + return format([]rune{}, c) + } + + r := make([]rune, len(c.input)) + for i := range r { + r[i] = mask + } + return format(r, c) +} + +// Update inserts newinput into the input []rune in the appropriate place. +// The cursor is moved to the end of the inputed sequence. +func (c *Cursor) Update(newinput string) { + a := c.input + b := []rune(newinput) + i := c.Position + a = append(a[:i], append(b, a[i:]...)...) + c.input = a + c.Move(len(b)) +} + +// Get returns a copy of the input +func (c *Cursor) Get() string { + return string(c.input) +} + +// GetMask returns a mask string with length equal to the input +func (c *Cursor) GetMask(mask rune) string { + return strings.Repeat(string(mask), len(c.input)) +} + +// Replace replaces the previous input with whatever is specified, and moves the +// cursor to the end position +func (c *Cursor) Replace(input string) { + c.input = []rune(input) + c.End() +} + +// Place moves the cursor to the absolute array index specified by position +func (c *Cursor) Place(position int) { + c.Position = position + c.correctPosition() +} + +// Move moves the cursor over in relative terms, by shift indices. +func (c *Cursor) Move(shift int) { + // delete the current cursor + c.Position = c.Position + shift + c.correctPosition() +} + +// Backspace removes the rune that precedes the cursor +// +// It handles being at the beginning or end of the row, and moves the cursor to +// the appropriate position. +func (c *Cursor) Backspace() { + a := c.input + i := c.Position + if i == 0 { + // Shrug + return + } + if i == len(a) { + c.input = a[:i-1] + } else { + c.input = append(a[:i-1], a[i:]...) + } + // now it's pointing to the i+1th element + c.Move(-1) +} + +// Listen is a readline Listener that updates internal cursor state appropriately. +func (c *Cursor) Listen(line []rune, pos int, key rune) ([]rune, int, bool) { + if line != nil { + // no matter what, update our internal representation. + c.Update(string(line)) + } + + switch key { + case 0: // empty + case KeyEnter: + return []rune(c.Get()), c.Position, false + case KeyBackspace, KeyCtrlH: + if c.erase { + c.erase = false + c.Replace("") + } + c.Backspace() + case KeyForward: + // the user wants to edit the default, despite how we set it up. Let + // them. + c.erase = false + c.Move(1) + case KeyBackward: + c.Move(-1) + default: + if c.erase { + c.erase = false + c.Replace("") + c.Update(string(key)) + } + } + + return []rune(c.Get()), c.Position, true +} diff --git a/vendor/github.com/manifoldco/promptui/go.mod b/vendor/github.com/manifoldco/promptui/go.mod new file mode 100644 index 000000000..760a44deb --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/go.mod @@ -0,0 +1,16 @@ +module github.com/manifoldco/promptui + +go 1.12 + +require ( + github.com/chzyer/logex v1.1.10 // indirect + github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e + github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect + github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a + github.com/kr/pretty v0.1.0 // indirect + github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a // indirect + github.com/mattn/go-colorable v0.0.9 // indirect + github.com/mattn/go-isatty v0.0.4 // indirect + golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect +) diff --git a/vendor/github.com/manifoldco/promptui/go.sum b/vendor/github.com/manifoldco/promptui/go.sum new file mode 100644 index 000000000..be5f99025 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/go.sum @@ -0,0 +1,23 @@ +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b h1:MQE+LT/ABUuuvEZ+YQAMSXindAdUh7slEmAkup74op4= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/vendor/github.com/manifoldco/promptui/keycodes.go b/vendor/github.com/manifoldco/promptui/keycodes.go new file mode 100644 index 000000000..ef5cd6f0a --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/keycodes.go @@ -0,0 +1,29 @@ +package promptui + +import "github.com/chzyer/readline" + +// These runes are used to identify the commands entered by the user in the command prompt. They map +// to specific actions of promptui in prompt mode and can be remapped if necessary. +var ( + // KeyEnter is the default key for submission/selection. + KeyEnter rune = readline.CharEnter + + // KeyCtrlH is the key for deleting input text. + KeyCtrlH rune = readline.CharCtrlH + + // KeyPrev is the default key to go up during selection. + KeyPrev rune = readline.CharPrev + KeyPrevDisplay = "↑" + + // KeyNext is the default key to go down during selection. + KeyNext rune = readline.CharNext + KeyNextDisplay = "↓" + + // KeyBackward is the default key to page up during selection. + KeyBackward rune = readline.CharBackward + KeyBackwardDisplay = "←" + + // KeyForward is the default key to page down during selection. + KeyForward rune = readline.CharForward + KeyForwardDisplay = "→" +) diff --git a/vendor/github.com/manifoldco/promptui/keycodes_other.go b/vendor/github.com/manifoldco/promptui/keycodes_other.go new file mode 100644 index 000000000..789feae4c --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/keycodes_other.go @@ -0,0 +1,10 @@ +// +build !windows + +package promptui + +import "github.com/chzyer/readline" + +var ( + // KeyBackspace is the default key for deleting input text. + KeyBackspace rune = readline.CharBackspace +) diff --git a/vendor/github.com/manifoldco/promptui/keycodes_windows.go b/vendor/github.com/manifoldco/promptui/keycodes_windows.go new file mode 100644 index 000000000..737d1566e --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/keycodes_windows.go @@ -0,0 +1,10 @@ +// +build windows + +package promptui + +// source: https://msdn.microsoft.com/en-us/library/aa243025(v=vs.60).aspx + +var ( + // KeyBackspace is the default key for deleting input text inside a command line prompt. + KeyBackspace rune = 8 +) diff --git a/vendor/github.com/manifoldco/promptui/list/list.go b/vendor/github.com/manifoldco/promptui/list/list.go new file mode 100644 index 000000000..c98a39cfa --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/list/list.go @@ -0,0 +1,237 @@ +package list + +import ( + "fmt" + "reflect" + "strings" +) + +// Searcher is a base function signature that is used inside select when activating the search mode. +// If defined, it is called on each items of the select and should return a boolean for whether or not +// the item fits the searched term. +type Searcher func(input string, index int) bool + +// NotFound is an index returned when no item was selected. This could +// happen due to a search without results. +const NotFound = -1 + +// List holds a collection of items that can be displayed with an N number of +// visible items. The list can be moved up, down by one item of time or an +// entire page (ie: visible size). It keeps track of the current selected item. +type List struct { + items []*interface{} + scope []*interface{} + cursor int // cursor holds the index of the current selected item + size int // size is the number of visible options + start int + Searcher Searcher +} + +// New creates and initializes a list of searchable items. The items attribute must be a slice type with a +// size greater than 0. Error will be returned if those two conditions are not met. +func New(items interface{}, size int) (*List, error) { + if size < 1 { + return nil, fmt.Errorf("list size %d must be greater than 0", size) + } + + if items == nil || reflect.TypeOf(items).Kind() != reflect.Slice { + return nil, fmt.Errorf("items %v is not a slice", items) + } + + slice := reflect.ValueOf(items) + values := make([]*interface{}, slice.Len()) + + for i := range values { + item := slice.Index(i).Interface() + values[i] = &item + } + + return &List{size: size, items: values, scope: values}, nil +} + +// Prev moves the visible list back one item. If the selected item is out of +// view, the new select item becomes the last visible item. If the list is +// already at the top, nothing happens. +func (l *List) Prev() { + if l.cursor > 0 { + l.cursor-- + } + + if l.start > l.cursor { + l.start = l.cursor + } +} + +// Search allows the list to be filtered by a given term. The list must +// implement the searcher function signature for this functionality to work. +func (l *List) Search(term string) { + term = strings.Trim(term, " ") + l.cursor = 0 + l.start = 0 + l.search(term) +} + +// CancelSearch stops the current search and returns the list to its +// original order. +func (l *List) CancelSearch() { + l.cursor = 0 + l.start = 0 + l.scope = l.items +} + +func (l *List) search(term string) { + var scope []*interface{} + + for i, item := range l.items { + if l.Searcher(term, i) { + scope = append(scope, item) + } + } + + l.scope = scope +} + +// Start returns the current render start position of the list. +func (l *List) Start() int { + return l.start +} + +// SetStart sets the current scroll position. Values out of bounds will be +// clamped. +func (l *List) SetStart(i int) { + if i < 0 { + i = 0 + } + if i > l.cursor { + l.start = l.cursor + } else { + l.start = i + } +} + +// SetCursor sets the position of the cursor in the list. Values out of bounds +// will be clamped. +func (l *List) SetCursor(i int) { + max := len(l.scope) - 1 + if i >= max { + i = max + } + if i < 0 { + i = 0 + } + l.cursor = i + + if l.start > l.cursor { + l.start = l.cursor + } else if l.start+l.size <= l.cursor { + l.start = l.cursor - l.size + 1 + } +} + +// Next moves the visible list forward one item. If the selected item is out of +// view, the new select item becomes the first visible item. If the list is +// already at the bottom, nothing happens. +func (l *List) Next() { + max := len(l.scope) - 1 + + if l.cursor < max { + l.cursor++ + } + + if l.start+l.size <= l.cursor { + l.start = l.cursor - l.size + 1 + } +} + +// PageUp moves the visible list backward by x items. Where x is the size of the +// visible items on the list. The selected item becomes the first visible item. +// If the list is already at the bottom, the selected item becomes the last +// visible item. +func (l *List) PageUp() { + start := l.start - l.size + if start < 0 { + l.start = 0 + } else { + l.start = start + } + + cursor := l.start + + if cursor < l.cursor { + l.cursor = cursor + } +} + +// PageDown moves the visible list forward by x items. Where x is the size of +// the visible items on the list. The selected item becomes the first visible +// item. +func (l *List) PageDown() { + start := l.start + l.size + max := len(l.scope) - l.size + + switch { + case len(l.scope) < l.size: + l.start = 0 + case start > max: + l.start = max + default: + l.start = start + } + + cursor := l.start + + if cursor == l.cursor { + l.cursor = len(l.scope) - 1 + } else if cursor > l.cursor { + l.cursor = cursor + } +} + +// CanPageDown returns whether a list can still PageDown(). +func (l *List) CanPageDown() bool { + max := len(l.scope) + return l.start+l.size < max +} + +// CanPageUp returns whether a list can still PageUp(). +func (l *List) CanPageUp() bool { + return l.start > 0 +} + +// Index returns the index of the item currently selected inside the searched list. If no item is selected, +// the NotFound (-1) index is returned. +func (l *List) Index() int { + selected := l.scope[l.cursor] + + for i, item := range l.items { + if item == selected { + return i + } + } + + return NotFound +} + +// Items returns a slice equal to the size of the list with the current visible +// items and the index of the active item in this list. +func (l *List) Items() ([]interface{}, int) { + var result []interface{} + max := len(l.scope) + end := l.start + l.size + + if end > max { + end = max + } + + active := NotFound + + for i, j := l.start, 0; i < end; i, j = i+1, j+1 { + if l.cursor == i { + active = j + } + + result = append(result, *l.scope[i]) + } + + return result, active +} diff --git a/vendor/github.com/manifoldco/promptui/prompt.go b/vendor/github.com/manifoldco/promptui/prompt.go new file mode 100644 index 000000000..8e35123b0 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/prompt.go @@ -0,0 +1,341 @@ +package promptui + +import ( + "fmt" + "io" + "strings" + "text/template" + + "github.com/chzyer/readline" + "github.com/manifoldco/promptui/screenbuf" +) + +// Prompt represents a single line text field input with options for validation and input masks. +type Prompt struct { + // Label is the value displayed on the command line prompt. + // + // The value for Label can be a simple string or a struct that will need to be accessed by dot notation + // inside the templates. For example, `{{ .Name }}` will display the name property of a struct. + Label interface{} + + // Default is the initial value for the prompt. This value will be displayed next to the prompt's label + // and the user will be able to view or change it depending on the options. + Default string + + // AllowEdit lets the user edit the default value. If false, any key press + // other than <Enter> automatically clears the default value. + AllowEdit bool + + // Validate is an optional function that fill be used against the entered value in the prompt to validate it. + Validate ValidateFunc + + // Mask is an optional rune that sets which character to display instead of the entered characters. This + // allows hiding private information like passwords. + Mask rune + + // HideEntered sets whether to hide the text after the user has pressed enter. + HideEntered bool + + // Templates can be used to customize the prompt output. If nil is passed, the + // default templates are used. See the PromptTemplates docs for more info. + Templates *PromptTemplates + + // IsConfirm makes the prompt ask for a yes or no ([Y/N]) question rather than request an input. When set, + // most properties related to input will be ignored. + IsConfirm bool + + // IsVimMode enables vi-like movements (hjkl) and editing. + IsVimMode bool + + // the Pointer defines how to render the cursor. + Pointer Pointer + + Stdin io.ReadCloser + Stdout io.WriteCloser +} + +// PromptTemplates allow a prompt to be customized following stdlib +// text/template syntax. Custom state, colors and background color are available for use inside +// the templates and are documented inside the Variable section of the docs. +// +// Examples +// +// text/templates use a special notation to display programmable content. Using the double bracket notation, +// the value can be printed with specific helper functions. For example +// +// This displays the value given to the template as pure, unstylized text. +// '{{ . }}' +// +// This displays the value colored in cyan +// '{{ . | cyan }}' +// +// This displays the value colored in red with a cyan background-color +// '{{ . | red | cyan }}' +// +// See the doc of text/template for more info: https://golang.org/pkg/text/template/ +type PromptTemplates struct { + // Prompt is a text/template for the prompt label displayed on the left side of the prompt. + Prompt string + + // Prompt is a text/template for the prompt label when IsConfirm is set as true. + Confirm string + + // Valid is a text/template for the prompt label when the value entered is valid. + Valid string + + // Invalid is a text/template for the prompt label when the value entered is invalid. + Invalid string + + // Success is a text/template for the prompt label when the user has pressed entered and the value has been + // deemed valid by the validation function. The label will keep using this template even when the prompt ends + // inside the console. + Success string + + // Prompt is a text/template for the prompt label when the value is invalid due to an error triggered by + // the prompt's validation function. + ValidationError string + + // FuncMap is a map of helper functions that can be used inside of templates according to the text/template + // documentation. + // + // By default, FuncMap contains the color functions used to color the text in templates. If FuncMap + // is overridden, the colors functions must be added in the override from promptui.FuncMap to work. + FuncMap template.FuncMap + + prompt *template.Template + valid *template.Template + invalid *template.Template + validation *template.Template + success *template.Template +} + +// Run executes the prompt. Its displays the label and default value if any, asking the user to enter a value. +// Run will keep the prompt alive until it has been canceled from the command prompt or it has received a valid +// value. It will return the value and an error if any occurred during the prompt's execution. +func (p *Prompt) Run() (string, error) { + var err error + + err = p.prepareTemplates() + if err != nil { + return "", err + } + + c := &readline.Config{ + Stdin: p.Stdin, + Stdout: p.Stdout, + EnableMask: p.Mask != 0, + MaskRune: p.Mask, + HistoryLimit: -1, + VimMode: p.IsVimMode, + UniqueEditLine: true, + } + + err = c.Init() + if err != nil { + return "", err + } + + rl, err := readline.NewEx(c) + if err != nil { + return "", err + } + // we're taking over the cursor, so stop showing it. + rl.Write([]byte(hideCursor)) + sb := screenbuf.New(rl) + + validFn := func(x string) error { + return nil + } + if p.Validate != nil { + validFn = p.Validate + } + + var inputErr error + input := p.Default + if p.IsConfirm { + input = "" + } + eraseDefault := input != "" && !p.AllowEdit + cur := NewCursor(input, p.Pointer, eraseDefault) + + listen := func(input []rune, pos int, key rune) ([]rune, int, bool) { + _, _, keepOn := cur.Listen(input, pos, key) + err := validFn(cur.Get()) + var prompt []byte + + if err != nil { + prompt = render(p.Templates.invalid, p.Label) + } else { + prompt = render(p.Templates.valid, p.Label) + if p.IsConfirm { + prompt = render(p.Templates.prompt, p.Label) + } + } + + echo := cur.Format() + if p.Mask != 0 { + echo = cur.FormatMask(p.Mask) + } + + prompt = append(prompt, []byte(echo)...) + sb.Reset() + sb.Write(prompt) + if inputErr != nil { + validation := render(p.Templates.validation, inputErr) + sb.Write(validation) + inputErr = nil + } + sb.Flush() + return nil, 0, keepOn + } + + c.SetListener(listen) + + for { + _, err = rl.Readline() + inputErr = validFn(cur.Get()) + if inputErr == nil { + break + } + + if err != nil { + break + } + } + + if err != nil { + switch err { + case readline.ErrInterrupt: + err = ErrInterrupt + case io.EOF: + err = ErrEOF + } + if err.Error() == "Interrupt" { + err = ErrInterrupt + } + sb.Reset() + sb.WriteString("") + sb.Flush() + rl.Write([]byte(showCursor)) + rl.Close() + return "", err + } + + echo := cur.Get() + if p.Mask != 0 { + echo = cur.GetMask(p.Mask) + } + + prompt := render(p.Templates.success, p.Label) + prompt = append(prompt, []byte(echo)...) + + if p.IsConfirm { + lowerDefault := strings.ToLower(p.Default) + if strings.ToLower(cur.Get()) != "y" && (lowerDefault != "y" || (lowerDefault == "y" && cur.Get() != "")) { + prompt = render(p.Templates.invalid, p.Label) + err = ErrAbort + } + } + + if p.HideEntered { + clearScreen(sb) + } else { + sb.Reset() + sb.Write(prompt) + sb.Flush() + } + + rl.Write([]byte(showCursor)) + rl.Close() + + return cur.Get(), err +} + +func (p *Prompt) prepareTemplates() error { + tpls := p.Templates + if tpls == nil { + tpls = &PromptTemplates{} + } + + if tpls.FuncMap == nil { + tpls.FuncMap = FuncMap + } + + bold := Styler(FGBold) + + if p.IsConfirm { + if tpls.Confirm == "" { + confirm := "y/N" + if strings.ToLower(p.Default) == "y" { + confirm = "Y/n" + } + tpls.Confirm = fmt.Sprintf(`{{ "%s" | bold }} {{ . | bold }}? {{ "[%s]" | faint }} `, IconInitial, confirm) + } + + tpl, err := template.New("").Funcs(tpls.FuncMap).Parse(tpls.Confirm) + if err != nil { + return err + } + + tpls.prompt = tpl + } else { + if tpls.Prompt == "" { + tpls.Prompt = fmt.Sprintf("%s {{ . | bold }}%s ", bold(IconInitial), bold(":")) + } + + tpl, err := template.New("").Funcs(tpls.FuncMap).Parse(tpls.Prompt) + if err != nil { + return err + } + + tpls.prompt = tpl + } + + if tpls.Valid == "" { + tpls.Valid = fmt.Sprintf("%s {{ . | bold }}%s ", bold(IconGood), bold(":")) + } + + tpl, err := template.New("").Funcs(tpls.FuncMap).Parse(tpls.Valid) + if err != nil { + return err + } + + tpls.valid = tpl + + if tpls.Invalid == "" { + tpls.Invalid = fmt.Sprintf("%s {{ . | bold }}%s ", bold(IconBad), bold(":")) + } + + tpl, err = template.New("").Funcs(tpls.FuncMap).Parse(tpls.Invalid) + if err != nil { + return err + } + + tpls.invalid = tpl + + if tpls.ValidationError == "" { + tpls.ValidationError = `{{ ">>" | red }} {{ . | red }}` + } + + tpl, err = template.New("").Funcs(tpls.FuncMap).Parse(tpls.ValidationError) + if err != nil { + return err + } + + tpls.validation = tpl + + if tpls.Success == "" { + tpls.Success = fmt.Sprintf("{{ . | faint }}%s ", Styler(FGFaint)(":")) + } + + tpl, err = template.New("").Funcs(tpls.FuncMap).Parse(tpls.Success) + if err != nil { + return err + } + + tpls.success = tpl + + p.Templates = tpls + + return nil +} diff --git a/vendor/github.com/manifoldco/promptui/promptui.go b/vendor/github.com/manifoldco/promptui/promptui.go new file mode 100644 index 000000000..6248e5859 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/promptui.go @@ -0,0 +1,27 @@ +// Package promptui is a library providing a simple interface to create command-line prompts for go. +// It can be easily integrated into spf13/cobra, urfave/cli or any cli go application. +// +// promptui has two main input modes: +// +// Prompt provides a single line for user input. It supports optional live validation, +// confirmation and masking the input. +// +// Select provides a list of options to choose from. It supports pagination, search, +// detailed view and custom templates. +package promptui + +import "errors" + +// ErrEOF is the error returned from prompts when EOF is encountered. +var ErrEOF = errors.New("^D") + +// ErrInterrupt is the error returned from prompts when an interrupt (ctrl-c) is +// encountered. +var ErrInterrupt = errors.New("^C") + +// ErrAbort is the error returned when confirm prompts are supplied "n" +var ErrAbort = errors.New("") + +// ValidateFunc is a placeholder type for any validation functions that validates a given input. It should return +// a ValidationError if the input is not valid. +type ValidateFunc func(string) error diff --git a/vendor/github.com/manifoldco/promptui/screenbuf/screenbuf.go b/vendor/github.com/manifoldco/promptui/screenbuf/screenbuf.go new file mode 100644 index 000000000..861390045 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/screenbuf/screenbuf.go @@ -0,0 +1,151 @@ +package screenbuf + +import ( + "bytes" + "fmt" + "io" +) + +const esc = "\033[" + +var ( + clearLine = []byte(esc + "2K\r") + moveUp = []byte(esc + "1A") + moveDown = []byte(esc + "1B") +) + +// ScreenBuf is a convenient way to write to terminal screens. It creates, +// clears and, moves up or down lines as needed to write the output to the +// terminal using ANSI escape codes. +type ScreenBuf struct { + w io.Writer + buf *bytes.Buffer + reset bool + cursor int + height int +} + +// New creates and initializes a new ScreenBuf. +func New(w io.Writer) *ScreenBuf { + return &ScreenBuf{buf: &bytes.Buffer{}, w: w} +} + +// Reset truncates the underlining buffer and marks all its previous lines to be +// cleared during the next Write. +func (s *ScreenBuf) Reset() { + s.buf.Reset() + s.reset = true +} + +// Clear clears all previous lines and the output starts from the top. +func (s *ScreenBuf) Clear() error { + for i := 0; i < s.height; i++ { + _, err := s.buf.Write(moveUp) + if err != nil { + return err + } + _, err = s.buf.Write(clearLine) + if err != nil { + return err + } + } + s.cursor = 0 + s.height = 0 + s.reset = false + return nil +} + +// Write writes a single line to the underlining buffer. If the ScreenBuf was +// previously reset, all previous lines are cleared and the output starts from +// the top. Lines with \r or \n will cause an error since they can interfere with the +// terminal ability to move between lines. +func (s *ScreenBuf) Write(b []byte) (int, error) { + if bytes.ContainsAny(b, "\r\n") { + return 0, fmt.Errorf("%q should not contain either \\r or \\n", b) + } + + if s.reset { + if err := s.Clear(); err != nil { + return 0, err + } + } + + switch { + case s.cursor == s.height: + n, err := s.buf.Write(clearLine) + if err != nil { + return n, err + } + + n, err = s.buf.Write(b) + if err != nil { + return n, err + } + + _, err = s.buf.Write([]byte("\n")) + if err != nil { + return n, err + } + + s.height++ + s.cursor++ + return n, nil + case s.cursor < s.height: + n, err := s.buf.Write(clearLine) + if err != nil { + return n, err + } + n, err = s.buf.Write(b) + if err != nil { + return n, err + } + n, err = s.buf.Write(moveDown) + if err != nil { + return n, err + } + s.cursor++ + return n, nil + default: + return 0, fmt.Errorf("Invalid write cursor position (%d) exceeded line height: %d", s.cursor, s.height) + } +} + +// Flush writes any buffered data to the underlying io.Writer, ensuring that any pending data is displayed. +func (s *ScreenBuf) Flush() error { + for i := s.cursor; i < s.height; i++ { + if i < s.height { + _, err := s.buf.Write(clearLine) + if err != nil { + return err + } + } + _, err := s.buf.Write(moveDown) + if err != nil { + return err + } + } + + _, err := s.buf.WriteTo(s.w) + if err != nil { + return err + } + + s.buf.Reset() + + for i := 0; i < s.height; i++ { + _, err := s.buf.Write(moveUp) + if err != nil { + return err + } + } + + s.cursor = 0 + + return nil +} + +// WriteString is a convenient function to write a new line passing a string. +// Check ScreenBuf.Write() for a detailed explanation of the function behaviour. +func (s *ScreenBuf) WriteString(str string) (int, error) { + return s.Write([]byte(str)) +} diff --git a/vendor/github.com/manifoldco/promptui/select.go b/vendor/github.com/manifoldco/promptui/select.go new file mode 100644 index 000000000..19b9e0c2e --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/select.go @@ -0,0 +1,637 @@ +package promptui + +import ( + "bytes" + "fmt" + "io" + "os" + "text/template" + + "github.com/chzyer/readline" + "github.com/juju/ansiterm" + "github.com/manifoldco/promptui/list" + "github.com/manifoldco/promptui/screenbuf" +) + +// SelectedAdd is used internally inside SelectWithAdd when the add option is selected in select mode. +// Since -1 is not a possible selected index, this ensure that add mode is always unique inside +// SelectWithAdd's logic. +const SelectedAdd = -1 + +// Select represents a list of items used to enable selections, they can be used as search engines, menus +// or as a list of items in a cli based prompt. +type Select struct { + // Label is the text displayed on top of the list to direct input. The IconInitial value "?" will be + // appended automatically to the label so it does not need to be added. + // + // The value for Label can be a simple string or a struct that will need to be accessed by dot notation + // inside the templates. For example, `{{ .Name }}` will display the name property of a struct. + Label interface{} + + // Items are the items to display inside the list. It expect a slice of any kind of values, including strings. + // + // If using a slice of strings, promptui will use those strings directly into its base templates or the + // provided templates. If using any other type in the slice, it will attempt to transform it into a string + // before giving it to its templates. Custom templates will override this behavior if using the dot notation + // inside the templates. + // + // For example, `{{ .Name }}` will display the name property of a struct. + Items interface{} + + // Size is the number of items that should appear on the select before scrolling is necessary. Defaults to 5. + Size int + + // CursorPos is the initial position of the cursor. + CursorPos int + + // IsVimMode sets whether to use vim mode when using readline in the command prompt. Look at + // https://godoc.org/github.com/chzyer/readline#Config for more information on readline. + IsVimMode bool + + // HideHelp sets whether to hide help information. + HideHelp bool + + // HideSelected sets whether to hide the text displayed after an item is successfully selected. + HideSelected bool + + // Templates can be used to customize the select output. If nil is passed, the + // default templates are used. See the SelectTemplates docs for more info. + Templates *SelectTemplates + + // Keys is the set of keys used in select mode to control the command line interface. See the SelectKeys docs for + // more info. + Keys *SelectKeys + + // Searcher is a function that can be implemented to refine the base searching algorithm in selects. + // + // Search is a function that will receive the searched term and the item's index and should return a boolean + // for whether or not the terms are alike. It is unimplemented by default and search will not work unless + // it is implemented. + Searcher list.Searcher + + // StartInSearchMode sets whether or not the select mode should start in search mode or selection mode. + // For search mode to work, the Search property must be implemented. + StartInSearchMode bool + + list *list.List + + // A function that determines how to render the cursor + Pointer Pointer + + Stdin io.ReadCloser + Stdout io.WriteCloser +} + +// SelectKeys defines the available keys used by select mode to enable the user to move around the list +// and trigger search mode. See the Key struct docs for more information on keys. +type SelectKeys struct { + // Next is the key used to move to the next element inside the list. Defaults to down arrow key. + Next Key + + // Prev is the key used to move to the previous element inside the list. Defaults to up arrow key. + Prev Key + + // PageUp is the key used to jump back to the first element inside the list. Defaults to left arrow key. + PageUp Key + + // PageUp is the key used to jump forward to the last element inside the list. Defaults to right arrow key. + PageDown Key + + // Search is the key used to trigger the search mode for the list. Default to the "/" key. + Search Key +} + +// Key defines a keyboard code and a display representation for the help menu. +type Key struct { + // Code is a rune that will be used to compare against typed keys with readline. + // Check https://github.com/chzyer/readline for a list of codes + Code rune + + // Display is the string that will be displayed inside the help menu to help inform the user + // of which key to use on his keyboard for various functions. + Display string +} + +// SelectTemplates allow a select list to be customized following stdlib +// text/template syntax. Custom state, colors and background color are available for use inside +// the templates and are documented inside the Variable section of the docs. +// +// Examples +// +// text/templates use a special notation to display programmable content. Using the double bracket notation, +// the value can be printed with specific helper functions. For example +// +// This displays the value given to the template as pure, unstylized text. Structs are transformed to string +// with this notation. +// '{{ . }}' +// +// This displays the name property of the value colored in cyan +// '{{ .Name | cyan }}' +// +// This displays the label property of value colored in red with a cyan background-color +// '{{ .Label | red | cyan }}' +// +// See the doc of text/template for more info: https://golang.org/pkg/text/template/ +// +// Notes +// +// Setting any of these templates will remove the icons from the default templates. They must +// be added back in each of their specific templates. The styles.go constants contains the default icons. +type SelectTemplates struct { + // Label is a text/template for the main command line label. Defaults to printing the label as it with + // the IconInitial. + Label string + + // Active is a text/template for when an item is currently active within the list. + Active string + + // Inactive is a text/template for when an item is not currently active inside the list. This + // template is used for all items unless they are active or selected. + Inactive string + + // Selected is a text/template for when an item was successfully selected. + Selected string + + // Details is a text/template for when an item current active to show + // additional information. It can have multiple lines. + // + // Detail will always be displayed for the active element and thus can be used to display additional + // information on the element beyond its label. + // + // promptui will not trim spaces and tabs will be displayed if the template is indented. + Details string + + // Help is a text/template for displaying instructions at the top. By default + // it shows keys for movement and search. + Help string + + // FuncMap is a map of helper functions that can be used inside of templates according to the text/template + // documentation. + // + // By default, FuncMap contains the color functions used to color the text in templates. If FuncMap + // is overridden, the colors functions must be added in the override from promptui.FuncMap to work. + FuncMap template.FuncMap + + label *template.Template + active *template.Template + inactive *template.Template + selected *template.Template + details *template.Template + help *template.Template +} + +// SearchPrompt is the prompt displayed in search mode. +var SearchPrompt = "Search: " + +// Run executes the select list. It displays the label and the list of items, asking the user to chose any +// value within to list. Run will keep the prompt alive until it has been canceled from +// the command prompt or it has received a valid value. It will return the value and an error if any +// occurred during the select's execution. +func (s *Select) Run() (int, string, error) { + return s.RunCursorAt(s.CursorPos, 0) +} + +// RunCursorAt executes the select list, initializing the cursor to the given +// position. Invalid cursor positions will be clamped to valid values. It +// displays the label and the list of items, asking the user to chose any value +// within to list. Run will keep the prompt alive until it has been canceled +// from the command prompt or it has received a valid value. It will return +// the value and an error if any occurred during the select's execution. +func (s *Select) RunCursorAt(cursorPos, scroll int) (int, string, error) { + if s.Size == 0 { + s.Size = 5 + } + + l, err := list.New(s.Items, s.Size) + if err != nil { + return 0, "", err + } + l.Searcher = s.Searcher + + s.list = l + + s.setKeys() + + err = s.prepareTemplates() + if err != nil { + return 0, "", err + } + return s.innerRun(cursorPos, scroll, ' ') +} + +func (s *Select) innerRun(cursorPos, scroll int, top rune) (int, string, error) { + c := &readline.Config{ + Stdin: s.Stdin, + Stdout: s.Stdout, + } + err := c.Init() + if err != nil { + return 0, "", err + } + + c.Stdin = readline.NewCancelableStdin(c.Stdin) + + if s.IsVimMode { + c.VimMode = true + } + + c.HistoryLimit = -1 + c.UniqueEditLine = true + + rl, err := readline.NewEx(c) + if err != nil { + return 0, "", err + } + + rl.Write([]byte(hideCursor)) + sb := screenbuf.New(rl) + + cur := NewCursor("", s.Pointer, false) + + canSearch := s.Searcher != nil + searchMode := s.StartInSearchMode + s.list.SetCursor(cursorPos) + s.list.SetStart(scroll) + + c.SetListener(func(line []rune, pos int, key rune) ([]rune, int, bool) { + switch { + case key == KeyEnter: + return nil, 0, true + case key == s.Keys.Next.Code || (key == 'j' && !searchMode): + s.list.Next() + case key == s.Keys.Prev.Code || (key == 'k' && !searchMode): + s.list.Prev() + case key == s.Keys.Search.Code: + if !canSearch { + break + } + + if searchMode { + searchMode = false + cur.Replace("") + s.list.CancelSearch() + } else { + searchMode = true + } + case key == KeyBackspace || key == KeyCtrlH: + if !canSearch || !searchMode { + break + } + + cur.Backspace() + if len(cur.Get()) > 0 { + s.list.Search(cur.Get()) + } else { + s.list.CancelSearch() + } + case key == s.Keys.PageUp.Code || (key == 'h' && !searchMode): + s.list.PageUp() + case key == s.Keys.PageDown.Code || (key == 'l' && !searchMode): + s.list.PageDown() + default: + if canSearch && searchMode { + cur.Update(string(line)) + s.list.Search(cur.Get()) + } + } + + if searchMode { + header := SearchPrompt + cur.Format() + sb.WriteString(header) + } else if !s.HideHelp { + help := s.renderHelp(canSearch) + sb.Write(help) + } + + label := render(s.Templates.label, s.Label) + sb.Write(label) + + items, idx := s.list.Items() + last := len(items) - 1 + + for i, item := range items { + page := " " + + switch i { + case 0: + if s.list.CanPageUp() { + page = "↑" + } else { + page = string(top) + } + case last: + if s.list.CanPageDown() { + page = "↓" + } + } + + output := []byte(page + " ") + + if i == idx { + output = append(output, render(s.Templates.active, item)...) + } else { + output = append(output, render(s.Templates.inactive, item)...) + } + + sb.Write(output) + } + + if idx == list.NotFound { + sb.WriteString("") + sb.WriteString("No results") + } else { + active := items[idx] + + details := s.renderDetails(active) + for _, d := range details { + sb.Write(d) + } + } + + sb.Flush() + + return nil, 0, true + }) + + for { + _, err = rl.Readline() + + if err != nil { + switch { + case err == readline.ErrInterrupt, err.Error() == "Interrupt": + err = ErrInterrupt + case err == io.EOF: + err = ErrEOF + } + break + } + + _, idx := s.list.Items() + if idx != list.NotFound { + break + } + + } + + if err != nil { + if err.Error() == "Interrupt" { + err = ErrInterrupt + } + sb.Reset() + sb.WriteString("") + sb.Flush() + rl.Write([]byte(showCursor)) + rl.Close() + return 0, "", err + } + + items, idx := s.list.Items() + item := items[idx] + + if s.HideSelected { + clearScreen(sb) + } else { + sb.Reset() + sb.Write(render(s.Templates.selected, item)) + sb.Flush() + } + + rl.Write([]byte(showCursor)) + rl.Close() + + return s.list.Index(), fmt.Sprintf("%v", item), err +} + +// ScrollPosition returns the current scroll position. +func (s *Select) ScrollPosition() int { + return s.list.Start() +} + +func (s *Select) prepareTemplates() error { + tpls := s.Templates + if tpls == nil { + tpls = &SelectTemplates{} + } + + if tpls.FuncMap == nil { + tpls.FuncMap = FuncMap + } + + if tpls.Label == "" { + tpls.Label = fmt.Sprintf("%s {{.}}: ", IconInitial) + } + + tpl, err := template.New("").Funcs(tpls.FuncMap).Parse(tpls.Label) + if err != nil { + return err + } + + tpls.label = tpl + + if tpls.Active == "" { + tpls.Active = fmt.Sprintf("%s {{ . | underline }}", IconSelect) + } + + tpl, err = template.New("").Funcs(tpls.FuncMap).Parse(tpls.Active) + if err != nil { + return err + } + + tpls.active = tpl + + if tpls.Inactive == "" { + tpls.Inactive = " {{.}}" + } + + tpl, err = template.New("").Funcs(tpls.FuncMap).Parse(tpls.Inactive) + if err != nil { + return err + } + + tpls.inactive = tpl + + if tpls.Selected == "" { + tpls.Selected = fmt.Sprintf(`{{ "%s" | green }} {{ . | faint }}`, IconGood) + } + + tpl, err = template.New("").Funcs(tpls.FuncMap).Parse(tpls.Selected) + if err != nil { + return err + } + tpls.selected = tpl + + if tpls.Details != "" { + tpl, err = template.New("").Funcs(tpls.FuncMap).Parse(tpls.Details) + if err != nil { + return err + } + + tpls.details = tpl + } + + if tpls.Help == "" { + tpls.Help = fmt.Sprintf(`{{ "Use the arrow keys to navigate:" | faint }} {{ .NextKey | faint }} ` + + `{{ .PrevKey | faint }} {{ .PageDownKey | faint }} {{ .PageUpKey | faint }} ` + + `{{ if .Search }} {{ "and" | faint }} {{ .SearchKey | faint }} {{ "toggles search" | faint }}{{ end }}`) + } + + tpl, err = template.New("").Funcs(tpls.FuncMap).Parse(tpls.Help) + if err != nil { + return err + } + + tpls.help = tpl + + s.Templates = tpls + + return nil +} + +// SelectWithAdd represents a list for selecting a single item inside a list of items with the possibility to +// add new items to the list. +type SelectWithAdd struct { + // Label is the text displayed on top of the list to direct input. The IconInitial value "?" will be + // appended automatically to the label so it does not need to be added. + Label string + + // Items are the items to display inside the list. Each item will be listed individually with the + // AddLabel as the first item of the list. + Items []string + + // AddLabel is the label used for the first item of the list that enables adding a new item. + // Selecting this item in the list displays the add item prompt using promptui/prompt. + AddLabel string + + // Validate is an optional function that fill be used against the entered value in the prompt to validate it. + // If the value is valid, it is returned to the callee to be added in the list. + Validate ValidateFunc + + // IsVimMode sets whether to use vim mode when using readline in the command prompt. Look at + // https://godoc.org/github.com/chzyer/readline#Config for more information on readline. + IsVimMode bool + + // a function that defines how to render the cursor + Pointer Pointer + + // HideHelp sets whether to hide help information. + HideHelp bool +} + +// Run executes the select list. Its displays the label and the list of items, asking the user to chose any +// value within to list or add his own. Run will keep the prompt alive until it has been canceled from +// the command prompt or it has received a valid value. +// +// If the addLabel is selected in the list, this function will return a -1 index with the added label and no error. +// Otherwise, it will return the index and the value of the selected item. In any case, if an error is triggered, it +// will also return the error as its third return value. +func (sa *SelectWithAdd) Run() (int, string, error) { + if len(sa.Items) > 0 { + newItems := append([]string{sa.AddLabel}, sa.Items...) + + list, err := list.New(newItems, 5) + if err != nil { + return 0, "", err + } + + s := Select{ + Label: sa.Label, + Items: newItems, + IsVimMode: sa.IsVimMode, + HideHelp: sa.HideHelp, + Size: 5, + list: list, + Pointer: sa.Pointer, + } + s.setKeys() + + err = s.prepareTemplates() + if err != nil { + return 0, "", err + } + + selected, value, err := s.innerRun(1, 0, '+') + if err != nil || selected != 0 { + return selected - 1, value, err + } + + // XXX run through terminal for windows + os.Stdout.Write([]byte(upLine(1) + "\r" + clearLine)) + } + + p := Prompt{ + Label: sa.AddLabel, + Validate: sa.Validate, + IsVimMode: sa.IsVimMode, + Pointer: sa.Pointer, + } + value, err := p.Run() + return SelectedAdd, value, err +} + +func (s *Select) setKeys() { + if s.Keys != nil { + return + } + s.Keys = &SelectKeys{ + Prev: Key{Code: KeyPrev, Display: KeyPrevDisplay}, + Next: Key{Code: KeyNext, Display: KeyNextDisplay}, + PageUp: Key{Code: KeyBackward, Display: KeyBackwardDisplay}, + PageDown: Key{Code: KeyForward, Display: KeyForwardDisplay}, + Search: Key{Code: '/', Display: "/"}, + } +} + +func (s *Select) renderDetails(item interface{}) [][]byte { + if s.Templates.details == nil { + return nil + } + + var buf bytes.Buffer + w := ansiterm.NewTabWriter(&buf, 0, 0, 8, ' ', 0) + + err := s.Templates.details.Execute(w, item) + if err != nil { + fmt.Fprintf(w, "%v", item) + } + + w.Flush() + + output := buf.Bytes() + + return bytes.Split(output, []byte("\n")) +} + +func (s *Select) renderHelp(b bool) []byte { + keys := struct { + NextKey string + PrevKey string + PageDownKey string + PageUpKey string + Search bool + SearchKey string + }{ + NextKey: s.Keys.Next.Display, + PrevKey: s.Keys.Prev.Display, + PageDownKey: s.Keys.PageDown.Display, + PageUpKey: s.Keys.PageUp.Display, + SearchKey: s.Keys.Search.Display, + Search: b, + } + + return render(s.Templates.help, keys) +} + +func render(tpl *template.Template, data interface{}) []byte { + var buf bytes.Buffer + err := tpl.Execute(&buf, data) + if err != nil { + return []byte(fmt.Sprintf("%v", data)) + } + return buf.Bytes() +} + +func clearScreen(sb *screenbuf.ScreenBuf) { + sb.Reset() + sb.Clear() + sb.Flush() +} diff --git a/vendor/github.com/manifoldco/promptui/styles.go b/vendor/github.com/manifoldco/promptui/styles.go new file mode 100644 index 000000000..d7698c9cf --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/styles.go @@ -0,0 +1,23 @@ +// +build !windows + +package promptui + +// These are the default icons used by promptui for select and prompts. These should not be overridden and instead +// customized through the use of custom templates +var ( + // IconInitial is the icon used when starting in prompt mode and the icon next to the label when + // starting in select mode. + IconInitial = Styler(FGBlue)("?") + + // IconGood is the icon used when a good answer is entered in prompt mode. + IconGood = Styler(FGGreen)("✔") + + // IconWarn is the icon used when a good, but potentially invalid answer is entered in prompt mode. + IconWarn = Styler(FGYellow)("⚠") + + // IconBad is the icon used when a bad answer is entered in prompt mode. + IconBad = Styler(FGRed)("✗") + + // IconSelect is the icon used to identify the currently selected item in select mode. + IconSelect = Styler(FGBold)("▸") +) diff --git a/vendor/github.com/manifoldco/promptui/styles_windows.go b/vendor/github.com/manifoldco/promptui/styles_windows.go new file mode 100644 index 000000000..36de268a6 --- /dev/null +++ b/vendor/github.com/manifoldco/promptui/styles_windows.go @@ -0,0 +1,21 @@ +package promptui + +// These are the default icons used bu promptui for select and prompts. They can either be overridden directly +// from these variable or customized through the use of custom templates +var ( + // IconInitial is the icon used when starting in prompt mode and the icon next to the label when + // starting in select mode. + IconInitial = Styler(FGBlue)("?") + + // IconGood is the icon used when a good answer is entered in prompt mode. + IconGood = Styler(FGGreen)("v") + + // IconWarn is the icon used when a good, but potentially invalid answer is entered in prompt mode. + IconWarn = Styler(FGYellow)("!") + + // IconBad is the icon used when a bad answer is entered in prompt mode. + IconBad = Styler(FGRed)("x") + + // IconSelect is the icon used to identify the currently selected item in select mode. + IconSelect = Styler(FGBold)(">") +) |