Skip to content

Commit d47448a

Browse files
author
҈҈҈Luiz Branco
authored
Prompt improve default value (#21)
Now any key press other than <Enter> will clear the default value for the prompt.
1 parent ae1428a commit d47448a

File tree

5 files changed

+78
-136
lines changed

5 files changed

+78
-136
lines changed

codes.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,6 @@ func upLine(n uint) string {
6363
return movementCode(n, 'A')
6464
}
6565

66-
func downLine(n uint) string {
67-
return movementCode(n, 'B')
68-
}
69-
7066
func movementCode(n uint, code rune) string {
7167
return esc + strconv.FormatUint(uint64(n), 10) + string(code)
7268
}

prompt.go

Lines changed: 63 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package promptui
22

33
import (
4-
"bytes"
54
"fmt"
65
"io"
7-
"os"
86
"strings"
97
"text/template"
108

119
"github.com/chzyer/readline"
10+
"github.com/manifoldco/promptui/screenbuf"
1211
)
1312

13+
const cursor = "\u258f"
14+
1415
// Prompt represents a single line text field input.
1516
type Prompt struct {
1617
// Label is the value displayed on the command line prompt. It can be any
@@ -100,28 +101,17 @@ func (p *Prompt) Run() (string, error) {
100101
c.VimMode = true
101102
}
102103

103-
prompt := render(p.Templates.prompt, p.Label)
104-
105-
c.Prompt = prompt
106104
c.HistoryLimit = -1
107105
c.UniqueEditLine = true
108106

109-
firstListen := true
110-
wroteErr := false
111-
caughtup := true
112-
var out string
113-
114-
if p.Default != "" {
115-
caughtup = false
116-
out = p.Default
117-
c.Stdin = io.MultiReader(bytes.NewBuffer([]byte(out)), os.Stdin)
118-
}
119-
120107
rl, err := readline.NewEx(c)
121108
if err != nil {
122109
return "", err
123110
}
124111

112+
rl.Write([]byte(hideCursor))
113+
sb := screenbuf.New(rl)
114+
125115
validFn := func(x string) error {
126116
return nil
127117
}
@@ -130,27 +120,36 @@ func (p *Prompt) Run() (string, error) {
130120
validFn = p.Validate
131121
}
132122

123+
var inputErr error
124+
input := p.Default
125+
eraseDefault := input != ""
126+
133127
c.SetListener(func(line []rune, pos int, key rune) ([]rune, int, bool) {
134-
if key == readline.CharEnter {
135-
return nil, 0, false
128+
if line != nil {
129+
input += string(line)
136130
}
137131

138-
if firstListen {
139-
firstListen = false
132+
switch key {
133+
case 0: // empty
134+
case readline.CharEnter:
140135
return nil, 0, false
141-
}
142-
143-
if !caughtup && out != "" {
144-
if string(line) == out {
145-
caughtup = true
136+
case readline.CharBackspace:
137+
if eraseDefault {
138+
eraseDefault = false
139+
input = ""
146140
}
147-
if wroteErr {
148-
return nil, 0, false
141+
if len(input) > 0 {
142+
input = input[:len(input)-1]
143+
}
144+
default:
145+
if eraseDefault {
146+
eraseDefault = false
147+
input = string(line)
149148
}
150149
}
151150

152-
err := validFn(string(line))
153-
var prompt string
151+
err := validFn(input)
152+
var prompt []byte
154153

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

164-
rl.SetPrompt(prompt)
165-
rl.Refresh()
166-
wroteErr = false
163+
echo := input
164+
if p.Mask != 0 {
165+
echo = strings.Repeat(string(p.Mask), len(echo))
166+
}
167167

168-
return nil, 0, false
168+
prompt = append(prompt, []byte(echo+cursor)...)
169+
170+
sb.Reset()
171+
sb.Write(prompt)
172+
173+
if inputErr != nil {
174+
validation := render(p.Templates.validation, inputErr)
175+
sb.Write(validation)
176+
inputErr = nil
177+
}
178+
179+
sb.Flush()
180+
181+
return nil, 0, true
169182
})
170183

171184
for {
172-
out, err = rl.Readline()
185+
_, err = rl.Readline()
173186

174-
oerr := validFn(out)
175-
if oerr == nil {
187+
inputErr = validFn(input)
188+
if inputErr == nil {
176189
break
177190
}
178191

@@ -183,52 +196,42 @@ func (p *Prompt) Run() (string, error) {
183196
case io.EOF:
184197
err = ErrEOF
185198
}
186-
187199
break
188200
}
189-
190-
caughtup = false
191-
192-
c.Stdin = io.MultiReader(bytes.NewBuffer([]byte(out)), os.Stdin)
193-
rl, _ = readline.NewEx(c)
194-
195-
firstListen = true
196-
wroteErr = true
197-
198-
validation := render(p.Templates.validation, oerr)
199-
prompt := render(p.Templates.invalid, p.Label)
200-
201-
rl.SetPrompt("\n" + validation + upLine(1) + "\r" + prompt)
202-
rl.Refresh()
203-
}
204-
205-
if wroteErr {
206-
rl.Write([]byte(downLine(1) + clearLine + upLine(1) + "\r"))
207201
}
208202

209203
if err != nil {
210204
if err.Error() == "Interrupt" {
211205
err = ErrInterrupt
212206
}
213-
rl.Write([]byte("\n"))
207+
sb.Reset()
208+
sb.WriteString("")
209+
sb.Flush()
210+
rl.Write([]byte(showCursor))
211+
rl.Close()
214212
return "", err
215213
}
216214

217-
echo := out
215+
echo := input
218216
if p.Mask != 0 {
219217
echo = strings.Repeat(string(p.Mask), len(echo))
220218
}
221219

222-
prompt = render(p.Templates.valid, p.Label)
220+
prompt := render(p.Templates.valid, p.Label)
221+
prompt = append(prompt, []byte(echo)...)
223222

224223
if p.IsConfirm && strings.ToLower(echo) != "y" {
225224
prompt = render(p.Templates.invalid, p.Label)
226225
err = ErrAbort
227226
}
228227

229-
rl.Write([]byte(prompt + render(p.Templates.success, echo) + "\n"))
228+
sb.Reset()
229+
sb.Write(prompt)
230+
sb.Flush()
231+
rl.Write([]byte(showCursor))
232+
rl.Close()
230233

231-
return out, err
234+
return input, err
232235
}
233236

234237
func (p *Prompt) prepareTemplates() error {
@@ -242,7 +245,6 @@ func (p *Prompt) prepareTemplates() error {
242245
}
243246

244247
bold := Styler(FGBold)
245-
//faint := Styler(FGFaint)
246248

247249
if p.IsConfirm {
248250
p.Default = ""
@@ -321,12 +323,3 @@ func (p *Prompt) prepareTemplates() error {
321323

322324
return nil
323325
}
324-
325-
func render(tpl *template.Template, data interface{}) string {
326-
var buf bytes.Buffer
327-
err := tpl.Execute(&buf, data)
328-
if err != nil {
329-
return fmt.Sprintf("%v", data)
330-
}
331-
return buf.String()
332-
}

prompt_test.go

Lines changed: 0 additions & 43 deletions
This file was deleted.

select.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func (s *Select) innerRun(starting int, top rune) (int, string, error) {
133133
s.list.PageUp()
134134
}
135135

136-
label := renderBytes(s.Templates.label, s.Label)
136+
label := render(s.Templates.label, s.Label)
137137
sb.Write(label)
138138

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

160160
if i == idx {
161-
output = append(output, renderBytes(s.Templates.active, item)...)
161+
output = append(output, render(s.Templates.active, item)...)
162162
} else {
163-
output = append(output, renderBytes(s.Templates.inactive, item)...)
163+
output = append(output, render(s.Templates.inactive, item)...)
164164
}
165165

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

200-
output := renderBytes(s.Templates.selected, item)
200+
output := render(s.Templates.selected, item)
201201

202202
sb.Reset()
203203
sb.Write(output)
@@ -353,7 +353,7 @@ func (s *Select) detailsOutput(item interface{}) [][]byte {
353353
return bytes.Split(output, []byte("\n"))
354354
}
355355

356-
func renderBytes(tpl *template.Template, data interface{}) []byte {
356+
func render(tpl *template.Template, data interface{}) []byte {
357357
var buf bytes.Buffer
358358
err := tpl.Execute(&buf, data)
359359
if err != nil {

0 commit comments

Comments
 (0)