11package promptui
22
33import (
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.
1516type 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
234237func (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- }
0 commit comments