Commited dependencies
This commit is contained in:
1
vendor/github.com/manifoldco/promptui/.gitignore
generated
vendored
Normal file
1
vendor/github.com/manifoldco/promptui/.gitignore
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
vendor
|
||||
10
vendor/github.com/manifoldco/promptui/.travis.yml
generated
vendored
Normal file
10
vendor/github.com/manifoldco/promptui/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
install: make bootstrap
|
||||
30
vendor/github.com/manifoldco/promptui/CHANGELOG.md
generated
vendored
Normal file
30
vendor/github.com/manifoldco/promptui/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# 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.2.1] - 2017-11-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- `SelectWithAdd` panicking on `.Run` due to lack of keys setup
|
||||
|
||||
## [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).
|
||||
129
vendor/github.com/manifoldco/promptui/Gopkg.lock
generated
vendored
Normal file
129
vendor/github.com/manifoldco/promptui/Gopkg.lock
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/alecthomas/gometalinter"
|
||||
packages = ["."]
|
||||
revision = "bae2f1293d092fd8167939d5108d1b025eaef9de"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/alecthomas/units"
|
||||
packages = ["."]
|
||||
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/chzyer/readline"
|
||||
packages = ["."]
|
||||
revision = "6a4bc7b4feaeff8feb63f87d5fb2cf3e3610a559"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/client9/misspell"
|
||||
packages = [".","cmd/misspell"]
|
||||
revision = "59894abde931a32630d4e884a09c682ed20c5c7c"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golang/lint"
|
||||
packages = [".","golint"]
|
||||
revision = "6aaf7c34af0f4c36a57e0c429bace4d706d8e931"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/google/shlex"
|
||||
packages = ["."]
|
||||
revision = "6f45313302b9c56850fc17f99e40caebce98c716"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/gordonklaus/ineffassign"
|
||||
packages = ["."]
|
||||
revision = "da3d65debb9bff70fcfb6f277a8db31e8200de20"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/juju/ansiterm"
|
||||
packages = [".","tabwriter"]
|
||||
revision = "35c59b9e0fe275705a71bf5d58ee293f27efbbc4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kisielk/gotool"
|
||||
packages = [".","internal/load"]
|
||||
revision = "d6ce6262d87e3a4e153e86023ff56ae771554a41"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/lunixbochs/vtclean"
|
||||
packages = ["."]
|
||||
revision = "d14193dfc626125c831501c1c42340b4248e1f5a"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-colorable"
|
||||
packages = ["."]
|
||||
revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072"
|
||||
version = "v0.0.9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mattn/go-isatty"
|
||||
packages = ["."]
|
||||
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
|
||||
version = "v0.0.3"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/nicksnyder/go-i18n"
|
||||
packages = ["i18n","i18n/bundle","i18n/language","i18n/translation"]
|
||||
revision = "ca33e78c8a430e2df435b02f63a3944fa8e9ea11"
|
||||
version = "v1.9.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pelletier/go-toml"
|
||||
packages = ["."]
|
||||
revision = "16398bac157da96aa88f98a2df640c7f32af1da2"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/tsenart/deadcode"
|
||||
packages = ["."]
|
||||
revision = "210d2dc333e90c7e3eedf4f2242507a8e83ed4ab"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "686000749eaec0b8855b8eef5336cf63899fe51d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/tools"
|
||||
packages = ["go/ast/astutil","go/buildutil","go/gcexportdata","go/gcimporter15","go/loader","go/types/typeutil"]
|
||||
revision = "9bd2f442688b66c5289262d70f537c2ecf81d7de"
|
||||
|
||||
[[projects]]
|
||||
branch = "v3-unstable"
|
||||
name = "gopkg.in/alecthomas/kingpin.v3-unstable"
|
||||
packages = ["."]
|
||||
revision = "63abe20a23e29e80bbef8089bd3dee3ac25e5306"
|
||||
|
||||
[[projects]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
|
||||
|
||||
[[projects]]
|
||||
name = "honnef.co/go/tools"
|
||||
packages = ["cmd/gosimple","internal/sharedcheck","lint","lint/lintutil","simple","ssa","ssa/ssautil","version"]
|
||||
revision = "e5147431c7c056bd6ef33ad2f23c846f835571dd"
|
||||
version = "2017.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "160169cbbea61d01890b2670b2cc5448b5b8dd6ebbb18f57bd677c0c7d1f8a42"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
16
vendor/github.com/manifoldco/promptui/Gopkg.toml
generated
vendored
Normal file
16
vendor/github.com/manifoldco/promptui/Gopkg.toml
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
required=[
|
||||
"honnef.co/go/tools/cmd/gosimple",
|
||||
"github.com/alecthomas/gometalinter",
|
||||
"github.com/client9/misspell/cmd/misspell",
|
||||
"github.com/golang/lint/golint",
|
||||
"github.com/gordonklaus/ineffassign",
|
||||
"github.com/tsenart/deadcode",
|
||||
]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/chzyer/readline"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/juju/master"
|
||||
branch = "master"
|
||||
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.
|
||||
55
vendor/github.com/manifoldco/promptui/Makefile
generated
vendored
Normal file
55
vendor/github.com/manifoldco/promptui/Makefile
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
LINTERS=\
|
||||
gofmt \
|
||||
golint \
|
||||
gosimple \
|
||||
vet \
|
||||
misspell \
|
||||
ineffassign \
|
||||
deadcode
|
||||
|
||||
ci: $(LINTERS) test
|
||||
|
||||
.PHONY: ci
|
||||
|
||||
#################################################
|
||||
# Bootstrapping for base golang package deps
|
||||
#################################################
|
||||
|
||||
CMD_PKGS=\
|
||||
github.com/golang/lint/golint \
|
||||
honnef.co/go/tools/cmd/gosimple \
|
||||
github.com/client9/misspell/cmd/misspell \
|
||||
github.com/gordonklaus/ineffassign \
|
||||
github.com/tsenart/deadcode \
|
||||
github.com/alecthomas/gometalinter
|
||||
|
||||
define VENDOR_BIN_TMPL
|
||||
vendor/bin/$(notdir $(1)): vendor
|
||||
go build -o $$@ ./vendor/$(1)
|
||||
VENDOR_BINS += vendor/bin/$(notdir $(1))
|
||||
endef
|
||||
|
||||
$(foreach cmd_pkg,$(CMD_PKGS),$(eval $(call VENDOR_BIN_TMPL,$(cmd_pkg))))
|
||||
$(patsubst %,%-bin,$(filter-out gofmt vet,$(LINTERS))): %-bin: vendor/bin/%
|
||||
gofmt-bin vet-bin:
|
||||
|
||||
bootstrap:
|
||||
which dep || go get -u github.com/golang/dep/cmd/dep
|
||||
|
||||
vendor: Gopkg.lock
|
||||
dep ensure
|
||||
|
||||
.PHONY: bootstrap $(CMD_PKGS)
|
||||
|
||||
#################################################
|
||||
# Test and linting
|
||||
#################################################
|
||||
|
||||
test: vendor
|
||||
@CGO_ENABLED=0 go test -v $$(go list ./... | grep -v vendor)
|
||||
|
||||
$(LINTERS): %: vendor/bin/gometalinter %-bin vendor
|
||||
PATH=`pwd`/vendor/bin:$$PATH gometalinter --tests --disable-all --vendor \
|
||||
--deadline=5m -s data ./... --enable $@
|
||||
|
||||
.PHONY: $(LINTERS) test
|
||||
106
vendor/github.com/manifoldco/promptui/README.md
generated
vendored
Normal file
106
vendor/github.com/manifoldco/promptui/README.md
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
# 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)
|
||||
88
vendor/github.com/manifoldco/promptui/codes.go
generated
vendored
Normal file
88
vendor/github.com/manifoldco/promptui/codes.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
package promptui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const esc = "\033["
|
||||
|
||||
type attribute int
|
||||
|
||||
// Foreground weight/decoration attributes.
|
||||
const (
|
||||
reset attribute = iota
|
||||
|
||||
FGBold
|
||||
FGFaint
|
||||
FGItalic
|
||||
FGUnderline
|
||||
)
|
||||
|
||||
// Foreground color attributes
|
||||
const (
|
||||
FGBlack attribute = iota + 30
|
||||
FGRed
|
||||
FGGreen
|
||||
FGYellow
|
||||
FGBlue
|
||||
FGMagenta
|
||||
FGCyan
|
||||
FGWhite
|
||||
)
|
||||
|
||||
// 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.
|
||||
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),
|
||||
"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 returns a func that applies the attributes given in the Styler call
|
||||
// to the provided string.
|
||||
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)
|
||||
}
|
||||
}
|
||||
197
vendor/github.com/manifoldco/promptui/list/list.go
generated
vendored
Normal file
197
vendor/github.com/manifoldco/promptui/list/list.go
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
package list
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Searcher can be implemented to allow the list to search for results.
|
||||
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. Items must be a slice type and size must
|
||||
// be greater than 0.
|
||||
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 method for that.
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
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
|
||||
}
|
||||
325
vendor/github.com/manifoldco/promptui/prompt.go
generated
vendored
Normal file
325
vendor/github.com/manifoldco/promptui/prompt.go
generated
vendored
Normal file
@@ -0,0 +1,325 @@
|
||||
package promptui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/chzyer/readline"
|
||||
"github.com/manifoldco/promptui/screenbuf"
|
||||
)
|
||||
|
||||
const cursor = "\u2588"
|
||||
|
||||
// Prompt represents a single line text field input.
|
||||
type Prompt struct {
|
||||
// Label is the value displayed on the command line prompt. It can be any
|
||||
// value one would pass to a text/template Execute(), including just a string.
|
||||
Label interface{}
|
||||
|
||||
Default string // Default is the initial value to populate in the prompt
|
||||
|
||||
// Validate is optional. If set, this function is used to validate the input
|
||||
// after each character entry.
|
||||
Validate ValidateFunc
|
||||
|
||||
// If mask is set, this value is displayed instead of the actual input
|
||||
// characters.
|
||||
Mask rune
|
||||
|
||||
// Templates can be used to customize the prompt output. If nil is passed, the
|
||||
// default templates are used.
|
||||
Templates *PromptTemplates
|
||||
|
||||
IsConfirm bool
|
||||
IsVimMode bool
|
||||
|
||||
stdin io.Reader
|
||||
stdout io.Writer
|
||||
}
|
||||
|
||||
// PromptTemplates allow a prompt to be customized following stdlib
|
||||
// text/template syntax. If any field is blank a default template is used.
|
||||
type PromptTemplates struct {
|
||||
// Prompt is a text/template for the initial prompt question.
|
||||
Prompt string
|
||||
|
||||
// Prompt is a text/template if the prompt is a confirmation.
|
||||
Confirm string
|
||||
|
||||
// Valid is a text/template for when the current input is valid.
|
||||
Valid string
|
||||
|
||||
// Invalid is a text/template for when the current input is invalid.
|
||||
Invalid string
|
||||
|
||||
// Success is a text/template for the successful result.
|
||||
Success string
|
||||
|
||||
// Prompt is a text/template when there is a validation error.
|
||||
ValidationError string
|
||||
|
||||
// FuncMap is a map of helpers for the templates. If nil, the default helpers
|
||||
// are used.
|
||||
FuncMap template.FuncMap
|
||||
|
||||
prompt *template.Template
|
||||
valid *template.Template
|
||||
invalid *template.Template
|
||||
validation *template.Template
|
||||
success *template.Template
|
||||
}
|
||||
|
||||
// Run runs the prompt, returning the validated input.
|
||||
func (p *Prompt) Run() (string, error) {
|
||||
c := &readline.Config{}
|
||||
err := c.Init()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = p.prepareTemplates()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if p.stdin != nil {
|
||||
c.Stdin = p.stdin
|
||||
}
|
||||
|
||||
if p.stdout != nil {
|
||||
c.Stdout = p.stdout
|
||||
}
|
||||
|
||||
if p.Mask != 0 {
|
||||
c.EnableMask = true
|
||||
c.MaskRune = p.Mask
|
||||
}
|
||||
|
||||
if p.IsVimMode {
|
||||
c.VimMode = true
|
||||
}
|
||||
|
||||
c.HistoryLimit = -1
|
||||
c.UniqueEditLine = true
|
||||
|
||||
rl, err := readline.NewEx(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
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
|
||||
eraseDefault := input != ""
|
||||
|
||||
c.SetListener(func(line []rune, pos int, key rune) ([]rune, int, bool) {
|
||||
if line != nil {
|
||||
input += string(line)
|
||||
}
|
||||
|
||||
switch key {
|
||||
case 0: // empty
|
||||
case readline.CharEnter:
|
||||
return nil, 0, false
|
||||
case readline.CharBackspace:
|
||||
if eraseDefault {
|
||||
eraseDefault = false
|
||||
input = ""
|
||||
}
|
||||
if len(input) > 0 {
|
||||
input = input[:len(input)-1]
|
||||
}
|
||||
default:
|
||||
if eraseDefault {
|
||||
eraseDefault = false
|
||||
input = string(line)
|
||||
}
|
||||
}
|
||||
|
||||
err := validFn(input)
|
||||
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 := input
|
||||
if p.Mask != 0 {
|
||||
echo = strings.Repeat(string(p.Mask), len(echo))
|
||||
}
|
||||
|
||||
prompt = append(prompt, []byte(echo+cursor)...)
|
||||
|
||||
sb.Reset()
|
||||
sb.Write(prompt)
|
||||
|
||||
if inputErr != nil {
|
||||
validation := render(p.Templates.validation, inputErr)
|
||||
sb.Write(validation)
|
||||
inputErr = nil
|
||||
}
|
||||
|
||||
sb.Flush()
|
||||
|
||||
return nil, 0, true
|
||||
})
|
||||
|
||||
for {
|
||||
_, err = rl.Readline()
|
||||
|
||||
inputErr = validFn(input)
|
||||
if inputErr == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
switch err {
|
||||
case readline.ErrInterrupt:
|
||||
err = ErrInterrupt
|
||||
case io.EOF:
|
||||
err = ErrEOF
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err.Error() == "Interrupt" {
|
||||
err = ErrInterrupt
|
||||
}
|
||||
sb.Reset()
|
||||
sb.WriteString("")
|
||||
sb.Flush()
|
||||
rl.Write([]byte(showCursor))
|
||||
rl.Close()
|
||||
return "", err
|
||||
}
|
||||
|
||||
echo := input
|
||||
if p.Mask != 0 {
|
||||
echo = strings.Repeat(string(p.Mask), len(echo))
|
||||
}
|
||||
|
||||
prompt := render(p.Templates.valid, p.Label)
|
||||
prompt = append(prompt, []byte(echo)...)
|
||||
|
||||
if p.IsConfirm && strings.ToLower(echo) != "y" {
|
||||
prompt = render(p.Templates.invalid, p.Label)
|
||||
err = ErrAbort
|
||||
}
|
||||
|
||||
sb.Reset()
|
||||
sb.Write(prompt)
|
||||
sb.Flush()
|
||||
rl.Write([]byte(showCursor))
|
||||
rl.Close()
|
||||
|
||||
return input, 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 {
|
||||
p.Default = ""
|
||||
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 = `{{ . | faint }}`
|
||||
}
|
||||
|
||||
tpl, err = template.New("").Funcs(tpls.FuncMap).Parse(tpls.Success)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tpls.success = tpl
|
||||
|
||||
p.Templates = tpls
|
||||
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/manifoldco/promptui/promptui.go
generated
vendored
Normal file
18
vendor/github.com/manifoldco/promptui/promptui.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// Package promptui provides ui elements for the command line prompt.
|
||||
package promptui
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrEOF is returned from prompts when EOF is encountered.
|
||||
var ErrEOF = errors.New("^D")
|
||||
|
||||
// ErrInterrupt is returned from prompts when an interrupt (ctrl-c) is
|
||||
// encountered.
|
||||
var ErrInterrupt = errors.New("^C")
|
||||
|
||||
// ErrAbort is returned when confirm prompts are supplied "n"
|
||||
var ErrAbort = errors.New("")
|
||||
|
||||
// ValidateFunc validates the given input. It should return a ValidationError
|
||||
// if the input is not valid.
|
||||
type ValidateFunc func(string) error
|
||||
138
vendor/github.com/manifoldco/promptui/screenbuf/screenbuf.go
generated
vendored
Normal file
138
vendor/github.com/manifoldco/promptui/screenbuf/screenbuf.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
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
|
||||
flush 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
|
||||
}
|
||||
|
||||
// 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 fail 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 {
|
||||
for i := 0; i < s.height; i++ {
|
||||
_, err := s.buf.Write(moveUp)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, err = s.buf.Write(clearLine)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
s.cursor = 0
|
||||
s.height = 0
|
||||
s.reset = false
|
||||
}
|
||||
|
||||
switch {
|
||||
case s.cursor == s.height:
|
||||
n, err := s.buf.Write(clearLine)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
line := append(b, []byte("\n")...)
|
||||
n, err = s.buf.Write(line)
|
||||
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.
|
||||
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))
|
||||
}
|
||||
500
vendor/github.com/manifoldco/promptui/select.go
generated
vendored
Normal file
500
vendor/github.com/manifoldco/promptui/select.go
generated
vendored
Normal file
@@ -0,0 +1,500 @@
|
||||
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 returned from SelectWithAdd when add is selected.
|
||||
const SelectedAdd = -1
|
||||
|
||||
// Select represents a list for selecting a single item
|
||||
type Select struct {
|
||||
// Label is the value displayed on the command line prompt. It can be any
|
||||
// value one would pass to a text/template Execute(), including just a string.
|
||||
Label interface{}
|
||||
|
||||
// Items are the items to use in the list. It can be any slice type one would
|
||||
// pass to a text/template execute, including a string slice.
|
||||
Items interface{}
|
||||
|
||||
// Size is the number of items that should appear on the select before
|
||||
// scrolling. If it is 0, defaults to 5.
|
||||
Size int
|
||||
|
||||
// IsVimMode sets whether readline is using Vim mode.
|
||||
IsVimMode bool
|
||||
|
||||
// Templates can be used to customize the select output. If nil is passed, the
|
||||
// default templates are used.
|
||||
Templates *SelectTemplates
|
||||
|
||||
// Keys can be used to change movement and search keys.
|
||||
Keys *SelectKeys
|
||||
|
||||
// Searcher can be implemented to teach the select how to search for items.
|
||||
Searcher list.Searcher
|
||||
|
||||
label string
|
||||
|
||||
list *list.List
|
||||
}
|
||||
|
||||
// SelectKeys defines which keys can be used for movement and search.
|
||||
type SelectKeys struct {
|
||||
Next Key // Next defaults to down arrow key
|
||||
Prev Key // Prev defaults to up arrow key
|
||||
PageUp Key // PageUp defaults to left arrow key
|
||||
PageDown Key // PageDown defaults to right arrow key
|
||||
Search Key // Search defaults to '/' key
|
||||
}
|
||||
|
||||
// Key defines a keyboard code and a display representation for the help
|
||||
// Check https://github.com/chzyer/readline for a list of codes
|
||||
type Key struct {
|
||||
Code rune
|
||||
Display string
|
||||
}
|
||||
|
||||
// SelectTemplates allow a select prompt to be customized following stdlib
|
||||
// text/template syntax. If any field is blank a default template is used.
|
||||
type SelectTemplates struct {
|
||||
// Active is a text/template for the label.
|
||||
Label string
|
||||
|
||||
// Active is a text/template for when an item is current active.
|
||||
Active string
|
||||
|
||||
// Inactive is a text/template for when an item is not current active.
|
||||
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.
|
||||
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 helpers for the templates. If nil, the default helpers
|
||||
// are used.
|
||||
FuncMap template.FuncMap
|
||||
|
||||
label *template.Template
|
||||
active *template.Template
|
||||
inactive *template.Template
|
||||
selected *template.Template
|
||||
details *template.Template
|
||||
help *template.Template
|
||||
}
|
||||
|
||||
// Run runs the Select list. It returns the index of the selected element,
|
||||
// and its value.
|
||||
func (s *Select) Run() (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(0, ' ')
|
||||
}
|
||||
|
||||
func (s *Select) innerRun(starting int, top rune) (int, string, error) {
|
||||
stdin := readline.NewCancelableStdin(os.Stdin)
|
||||
c := &readline.Config{}
|
||||
err := c.Init()
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
c.Stdin = 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)
|
||||
|
||||
var searchInput []rune
|
||||
canSearch := s.Searcher != nil
|
||||
searchMode := false
|
||||
|
||||
c.SetListener(func(line []rune, pos int, key rune) ([]rune, int, bool) {
|
||||
switch {
|
||||
case key == readline.CharEnter:
|
||||
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
|
||||
searchInput = nil
|
||||
s.list.CancelSearch()
|
||||
} else {
|
||||
searchMode = true
|
||||
}
|
||||
case key == readline.CharBackspace:
|
||||
if !canSearch || !searchMode {
|
||||
break
|
||||
}
|
||||
|
||||
if len(searchInput) > 1 {
|
||||
searchInput = searchInput[:len(searchInput)-1]
|
||||
s.list.Search(string(searchInput))
|
||||
} else {
|
||||
searchInput = nil
|
||||
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 {
|
||||
searchInput = append(searchInput, line...)
|
||||
s.list.Search(string(searchInput))
|
||||
}
|
||||
}
|
||||
|
||||
if searchMode {
|
||||
header := fmt.Sprintf("Search: %s%s", string(searchInput), cursor)
|
||||
sb.WriteString(header)
|
||||
} else {
|
||||
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]
|
||||
|
||||
output := render(s.Templates.selected, item)
|
||||
|
||||
sb.Reset()
|
||||
sb.Write(output)
|
||||
sb.Flush()
|
||||
rl.Write([]byte(showCursor))
|
||||
rl.Close()
|
||||
|
||||
return s.list.Index(), fmt.Sprintf("%v", item), err
|
||||
}
|
||||
|
||||
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, or selecting
|
||||
// a newly created item.
|
||||
type SelectWithAdd struct {
|
||||
Label string // Label is the value displayed on the command line prompt.
|
||||
Items []string // Items are the items to use in the list.
|
||||
|
||||
AddLabel string // The label used in the item list for creating a new item.
|
||||
|
||||
// Validate is optional. If set, this function is used to validate the input
|
||||
// after each character entry.
|
||||
Validate ValidateFunc
|
||||
|
||||
IsVimMode bool // Whether readline is using Vim mode.
|
||||
}
|
||||
|
||||
// Run runs the Select list. It returns the index of the selected element,
|
||||
// and its value. If a new element is created, -1 is returned as the index.
|
||||
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,
|
||||
Size: 5,
|
||||
list: list,
|
||||
}
|
||||
s.setKeys()
|
||||
|
||||
err = s.prepareTemplates()
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
selected, value, err := s.innerRun(1, '+')
|
||||
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,
|
||||
}
|
||||
value, err := p.Run()
|
||||
return SelectedAdd, value, err
|
||||
}
|
||||
|
||||
func (s *Select) setKeys() {
|
||||
if s.Keys != nil {
|
||||
return
|
||||
}
|
||||
s.Keys = &SelectKeys{
|
||||
Prev: Key{Code: readline.CharPrev, Display: "↑"},
|
||||
Next: Key{Code: readline.CharNext, Display: "↓"},
|
||||
PageUp: Key{Code: readline.CharBackward, Display: "←"},
|
||||
PageDown: Key{Code: readline.CharForward, Display: "→"},
|
||||
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()
|
||||
}
|
||||
12
vendor/github.com/manifoldco/promptui/styles.go
generated
vendored
Normal file
12
vendor/github.com/manifoldco/promptui/styles.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build !windows
|
||||
|
||||
package promptui
|
||||
|
||||
// Icons used for displaying prompts or status
|
||||
var (
|
||||
IconInitial = Styler(FGBlue)("?")
|
||||
IconGood = Styler(FGGreen)("✔")
|
||||
IconWarn = Styler(FGYellow)("⚠")
|
||||
IconBad = Styler(FGRed)("✗")
|
||||
IconSelect = Styler(FGBold)("▸")
|
||||
)
|
||||
10
vendor/github.com/manifoldco/promptui/styles_windows.go
generated
vendored
Normal file
10
vendor/github.com/manifoldco/promptui/styles_windows.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
package promptui
|
||||
|
||||
// Icons used for displaying prompts or status
|
||||
var (
|
||||
IconInitial = Styler(FGBlue)("?")
|
||||
IconGood = Styler(FGGreen)("v")
|
||||
IconWarn = Styler(FGYellow)("!")
|
||||
IconBad = Styler(FGRed)("x")
|
||||
IconSelect = Styler(FGBold)(">")
|
||||
)
|
||||
Reference in New Issue
Block a user