Skip to content

Commit

Permalink
Prompt improve default value (#21)
Browse files Browse the repository at this point in the history
Now any key press other than <Enter> will clear the default value for the prompt.
  • Loading branch information
҈҈҈Luiz Branco committed Nov 2, 2017
1 parent ae1428a commit d47448a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 136 deletions.
4 changes: 0 additions & 4 deletions codes.go
Expand Up @@ -63,10 +63,6 @@ func upLine(n uint) string {
return movementCode(n, 'A')
}

func downLine(n uint) string {
return movementCode(n, 'B')
}

func movementCode(n uint, code rune) string {
return esc + strconv.FormatUint(uint64(n), 10) + string(code)
}
Expand Down
133 changes: 63 additions & 70 deletions prompt.go
@@ -1,16 +1,17 @@
package promptui

import (
"bytes"
"fmt"
"io"
"os"
"strings"
"text/template"

"github.com/chzyer/readline"
"github.com/manifoldco/promptui/screenbuf"
)

const cursor = "\u258f"

// 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
Expand Down Expand Up @@ -100,28 +101,17 @@ func (p *Prompt) Run() (string, error) {
c.VimMode = true
}

prompt := render(p.Templates.prompt, p.Label)

c.Prompt = prompt
c.HistoryLimit = -1
c.UniqueEditLine = true

firstListen := true
wroteErr := false
caughtup := true
var out string

if p.Default != "" {
caughtup = false
out = p.Default
c.Stdin = io.MultiReader(bytes.NewBuffer([]byte(out)), os.Stdin)
}

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
}
Expand All @@ -130,27 +120,36 @@ func (p *Prompt) Run() (string, error) {
validFn = p.Validate
}

var inputErr error
input := p.Default
eraseDefault := input != ""

c.SetListener(func(line []rune, pos int, key rune) ([]rune, int, bool) {
if key == readline.CharEnter {
return nil, 0, false
if line != nil {
input += string(line)
}

if firstListen {
firstListen = false
switch key {
case 0: // empty
case readline.CharEnter:
return nil, 0, false
}

if !caughtup && out != "" {
if string(line) == out {
caughtup = true
case readline.CharBackspace:
if eraseDefault {
eraseDefault = false
input = ""
}
if wroteErr {
return nil, 0, false
if len(input) > 0 {
input = input[:len(input)-1]
}
default:
if eraseDefault {
eraseDefault = false
input = string(line)
}
}

err := validFn(string(line))
var prompt string
err := validFn(input)
var prompt []byte

if err != nil {
prompt = render(p.Templates.invalid, p.Label)
Expand All @@ -161,18 +160,32 @@ func (p *Prompt) Run() (string, error) {
}
}

rl.SetPrompt(prompt)
rl.Refresh()
wroteErr = false
echo := input
if p.Mask != 0 {
echo = strings.Repeat(string(p.Mask), len(echo))
}

return nil, 0, false
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 {
out, err = rl.Readline()
_, err = rl.Readline()

oerr := validFn(out)
if oerr == nil {
inputErr = validFn(input)
if inputErr == nil {
break
}

Expand All @@ -183,52 +196,42 @@ func (p *Prompt) Run() (string, error) {
case io.EOF:
err = ErrEOF
}

break
}

caughtup = false

c.Stdin = io.MultiReader(bytes.NewBuffer([]byte(out)), os.Stdin)
rl, _ = readline.NewEx(c)

firstListen = true
wroteErr = true

validation := render(p.Templates.validation, oerr)
prompt := render(p.Templates.invalid, p.Label)

rl.SetPrompt("\n" + validation + upLine(1) + "\r" + prompt)
rl.Refresh()
}

if wroteErr {
rl.Write([]byte(downLine(1) + clearLine + upLine(1) + "\r"))
}

if err != nil {
if err.Error() == "Interrupt" {
err = ErrInterrupt
}
rl.Write([]byte("\n"))
sb.Reset()
sb.WriteString("")
sb.Flush()
rl.Write([]byte(showCursor))
rl.Close()
return "", err
}

echo := out
echo := input
if p.Mask != 0 {
echo = strings.Repeat(string(p.Mask), len(echo))
}

prompt = render(p.Templates.valid, p.Label)
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
}

rl.Write([]byte(prompt + render(p.Templates.success, echo) + "\n"))
sb.Reset()
sb.Write(prompt)
sb.Flush()
rl.Write([]byte(showCursor))
rl.Close()

return out, err
return input, err
}

func (p *Prompt) prepareTemplates() error {
Expand All @@ -242,7 +245,6 @@ func (p *Prompt) prepareTemplates() error {
}

bold := Styler(FGBold)
//faint := Styler(FGFaint)

if p.IsConfirm {
p.Default = ""
Expand Down Expand Up @@ -321,12 +323,3 @@ func (p *Prompt) prepareTemplates() error {

return nil
}

func render(tpl *template.Template, data interface{}) string {
var buf bytes.Buffer
err := tpl.Execute(&buf, data)
if err != nil {
return fmt.Sprintf("%v", data)
}
return buf.String()
}
43 changes: 0 additions & 43 deletions prompt_test.go

This file was deleted.

10 changes: 5 additions & 5 deletions select.go
Expand Up @@ -133,7 +133,7 @@ func (s *Select) innerRun(starting int, top rune) (int, string, error) {
s.list.PageUp()
}

label := renderBytes(s.Templates.label, s.Label)
label := render(s.Templates.label, s.Label)
sb.Write(label)

items, idx := s.list.Items()
Expand All @@ -158,9 +158,9 @@ func (s *Select) innerRun(starting int, top rune) (int, string, error) {
output := []byte(page + " ")

if i == idx {
output = append(output, renderBytes(s.Templates.active, item)...)
output = append(output, render(s.Templates.active, item)...)
} else {
output = append(output, renderBytes(s.Templates.inactive, item)...)
output = append(output, render(s.Templates.inactive, item)...)
}

sb.Write(output)
Expand Down Expand Up @@ -197,7 +197,7 @@ func (s *Select) innerRun(starting int, top rune) (int, string, error) {
items, idx := s.list.Items()
item := items[idx]

output := renderBytes(s.Templates.selected, item)
output := render(s.Templates.selected, item)

sb.Reset()
sb.Write(output)
Expand Down Expand Up @@ -353,7 +353,7 @@ func (s *Select) detailsOutput(item interface{}) [][]byte {
return bytes.Split(output, []byte("\n"))
}

func renderBytes(tpl *template.Template, data interface{}) []byte {
func render(tpl *template.Template, data interface{}) []byte {
var buf bytes.Buffer
err := tpl.Execute(&buf, data)
if err != nil {
Expand Down

0 comments on commit d47448a

Please sign in to comment.