1// +build windows linux darwin openbsd freebsd netbsd
2
3package liner
4
5import (
6	"bufio"
7	"container/ring"
8	"errors"
9	"fmt"
10	"io"
11	"os"
12	"strings"
13	"unicode"
14	"unicode/utf8"
15)
16
17type action int
18
19const (
20	left action = iota
21	right
22	up
23	down
24	home
25	end
26	insert
27	del
28	pageUp
29	pageDown
30	f1
31	f2
32	f3
33	f4
34	f5
35	f6
36	f7
37	f8
38	f9
39	f10
40	f11
41	f12
42	altB
43	altF
44	altY
45	shiftTab
46	wordLeft
47	wordRight
48	winch
49	unknown
50)
51
52const (
53	ctrlA = 1
54	ctrlB = 2
55	ctrlC = 3
56	ctrlD = 4
57	ctrlE = 5
58	ctrlF = 6
59	ctrlG = 7
60	ctrlH = 8
61	tab   = 9
62	lf    = 10
63	ctrlK = 11
64	ctrlL = 12
65	cr    = 13
66	ctrlN = 14
67	ctrlO = 15
68	ctrlP = 16
69	ctrlQ = 17
70	ctrlR = 18
71	ctrlS = 19
72	ctrlT = 20
73	ctrlU = 21
74	ctrlV = 22
75	ctrlW = 23
76	ctrlX = 24
77	ctrlY = 25
78	ctrlZ = 26
79	esc   = 27
80	bs    = 127
81)
82
83const (
84	beep = "\a"
85)
86
87type tabDirection int
88
89const (
90	tabForward tabDirection = iota
91	tabReverse
92)
93
94func (s *State) refresh(prompt []rune, buf []rune, pos int) error {
95	if s.columns == 0 {
96		return ErrInternal
97	}
98
99	s.needRefresh = false
100	if s.multiLineMode {
101		return s.refreshMultiLine(prompt, buf, pos)
102	}
103	return s.refreshSingleLine(prompt, buf, pos)
104}
105
106func (s *State) refreshSingleLine(prompt []rune, buf []rune, pos int) error {
107	s.cursorPos(0)
108	_, err := fmt.Print(string(prompt))
109	if err != nil {
110		return err
111	}
112
113	pLen := countGlyphs(prompt)
114	bLen := countGlyphs(buf)
115	pos = countGlyphs(buf[:pos])
116	if pLen+bLen < s.columns {
117		_, err = fmt.Print(string(buf))
118		s.eraseLine()
119		s.cursorPos(pLen + pos)
120	} else {
121		// Find space available
122		space := s.columns - pLen
123		space-- // space for cursor
124		start := pos - space/2
125		end := start + space
126		if end > bLen {
127			end = bLen
128			start = end - space
129		}
130		if start < 0 {
131			start = 0
132			end = space
133		}
134		pos -= start
135
136		// Leave space for markers
137		if start > 0 {
138			start++
139		}
140		if end < bLen {
141			end--
142		}
143		startRune := len(getPrefixGlyphs(buf, start))
144		line := getPrefixGlyphs(buf[startRune:], end-start)
145
146		// Output
147		if start > 0 {
148			fmt.Print("{")
149		}
150		fmt.Print(string(line))
151		if end < bLen {
152			fmt.Print("}")
153		}
154
155		// Set cursor position
156		s.eraseLine()
157		s.cursorPos(pLen + pos)
158	}
159	return err
160}
161
162func (s *State) refreshMultiLine(prompt []rune, buf []rune, pos int) error {
163	promptColumns := countMultiLineGlyphs(prompt, s.columns, 0)
164	totalColumns := countMultiLineGlyphs(buf, s.columns, promptColumns)
165	totalRows := (totalColumns + s.columns - 1) / s.columns
166	maxRows := s.maxRows
167	if totalRows > s.maxRows {
168		s.maxRows = totalRows
169	}
170	cursorRows := s.cursorRows
171	if cursorRows == 0 {
172		cursorRows = 1
173	}
174
175	/* First step: clear all the lines used before. To do so start by
176	* going to the last row. */
177	if maxRows-cursorRows > 0 {
178		s.moveDown(maxRows - cursorRows)
179	}
180
181	/* Now for every row clear it, go up. */
182	for i := 0; i < maxRows-1; i++ {
183		s.cursorPos(0)
184		s.eraseLine()
185		s.moveUp(1)
186	}
187
188	/* Clean the top line. */
189	s.cursorPos(0)
190	s.eraseLine()
191
192	/* Write the prompt and the current buffer content */
193	if _, err := fmt.Print(string(prompt)); err != nil {
194		return err
195	}
196	if _, err := fmt.Print(string(buf)); err != nil {
197		return err
198	}
199
200	/* If we are at the very end of the screen with our prompt, we need to
201	 * emit a newline and move the prompt to the first column. */
202	cursorColumns := countMultiLineGlyphs(buf[:pos], s.columns, promptColumns)
203	if cursorColumns == totalColumns && totalColumns%s.columns == 0 {
204		s.emitNewLine()
205		s.cursorPos(0)
206		totalRows++
207		if totalRows > s.maxRows {
208			s.maxRows = totalRows
209		}
210	}
211
212	/* Move cursor to right position. */
213	cursorRows = (cursorColumns + s.columns) / s.columns
214	if s.cursorRows > 0 && totalRows-cursorRows > 0 {
215		s.moveUp(totalRows - cursorRows)
216	}
217	/* Set column. */
218	s.cursorPos(cursorColumns % s.columns)
219
220	s.cursorRows = cursorRows
221	return nil
222}
223
224func (s *State) resetMultiLine(prompt []rune, buf []rune, pos int) {
225	columns := countMultiLineGlyphs(prompt, s.columns, 0)
226	columns = countMultiLineGlyphs(buf[:pos], s.columns, columns)
227	columns += 2 // ^C
228	cursorRows := (columns + s.columns) / s.columns
229	if s.maxRows-cursorRows > 0 {
230		for i := 0; i < s.maxRows-cursorRows; i++ {
231			fmt.Println() // always moves the cursor down or scrolls the window up as needed
232		}
233	}
234	s.maxRows = 1
235	s.cursorRows = 0
236}
237
238func longestCommonPrefix(strs []string) string {
239	if len(strs) == 0 {
240		return ""
241	}
242	longest := strs[0]
243
244	for _, str := range strs[1:] {
245		for !strings.HasPrefix(str, longest) {
246			longest = longest[:len(longest)-1]
247		}
248	}
249	// Remove trailing partial runes
250	longest = strings.TrimRight(longest, "\uFFFD")
251	return longest
252}
253
254func (s *State) circularTabs(items []string) func(tabDirection) (string, error) {
255	item := -1
256	return func(direction tabDirection) (string, error) {
257		if direction == tabForward {
258			if item < len(items)-1 {
259				item++
260			} else {
261				item = 0
262			}
263		} else if direction == tabReverse {
264			if item > 0 {
265				item--
266			} else {
267				item = len(items) - 1
268			}
269		}
270		return items[item], nil
271	}
272}
273
274func calculateColumns(screenWidth int, items []string) (numColumns, numRows, maxWidth int) {
275	for _, item := range items {
276		if len(item) >= screenWidth {
277			return 1, len(items), screenWidth - 1
278		}
279		if len(item) >= maxWidth {
280			maxWidth = len(item) + 1
281		}
282	}
283
284	numColumns = screenWidth / maxWidth
285	numRows = len(items) / numColumns
286	if len(items)%numColumns > 0 {
287		numRows++
288	}
289
290	if len(items) <= numColumns {
291		maxWidth = 0
292	}
293
294	return
295}
296
297func (s *State) printedTabs(items []string) func(tabDirection) (string, error) {
298	numTabs := 1
299	prefix := longestCommonPrefix(items)
300	return func(direction tabDirection) (string, error) {
301		if len(items) == 1 {
302			return items[0], nil
303		}
304
305		if numTabs == 2 {
306			if len(items) > 100 {
307				fmt.Printf("\nDisplay all %d possibilities? (y or n) ", len(items))
308			prompt:
309				for {
310					next, err := s.readNext()
311					if err != nil {
312						return prefix, err
313					}
314
315					if key, ok := next.(rune); ok {
316						switch key {
317						case 'n', 'N':
318							return prefix, nil
319						case 'y', 'Y':
320							break prompt
321						case ctrlC, ctrlD, cr, lf:
322							s.restartPrompt()
323						}
324					}
325				}
326			}
327			fmt.Println("")
328
329			numColumns, numRows, maxWidth := calculateColumns(s.columns, items)
330
331			for i := 0; i < numRows; i++ {
332				for j := 0; j < numColumns*numRows; j += numRows {
333					if i+j < len(items) {
334						if maxWidth > 0 {
335							fmt.Printf("%-*.[1]*s", maxWidth, items[i+j])
336						} else {
337							fmt.Printf("%v ", items[i+j])
338						}
339					}
340				}
341				fmt.Println("")
342			}
343		} else {
344			numTabs++
345		}
346		return prefix, nil
347	}
348}
349
350func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interface{}, error) {
351	if s.completer == nil {
352		return line, pos, rune(esc), nil
353	}
354	head, list, tail := s.completer(string(line), pos)
355	if len(list) <= 0 {
356		return line, pos, rune(esc), nil
357	}
358	hl := utf8.RuneCountInString(head)
359	if len(list) == 1 {
360		err := s.refresh(p, []rune(head+list[0]+tail), hl+utf8.RuneCountInString(list[0]))
361		return []rune(head + list[0] + tail), hl + utf8.RuneCountInString(list[0]), rune(esc), err
362	}
363
364	direction := tabForward
365	tabPrinter := s.circularTabs(list)
366	if s.tabStyle == TabPrints {
367		tabPrinter = s.printedTabs(list)
368	}
369
370	for {
371		pick, err := tabPrinter(direction)
372		if err != nil {
373			return line, pos, rune(esc), err
374		}
375		err = s.refresh(p, []rune(head+pick+tail), hl+utf8.RuneCountInString(pick))
376		if err != nil {
377			return line, pos, rune(esc), err
378		}
379
380		next, err := s.readNext()
381		if err != nil {
382			return line, pos, rune(esc), err
383		}
384		if key, ok := next.(rune); ok {
385			if key == tab {
386				direction = tabForward
387				continue
388			}
389			if key == esc {
390				return line, pos, rune(esc), nil
391			}
392		}
393		if a, ok := next.(action); ok && a == shiftTab {
394			direction = tabReverse
395			continue
396		}
397		return []rune(head + pick + tail), hl + utf8.RuneCountInString(pick), next, nil
398	}
399}
400
401// reverse intelligent search, implements a bash-like history search.
402func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, interface{}, error) {
403	p := "(reverse-i-search)`': "
404	err := s.refresh([]rune(p), origLine, origPos)
405	if err != nil {
406		return origLine, origPos, rune(esc), err
407	}
408
409	line := []rune{}
410	pos := 0
411	foundLine := string(origLine)
412	foundPos := origPos
413
414	getLine := func() ([]rune, []rune, int) {
415		search := string(line)
416		prompt := "(reverse-i-search)`%s': "
417		return []rune(fmt.Sprintf(prompt, search)), []rune(foundLine), foundPos
418	}
419
420	history, positions := s.getHistoryByPattern(string(line))
421	historyPos := len(history) - 1
422
423	for {
424		next, err := s.readNext()
425		if err != nil {
426			return []rune(foundLine), foundPos, rune(esc), err
427		}
428
429		switch v := next.(type) {
430		case rune:
431			switch v {
432			case ctrlR: // Search backwards
433				if historyPos > 0 && historyPos < len(history) {
434					historyPos--
435					foundLine = history[historyPos]
436					foundPos = positions[historyPos]
437				} else {
438					fmt.Print(beep)
439				}
440			case ctrlS: // Search forward
441				if historyPos < len(history)-1 && historyPos >= 0 {
442					historyPos++
443					foundLine = history[historyPos]
444					foundPos = positions[historyPos]
445				} else {
446					fmt.Print(beep)
447				}
448			case ctrlH, bs: // Backspace
449				if pos <= 0 {
450					fmt.Print(beep)
451				} else {
452					n := len(getSuffixGlyphs(line[:pos], 1))
453					line = append(line[:pos-n], line[pos:]...)
454					pos -= n
455
456					// For each char deleted, display the last matching line of history
457					history, positions := s.getHistoryByPattern(string(line))
458					historyPos = len(history) - 1
459					if len(history) > 0 {
460						foundLine = history[historyPos]
461						foundPos = positions[historyPos]
462					} else {
463						foundLine = ""
464						foundPos = 0
465					}
466				}
467			case ctrlG: // Cancel
468				return origLine, origPos, rune(esc), err
469
470			case tab, cr, lf, ctrlA, ctrlB, ctrlD, ctrlE, ctrlF, ctrlK,
471				ctrlL, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
472				fallthrough
473			case 0, ctrlC, esc, 28, 29, 30, 31:
474				return []rune(foundLine), foundPos, next, err
475			default:
476				line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
477				pos++
478
479				// For each keystroke typed, display the last matching line of history
480				history, positions = s.getHistoryByPattern(string(line))
481				historyPos = len(history) - 1
482				if len(history) > 0 {
483					foundLine = history[historyPos]
484					foundPos = positions[historyPos]
485				} else {
486					foundLine = ""
487					foundPos = 0
488				}
489			}
490		case action:
491			return []rune(foundLine), foundPos, next, err
492		}
493		err = s.refresh(getLine())
494		if err != nil {
495			return []rune(foundLine), foundPos, rune(esc), err
496		}
497	}
498}
499
500// addToKillRing adds some text to the kill ring. If mode is 0 it adds it to a
501// new node in the end of the kill ring, and move the current pointer to the new
502// node. If mode is 1 or 2 it appends or prepends the text to the current entry
503// of the killRing.
504func (s *State) addToKillRing(text []rune, mode int) {
505	// Don't use the same underlying array as text
506	killLine := make([]rune, len(text))
507	copy(killLine, text)
508
509	// Point killRing to a newNode, procedure depends on the killring state and
510	// append mode.
511	if mode == 0 { // Add new node to killRing
512		if s.killRing == nil { // if killring is empty, create a new one
513			s.killRing = ring.New(1)
514		} else if s.killRing.Len() >= KillRingMax { // if killring is "full"
515			s.killRing = s.killRing.Next()
516		} else { // Normal case
517			s.killRing.Link(ring.New(1))
518			s.killRing = s.killRing.Next()
519		}
520	} else {
521		if s.killRing == nil { // if killring is empty, create a new one
522			s.killRing = ring.New(1)
523			s.killRing.Value = []rune{}
524		}
525		if mode == 1 { // Append to last entry
526			killLine = append(s.killRing.Value.([]rune), killLine...)
527		} else if mode == 2 { // Prepend to last entry
528			killLine = append(killLine, s.killRing.Value.([]rune)...)
529		}
530	}
531
532	// Save text in the current killring node
533	s.killRing.Value = killLine
534}
535
536func (s *State) yank(p []rune, text []rune, pos int) ([]rune, int, interface{}, error) {
537	if s.killRing == nil {
538		return text, pos, rune(esc), nil
539	}
540
541	lineStart := text[:pos]
542	lineEnd := text[pos:]
543	var line []rune
544
545	for {
546		value := s.killRing.Value.([]rune)
547		line = make([]rune, 0)
548		line = append(line, lineStart...)
549		line = append(line, value...)
550		line = append(line, lineEnd...)
551
552		pos = len(lineStart) + len(value)
553		err := s.refresh(p, line, pos)
554		if err != nil {
555			return line, pos, 0, err
556		}
557
558		next, err := s.readNext()
559		if err != nil {
560			return line, pos, next, err
561		}
562
563		switch v := next.(type) {
564		case rune:
565			return line, pos, next, nil
566		case action:
567			switch v {
568			case altY:
569				s.killRing = s.killRing.Prev()
570			default:
571				return line, pos, next, nil
572			}
573		}
574	}
575}
576
577// Prompt displays p and returns a line of user input, not including a trailing
578// newline character. An io.EOF error is returned if the user signals end-of-file
579// by pressing Ctrl-D. Prompt allows line editing if the terminal supports it.
580func (s *State) Prompt(prompt string) (string, error) {
581	return s.PromptWithSuggestion(prompt, "", 0)
582}
583
584// PromptWithSuggestion displays prompt and an editable text with cursor at
585// given position. The cursor will be set to the end of the line if given position
586// is negative or greater than length of text. Returns a line of user input, not
587// including a trailing newline character. An io.EOF error is returned if the user
588// signals end-of-file by pressing Ctrl-D.
589func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (string, error) {
590	for _, r := range prompt {
591		if unicode.Is(unicode.C, r) {
592			return "", ErrInvalidPrompt
593		}
594	}
595	if s.inputRedirected || !s.terminalSupported {
596		return s.promptUnsupported(prompt)
597	}
598	p := []rune(prompt)
599	const minWorkingSpace = 10
600	if s.columns < countGlyphs(p)+minWorkingSpace {
601		return s.tooNarrow(prompt)
602	}
603	if s.outputRedirected {
604		return "", ErrNotTerminalOutput
605	}
606
607	s.historyMutex.RLock()
608	defer s.historyMutex.RUnlock()
609
610	fmt.Print(prompt)
611	var line = []rune(text)
612	historyEnd := ""
613	var historyPrefix []string
614	historyPos := 0
615	historyStale := true
616	historyAction := false // used to mark history related actions
617	killAction := 0        // used to mark kill related actions
618
619	defer s.stopPrompt()
620
621	if pos < 0 || len(text) < pos {
622		pos = len(text)
623	}
624	if len(line) > 0 {
625		err := s.refresh(p, line, pos)
626		if err != nil {
627			return "", err
628		}
629	}
630
631restart:
632	s.startPrompt()
633	s.getColumns()
634
635mainLoop:
636	for {
637		next, err := s.readNext()
638	haveNext:
639		if err != nil {
640			if s.shouldRestart != nil && s.shouldRestart(err) {
641				goto restart
642			}
643			return "", err
644		}
645
646		historyAction = false
647		switch v := next.(type) {
648		case rune:
649			switch v {
650			case cr, lf:
651				if s.needRefresh {
652					err := s.refresh(p, line, pos)
653					if err != nil {
654						return "", err
655					}
656				}
657				if s.multiLineMode {
658					s.resetMultiLine(p, line, pos)
659				}
660				fmt.Println()
661				break mainLoop
662			case ctrlA: // Start of line
663				pos = 0
664				s.needRefresh = true
665			case ctrlE: // End of line
666				pos = len(line)
667				s.needRefresh = true
668			case ctrlB: // left
669				if pos > 0 {
670					pos -= len(getSuffixGlyphs(line[:pos], 1))
671					s.needRefresh = true
672				} else {
673					fmt.Print(beep)
674				}
675			case ctrlF: // right
676				if pos < len(line) {
677					pos += len(getPrefixGlyphs(line[pos:], 1))
678					s.needRefresh = true
679				} else {
680					fmt.Print(beep)
681				}
682			case ctrlD: // del
683				if pos == 0 && len(line) == 0 {
684					// exit
685					return "", io.EOF
686				}
687
688				// ctrlD is a potential EOF, so the rune reader shuts down.
689				// Therefore, if it isn't actually an EOF, we must re-startPrompt.
690				s.restartPrompt()
691
692				if pos >= len(line) {
693					fmt.Print(beep)
694				} else {
695					n := len(getPrefixGlyphs(line[pos:], 1))
696					line = append(line[:pos], line[pos+n:]...)
697					s.needRefresh = true
698				}
699			case ctrlK: // delete remainder of line
700				if pos >= len(line) {
701					fmt.Print(beep)
702				} else {
703					if killAction > 0 {
704						s.addToKillRing(line[pos:], 1) // Add in apend mode
705					} else {
706						s.addToKillRing(line[pos:], 0) // Add in normal mode
707					}
708
709					killAction = 2 // Mark that there was a kill action
710					line = line[:pos]
711					s.needRefresh = true
712				}
713			case ctrlP: // up
714				historyAction = true
715				if historyStale {
716					historyPrefix = s.getHistoryByPrefix(string(line))
717					historyPos = len(historyPrefix)
718					historyStale = false
719				}
720				if historyPos > 0 {
721					if historyPos == len(historyPrefix) {
722						historyEnd = string(line)
723					}
724					historyPos--
725					line = []rune(historyPrefix[historyPos])
726					pos = len(line)
727					s.needRefresh = true
728				} else {
729					fmt.Print(beep)
730				}
731			case ctrlN: // down
732				historyAction = true
733				if historyStale {
734					historyPrefix = s.getHistoryByPrefix(string(line))
735					historyPos = len(historyPrefix)
736					historyStale = false
737				}
738				if historyPos < len(historyPrefix) {
739					historyPos++
740					if historyPos == len(historyPrefix) {
741						line = []rune(historyEnd)
742					} else {
743						line = []rune(historyPrefix[historyPos])
744					}
745					pos = len(line)
746					s.needRefresh = true
747				} else {
748					fmt.Print(beep)
749				}
750			case ctrlT: // transpose prev glyph with glyph under cursor
751				if len(line) < 2 || pos < 1 {
752					fmt.Print(beep)
753				} else {
754					if pos == len(line) {
755						pos -= len(getSuffixGlyphs(line, 1))
756					}
757					prev := getSuffixGlyphs(line[:pos], 1)
758					next := getPrefixGlyphs(line[pos:], 1)
759					scratch := make([]rune, len(prev))
760					copy(scratch, prev)
761					copy(line[pos-len(prev):], next)
762					copy(line[pos-len(prev)+len(next):], scratch)
763					pos += len(next)
764					s.needRefresh = true
765				}
766			case ctrlL: // clear screen
767				s.eraseScreen()
768				s.needRefresh = true
769			case ctrlC: // reset
770				fmt.Println("^C")
771				if s.multiLineMode {
772					s.resetMultiLine(p, line, pos)
773				}
774				if s.ctrlCAborts {
775					return "", ErrPromptAborted
776				}
777				line = line[:0]
778				pos = 0
779				fmt.Print(prompt)
780				s.restartPrompt()
781			case ctrlH, bs: // Backspace
782				if pos <= 0 {
783					fmt.Print(beep)
784				} else {
785					n := len(getSuffixGlyphs(line[:pos], 1))
786					line = append(line[:pos-n], line[pos:]...)
787					pos -= n
788					s.needRefresh = true
789				}
790			case ctrlU: // Erase line before cursor
791				if killAction > 0 {
792					s.addToKillRing(line[:pos], 2) // Add in prepend mode
793				} else {
794					s.addToKillRing(line[:pos], 0) // Add in normal mode
795				}
796
797				killAction = 2 // Mark that there was some killing
798				line = line[pos:]
799				pos = 0
800				s.needRefresh = true
801			case ctrlW: // Erase word
802				if pos == 0 {
803					fmt.Print(beep)
804					break
805				}
806				// Remove whitespace to the left
807				var buf []rune // Store the deleted chars in a buffer
808				for {
809					if pos == 0 || !unicode.IsSpace(line[pos-1]) {
810						break
811					}
812					buf = append(buf, line[pos-1])
813					line = append(line[:pos-1], line[pos:]...)
814					pos--
815				}
816				// Remove non-whitespace to the left
817				for {
818					if pos == 0 || unicode.IsSpace(line[pos-1]) {
819						break
820					}
821					buf = append(buf, line[pos-1])
822					line = append(line[:pos-1], line[pos:]...)
823					pos--
824				}
825				// Invert the buffer and save the result on the killRing
826				var newBuf []rune
827				for i := len(buf) - 1; i >= 0; i-- {
828					newBuf = append(newBuf, buf[i])
829				}
830				if killAction > 0 {
831					s.addToKillRing(newBuf, 2) // Add in prepend mode
832				} else {
833					s.addToKillRing(newBuf, 0) // Add in normal mode
834				}
835				killAction = 2 // Mark that there was some killing
836
837				s.needRefresh = true
838			case ctrlY: // Paste from Yank buffer
839				line, pos, next, err = s.yank(p, line, pos)
840				goto haveNext
841			case ctrlR: // Reverse Search
842				line, pos, next, err = s.reverseISearch(line, pos)
843				s.needRefresh = true
844				goto haveNext
845			case tab: // Tab completion
846				line, pos, next, err = s.tabComplete(p, line, pos)
847				goto haveNext
848			// Catch keys that do nothing, but you don't want them to beep
849			case esc:
850				// DO NOTHING
851			// Unused keys
852			case ctrlG, ctrlO, ctrlQ, ctrlS, ctrlV, ctrlX, ctrlZ:
853				fallthrough
854			// Catch unhandled control codes (anything <= 31)
855			case 0, 28, 29, 30, 31:
856				fmt.Print(beep)
857			default:
858				if pos == len(line) && !s.multiLineMode &&
859					len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines
860					countGlyphs(p)+countGlyphs(line) < s.columns-1 {
861					line = append(line, v)
862					fmt.Printf("%c", v)
863					pos++
864				} else {
865					line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
866					pos++
867					s.needRefresh = true
868				}
869			}
870		case action:
871			switch v {
872			case del:
873				if pos >= len(line) {
874					fmt.Print(beep)
875				} else {
876					n := len(getPrefixGlyphs(line[pos:], 1))
877					line = append(line[:pos], line[pos+n:]...)
878				}
879			case left:
880				if pos > 0 {
881					pos -= len(getSuffixGlyphs(line[:pos], 1))
882				} else {
883					fmt.Print(beep)
884				}
885			case wordLeft, altB:
886				if pos > 0 {
887					var spaceHere, spaceLeft, leftKnown bool
888					for {
889						pos--
890						if pos == 0 {
891							break
892						}
893						if leftKnown {
894							spaceHere = spaceLeft
895						} else {
896							spaceHere = unicode.IsSpace(line[pos])
897						}
898						spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true
899						if !spaceHere && spaceLeft {
900							break
901						}
902					}
903				} else {
904					fmt.Print(beep)
905				}
906			case right:
907				if pos < len(line) {
908					pos += len(getPrefixGlyphs(line[pos:], 1))
909				} else {
910					fmt.Print(beep)
911				}
912			case wordRight, altF:
913				if pos < len(line) {
914					var spaceHere, spaceLeft, hereKnown bool
915					for {
916						pos++
917						if pos == len(line) {
918							break
919						}
920						if hereKnown {
921							spaceLeft = spaceHere
922						} else {
923							spaceLeft = unicode.IsSpace(line[pos-1])
924						}
925						spaceHere, hereKnown = unicode.IsSpace(line[pos]), true
926						if spaceHere && !spaceLeft {
927							break
928						}
929					}
930				} else {
931					fmt.Print(beep)
932				}
933			case up:
934				historyAction = true
935				if historyStale {
936					historyPrefix = s.getHistoryByPrefix(string(line))
937					historyPos = len(historyPrefix)
938					historyStale = false
939				}
940				if historyPos > 0 {
941					if historyPos == len(historyPrefix) {
942						historyEnd = string(line)
943					}
944					historyPos--
945					line = []rune(historyPrefix[historyPos])
946					pos = len(line)
947				} else {
948					fmt.Print(beep)
949				}
950			case down:
951				historyAction = true
952				if historyStale {
953					historyPrefix = s.getHistoryByPrefix(string(line))
954					historyPos = len(historyPrefix)
955					historyStale = false
956				}
957				if historyPos < len(historyPrefix) {
958					historyPos++
959					if historyPos == len(historyPrefix) {
960						line = []rune(historyEnd)
961					} else {
962						line = []rune(historyPrefix[historyPos])
963					}
964					pos = len(line)
965				} else {
966					fmt.Print(beep)
967				}
968			case home: // Start of line
969				pos = 0
970			case end: // End of line
971				pos = len(line)
972			case winch: // Window change
973				if s.multiLineMode {
974					if s.maxRows-s.cursorRows > 0 {
975						s.moveDown(s.maxRows - s.cursorRows)
976					}
977					for i := 0; i < s.maxRows-1; i++ {
978						s.cursorPos(0)
979						s.eraseLine()
980						s.moveUp(1)
981					}
982					s.maxRows = 1
983					s.cursorRows = 1
984				}
985			}
986			s.needRefresh = true
987		}
988		if s.needRefresh && !s.inputWaiting() {
989			err := s.refresh(p, line, pos)
990			if err != nil {
991				return "", err
992			}
993		}
994		if !historyAction {
995			historyStale = true
996		}
997		if killAction > 0 {
998			killAction--
999		}
1000	}
1001	return string(line), nil
1002}
1003
1004// PasswordPrompt displays p, and then waits for user input. The input typed by
1005// the user is not displayed in the terminal.
1006func (s *State) PasswordPrompt(prompt string) (string, error) {
1007	for _, r := range prompt {
1008		if unicode.Is(unicode.C, r) {
1009			return "", ErrInvalidPrompt
1010		}
1011	}
1012	if !s.terminalSupported || s.columns == 0 {
1013		return "", errors.New("liner: function not supported in this terminal")
1014	}
1015	if s.inputRedirected {
1016		return s.promptUnsupported(prompt)
1017	}
1018	if s.outputRedirected {
1019		return "", ErrNotTerminalOutput
1020	}
1021
1022	p := []rune(prompt)
1023	const minWorkingSpace = 1
1024	if s.columns < countGlyphs(p)+minWorkingSpace {
1025		return s.tooNarrow(prompt)
1026	}
1027
1028	defer s.stopPrompt()
1029
1030restart:
1031	s.startPrompt()
1032	s.getColumns()
1033
1034	fmt.Print(prompt)
1035	var line []rune
1036	pos := 0
1037
1038mainLoop:
1039	for {
1040		next, err := s.readNext()
1041		if err != nil {
1042			if s.shouldRestart != nil && s.shouldRestart(err) {
1043				goto restart
1044			}
1045			return "", err
1046		}
1047
1048		switch v := next.(type) {
1049		case rune:
1050			switch v {
1051			case cr, lf:
1052				if s.needRefresh {
1053					err := s.refresh(p, line, pos)
1054					if err != nil {
1055						return "", err
1056					}
1057				}
1058				if s.multiLineMode {
1059					s.resetMultiLine(p, line, pos)
1060				}
1061				fmt.Println()
1062				break mainLoop
1063			case ctrlD: // del
1064				if pos == 0 && len(line) == 0 {
1065					// exit
1066					return "", io.EOF
1067				}
1068
1069				// ctrlD is a potential EOF, so the rune reader shuts down.
1070				// Therefore, if it isn't actually an EOF, we must re-startPrompt.
1071				s.restartPrompt()
1072			case ctrlL: // clear screen
1073				s.eraseScreen()
1074				err := s.refresh(p, []rune{}, 0)
1075				if err != nil {
1076					return "", err
1077				}
1078			case ctrlH, bs: // Backspace
1079				if pos <= 0 {
1080					fmt.Print(beep)
1081				} else {
1082					n := len(getSuffixGlyphs(line[:pos], 1))
1083					line = append(line[:pos-n], line[pos:]...)
1084					pos -= n
1085				}
1086			case ctrlC:
1087				fmt.Println("^C")
1088				if s.multiLineMode {
1089					s.resetMultiLine(p, line, pos)
1090				}
1091				if s.ctrlCAborts {
1092					return "", ErrPromptAborted
1093				}
1094				line = line[:0]
1095				pos = 0
1096				fmt.Print(prompt)
1097				s.restartPrompt()
1098			// Unused keys
1099			case esc, tab, ctrlA, ctrlB, ctrlE, ctrlF, ctrlG, ctrlK, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlR, ctrlS,
1100				ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
1101				fallthrough
1102			// Catch unhandled control codes (anything <= 31)
1103			case 0, 28, 29, 30, 31:
1104				fmt.Print(beep)
1105			default:
1106				line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
1107				pos++
1108			}
1109		}
1110	}
1111	return string(line), nil
1112}
1113
1114func (s *State) tooNarrow(prompt string) (string, error) {
1115	// Docker and OpenWRT and etc sometimes return 0 column width
1116	// Reset mode temporarily. Restore baked mode in case the terminal
1117	// is wide enough for the next Prompt attempt.
1118	m, merr := TerminalMode()
1119	s.origMode.ApplyMode()
1120	if merr == nil {
1121		defer m.ApplyMode()
1122	}
1123	if s.r == nil {
1124		// Windows does not always set s.r
1125		s.r = bufio.NewReader(os.Stdin)
1126		defer func() { s.r = nil }()
1127	}
1128	return s.promptUnsupported(prompt)
1129}
1130