Add vendor dependencies as part git repo
This commit is contained in:
3
vendor/github.com/manifoldco/promptui/.gitignore
generated
vendored
Normal file
3
vendor/github.com/manifoldco/promptui/.gitignore
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
vendor
|
||||
all-cover.txt
|
||||
bin/
|
26
vendor/github.com/manifoldco/promptui/.golangci.yml
generated
vendored
Normal file
26
vendor/github.com/manifoldco/promptui/.golangci.yml
generated
vendored
Normal file
@ -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
|
14
vendor/github.com/manifoldco/promptui/.travis.yml
generated
vendored
Normal file
14
vendor/github.com/manifoldco/promptui/.travis.yml
generated
vendored
Normal file
@ -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
|
130
vendor/github.com/manifoldco/promptui/CHANGELOG.md
generated
vendored
Normal file
130
vendor/github.com/manifoldco/promptui/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
# 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.9.0] - 2021-10-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- Resolve license incompatibility in tabwriter
|
||||
|
||||
|
||||
## [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
|
73
vendor/github.com/manifoldco/promptui/CODE_OF_CONDUCT.md
generated
vendored
Normal file
73
vendor/github.com/manifoldco/promptui/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@ -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).
|
29
vendor/github.com/manifoldco/promptui/LICENSE.md
generated
vendored
Normal file
29
vendor/github.com/manifoldco/promptui/LICENSE.md
generated
vendored
Normal file
@ -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.
|
49
vendor/github.com/manifoldco/promptui/Makefile
generated
vendored
Normal file
49
vendor/github.com/manifoldco/promptui/Makefile
generated
vendored
Normal file
@ -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
|
107
vendor/github.com/manifoldco/promptui/README.md
generated
vendored
Normal file
107
vendor/github.com/manifoldco/promptui/README.md
generated
vendored
Normal file
@ -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)
|
||||
|
||||
[](https://github.com/manifoldco/promptui/releases)
|
||||
[](https://godoc.org/github.com/manifoldco/promptui)
|
||||
[](https://travis-ci.org/manifoldco/promptui)
|
||||
[](https://goreportcard.com/report/github.com/manifoldco/promptui)
|
||||
[](./LICENSE.md)
|
||||
|
||||
## Overview
|
||||
|
||||

|
||||
|
||||
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)
|
120
vendor/github.com/manifoldco/promptui/codes.go
generated
vendored
Normal file
120
vendor/github.com/manifoldco/promptui/codes.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
232
vendor/github.com/manifoldco/promptui/cursor.go
generated
vendored
Normal file
232
vendor/github.com/manifoldco/promptui/cursor.go
generated
vendored
Normal file
@ -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
|
||||
}
|
29
vendor/github.com/manifoldco/promptui/keycodes.go
generated
vendored
Normal file
29
vendor/github.com/manifoldco/promptui/keycodes.go
generated
vendored
Normal file
@ -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 = "→"
|
||||
)
|
10
vendor/github.com/manifoldco/promptui/keycodes_other.go
generated
vendored
Normal file
10
vendor/github.com/manifoldco/promptui/keycodes_other.go
generated
vendored
Normal file
@ -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
|
||||
)
|
10
vendor/github.com/manifoldco/promptui/keycodes_windows.go
generated
vendored
Normal file
10
vendor/github.com/manifoldco/promptui/keycodes_windows.go
generated
vendored
Normal file
@ -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
|
||||
)
|
237
vendor/github.com/manifoldco/promptui/list/list.go
generated
vendored
Normal file
237
vendor/github.com/manifoldco/promptui/list/list.go
generated
vendored
Normal file
@ -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
|
||||
}
|
341
vendor/github.com/manifoldco/promptui/prompt.go
generated
vendored
Normal file
341
vendor/github.com/manifoldco/promptui/prompt.go
generated
vendored
Normal file
@ -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
|
||||
}
|
27
vendor/github.com/manifoldco/promptui/promptui.go
generated
vendored
Normal file
27
vendor/github.com/manifoldco/promptui/promptui.go
generated
vendored
Normal file
@ -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
|
151
vendor/github.com/manifoldco/promptui/screenbuf/screenbuf.go
generated
vendored
Normal file
151
vendor/github.com/manifoldco/promptui/screenbuf/screenbuf.go
generated
vendored
Normal file
@ -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))
|
||||
}
|
638
vendor/github.com/manifoldco/promptui/select.go
generated
vendored
Normal file
638
vendor/github.com/manifoldco/promptui/select.go
generated
vendored
Normal file
@ -0,0 +1,638 @@
|
||||
package promptui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
|
||||
"github.com/chzyer/readline"
|
||||
"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 := tabwriter.NewWriter(&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()
|
||||
}
|
23
vendor/github.com/manifoldco/promptui/styles.go
generated
vendored
Normal file
23
vendor/github.com/manifoldco/promptui/styles.go
generated
vendored
Normal file
@ -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)("▸")
|
||||
)
|
21
vendor/github.com/manifoldco/promptui/styles_windows.go
generated
vendored
Normal file
21
vendor/github.com/manifoldco/promptui/styles_windows.go
generated
vendored
Normal file
@ -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)(">")
|
||||
)
|
Reference in New Issue
Block a user