Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed cursorless echo after input finished #118

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions _examples/select_add/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ func main() {

for index < 0 {
prompt := promptui.SelectWithAdd{
Label: "What's your text editor",
Items: items,
AddLabel: "Other",
Label: "What's your text editor",
Items: items,
AddSelectLabel: "Other",
}

index, result, err = prompt.Run()
Expand Down
10 changes: 9 additions & 1 deletion cursor.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package promptui

import "fmt"
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.
Expand Down Expand Up @@ -144,6 +147,11 @@ 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) {
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ require (
//
// After that point, we should be able to remove it.
exclude gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c

go 1.13
4 changes: 2 additions & 2 deletions prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ func (p *Prompt) Run() (string, error) {
return "", err
}

echo := cur.Format()
echo := cur.Get()
if p.Mask != 0 {
echo = cur.FormatMask(p.Mask)
echo = cur.GetMask(p.Mask)
}

prompt := render(p.Templates.success, p.Label)
Expand Down
133 changes: 96 additions & 37 deletions select.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"reflect"
"text/template"

"github.com/chzyer/readline"
Expand Down Expand Up @@ -486,29 +487,75 @@ func (s *Select) prepareTemplates() error {
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
//
// 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. Each item will be listed individually with the
// AddLabel as the first item of the list.
Items []string
// 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{}

// AddLabel is the label used for the first item of the list that enables adding a new item.
// AddSelectLabel 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
// AddLabel must use the same type as Items.
AddSelectLabel interface{}

// 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
// AddPromptLabel is the prompt label once the Add option is selected.
// Defaults to the AddSelectLabel if not set.
AddPromptLabel interface{}

// 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.
AddPromptTemplate *PromptTemplates

// AddOnBottom puts the Add option on the bottom of the list.
AddOnBottom bool

// Size is the number of items that should appear on the select before scrolling is necessary. Defaults to 5.
Size 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

// a function that defines how to render the cursor
Pointer Pointer

// 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

// 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

// a function that defines how to render the cursor
Pointer Pointer
}

// Run executes the select list. Its displays the label and the list of items, asking the user to chose any
Expand All @@ -519,41 +566,53 @@ type SelectWithAdd struct {
// 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
}
if sa.Items == nil || reflect.TypeOf(sa.Items).Kind() != reflect.Slice {
return 0, "", fmt.Errorf("items %v is not a slice", sa.Items)
}
it := reflect.ValueOf(sa.Items)
items := make([]interface{}, it.Len()+1)
var offset int
if sa.AddOnBottom {
items[len(items)-1] = sa.AddSelectLabel
offset = 0
} else {
items[0] = sa.AddSelectLabel
offset = 1
}
for i := 0; i < it.Len(); i++ {
items[i+offset] = it.Index(i).Interface()
}

if it.Len() > 0 {
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
Label: sa.Label,
Items: items,
IsVimMode: sa.IsVimMode,
HideHelp: sa.HideHelp,
Size: sa.Size,
HideSelected: sa.HideSelected,
Templates: sa.Templates,
Keys: sa.Keys,
Searcher: sa.Searcher,
StartInSearchMode: sa.StartInSearchMode,
Pointer: sa.Pointer,
}

selected, value, err := s.innerRun(1, 0, '+')
if err != nil || selected != 0 {
return selected - 1, value, err
selected, value, err := s.RunCursorAt(offset, 0)
// check if selected the Add option.
if err != nil || selected != 0 && !sa.AddOnBottom || selected != len(items) && sa.AddOnBottom {
return selected - offset, value, err
}

// XXX run through terminal for windows
os.Stdout.Write([]byte(upLine(1) + "\r" + clearLine))
}

if sa.AddPromptLabel == nil {
sa.AddPromptLabel = sa.AddSelectLabel
}
p := Prompt{
Label: sa.AddLabel,
Label: sa.AddPromptLabel,
Templates: sa.AddPromptTemplate,
Validate: sa.Validate,
IsVimMode: sa.IsVimMode,
Pointer: sa.Pointer,
Expand Down