github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/third_party/liner/line.go (about)

     1  // +build windows linux darwin openbsd freebsd netbsd
     2  
     3  package liner
     4  
     5  import (
     6  	"container/ring"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"strings"
    11  	"unicode"
    12  	"unicode/utf8"
    13  )
    14  
    15  type action int
    16  
    17  const (
    18  	left action = iota
    19  	right
    20  	up
    21  	down
    22  	home
    23  	end
    24  	insert
    25  	del
    26  	pageUp
    27  	pageDown
    28  	f1
    29  	f2
    30  	f3
    31  	f4
    32  	f5
    33  	f6
    34  	f7
    35  	f8
    36  	f9
    37  	f10
    38  	f11
    39  	f12
    40  	altY
    41  	shiftTab
    42  	wordLeft
    43  	wordRight
    44  	winch
    45  	unknown
    46  )
    47  
    48  const (
    49  	ctrlA = 1
    50  	ctrlB = 2
    51  	ctrlC = 3
    52  	ctrlD = 4
    53  	ctrlE = 5
    54  	ctrlF = 6
    55  	ctrlG = 7
    56  	ctrlH = 8
    57  	tab   = 9
    58  	lf    = 10
    59  	ctrlK = 11
    60  	ctrlL = 12
    61  	cr    = 13
    62  	ctrlN = 14
    63  	ctrlO = 15
    64  	ctrlP = 16
    65  	ctrlQ = 17
    66  	ctrlR = 18
    67  	ctrlS = 19
    68  	ctrlT = 20
    69  	ctrlU = 21
    70  	ctrlV = 22
    71  	ctrlW = 23
    72  	ctrlX = 24
    73  	ctrlY = 25
    74  	ctrlZ = 26
    75  	esc   = 27
    76  	bs    = 127
    77  )
    78  
    79  const (
    80  	beep = "\a"
    81  )
    82  
    83  type tabDirection int
    84  
    85  const (
    86  	tabForward tabDirection = iota
    87  	tabReverse
    88  )
    89  
    90  func (s *State) refresh(prompt []rune, buf []rune, pos int) error {
    91  	s.cursorPos(0)
    92  	_, err := fmt.Print(string(prompt))
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	pLen := countGlyphs(prompt)
    98  	bLen := countGlyphs(buf)
    99  	pos = countGlyphs(buf[:pos])
   100  	if pLen+bLen < s.columns {
   101  		_, err = fmt.Print(string(buf))
   102  		s.eraseLine()
   103  		s.cursorPos(pLen + pos)
   104  	} else {
   105  		// Find space available
   106  		space := s.columns - pLen
   107  		space-- // space for cursor
   108  		start := pos - space/2
   109  		end := start + space
   110  		if end > bLen {
   111  			end = bLen
   112  			start = end - space
   113  		}
   114  		if start < 0 {
   115  			start = 0
   116  			end = space
   117  		}
   118  		pos -= start
   119  
   120  		// Leave space for markers
   121  		if start > 0 {
   122  			start++
   123  		}
   124  		if end < bLen {
   125  			end--
   126  		}
   127  		startRune := len(getPrefixGlyphs(buf, start))
   128  		line := getPrefixGlyphs(buf[startRune:], end-start)
   129  
   130  		// Output
   131  		if start > 0 {
   132  			fmt.Print("{")
   133  		}
   134  		fmt.Print(string(line))
   135  		if end < bLen {
   136  			fmt.Print("}")
   137  		}
   138  
   139  		// Set cursor position
   140  		s.eraseLine()
   141  		s.cursorPos(pLen + pos)
   142  	}
   143  	return err
   144  }
   145  
   146  func longestCommonPrefix(strs []string) string {
   147  	if len(strs) == 0 {
   148  		return ""
   149  	}
   150  	longest := strs[0]
   151  
   152  	for _, str := range strs[1:] {
   153  		for !strings.HasPrefix(str, longest) {
   154  			longest = longest[:len(longest)-1]
   155  		}
   156  	}
   157  	// Remove trailing partial runes
   158  	longest = strings.TrimRight(longest, "\uFFFD")
   159  	return longest
   160  }
   161  
   162  func (s *State) circularTabs(items []string) func(tabDirection) (string, error) {
   163  	item := -1
   164  	return func(direction tabDirection) (string, error) {
   165  		if direction == tabForward {
   166  			if item < len(items)-1 {
   167  				item++
   168  			} else {
   169  				item = 0
   170  			}
   171  		} else if direction == tabReverse {
   172  			if item > 0 {
   173  				item--
   174  			} else {
   175  				item = len(items) - 1
   176  			}
   177  		}
   178  		return items[item], nil
   179  	}
   180  }
   181  
   182  func (s *State) printedTabs(items []string) func(tabDirection) (string, error) {
   183  	numTabs := 1
   184  	prefix := longestCommonPrefix(items)
   185  	return func(direction tabDirection) (string, error) {
   186  		if len(items) == 1 {
   187  			return items[0], nil
   188  		}
   189  
   190  		if numTabs == 2 {
   191  			if len(items) > 100 {
   192  				fmt.Printf("\nDisplay all %d possibilities? (y or n) ", len(items))
   193  				for {
   194  					next, err := s.readNext()
   195  					if err != nil {
   196  						return prefix, err
   197  					}
   198  
   199  					if key, ok := next.(rune); ok {
   200  						if unicode.ToLower(key) == 'n' {
   201  							return prefix, nil
   202  						} else if unicode.ToLower(key) == 'y' {
   203  							break
   204  						}
   205  					}
   206  				}
   207  			}
   208  			fmt.Println("")
   209  			maxWidth := 0
   210  			for _, item := range items {
   211  				if len(item) >= maxWidth {
   212  					maxWidth = len(item) + 1
   213  				}
   214  			}
   215  
   216  			numColumns := s.columns / maxWidth
   217  			numRows := len(items) / numColumns
   218  			if len(items)%numColumns > 0 {
   219  				numRows++
   220  			}
   221  
   222  			if len(items) <= numColumns {
   223  				maxWidth = 0
   224  			}
   225  			for i := 0; i < numRows; i++ {
   226  				for j := 0; j < numColumns*numRows; j += numRows {
   227  					if i+j < len(items) {
   228  						if maxWidth > 0 {
   229  							fmt.Printf("%-*s", maxWidth, items[i+j])
   230  						} else {
   231  							fmt.Printf("%v ", items[i+j])
   232  						}
   233  					}
   234  				}
   235  				fmt.Println("")
   236  			}
   237  		} else {
   238  			numTabs++
   239  		}
   240  		return prefix, nil
   241  	}
   242  }
   243  
   244  func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interface{}, error) {
   245  	if s.completer == nil {
   246  		return line, pos, rune(esc), nil
   247  	}
   248  	head, list, tail := s.completer(string(line), pos)
   249  	if len(list) <= 0 {
   250  		return line, pos, rune(esc), nil
   251  	}
   252  	hl := utf8.RuneCountInString(head)
   253  	if len(list) == 1 {
   254  		s.refresh(p, []rune(head+list[0]+tail), hl+utf8.RuneCountInString(list[0]))
   255  		return []rune(head + list[0] + tail), hl + utf8.RuneCountInString(list[0]), rune(esc), nil
   256  	}
   257  
   258  	direction := tabForward
   259  	tabPrinter := s.circularTabs(list)
   260  	if s.tabStyle == TabPrints {
   261  		tabPrinter = s.printedTabs(list)
   262  	}
   263  
   264  	for {
   265  		pick, err := tabPrinter(direction)
   266  		if err != nil {
   267  			return line, pos, rune(esc), err
   268  		}
   269  		s.refresh(p, []rune(head+pick+tail), hl+utf8.RuneCountInString(pick))
   270  
   271  		next, err := s.readNext()
   272  		if err != nil {
   273  			return line, pos, rune(esc), err
   274  		}
   275  		if key, ok := next.(rune); ok {
   276  			if key == tab {
   277  				direction = tabForward
   278  				continue
   279  			}
   280  			if key == esc {
   281  				return line, pos, rune(esc), nil
   282  			}
   283  		}
   284  		if a, ok := next.(action); ok && a == shiftTab {
   285  			direction = tabReverse
   286  			continue
   287  		}
   288  		return []rune(head + pick + tail), hl + utf8.RuneCountInString(pick), next, nil
   289  	}
   290  	// Not reached
   291  	return line, pos, rune(esc), nil
   292  }
   293  
   294  // reverse intelligent search, implements a bash-like history search.
   295  func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, interface{}, error) {
   296  	p := "(reverse-i-search)`': "
   297  	s.refresh([]rune(p), origLine, origPos)
   298  
   299  	line := []rune{}
   300  	pos := 0
   301  	foundLine := string(origLine)
   302  	foundPos := origPos
   303  
   304  	getLine := func() ([]rune, []rune, int) {
   305  		search := string(line)
   306  		prompt := "(reverse-i-search)`%s': "
   307  		return []rune(fmt.Sprintf(prompt, search)), []rune(foundLine), foundPos
   308  	}
   309  
   310  	history, positions := s.getHistoryByPattern(string(line))
   311  	historyPos := len(history) - 1
   312  
   313  	for {
   314  		next, err := s.readNext()
   315  		if err != nil {
   316  			return []rune(foundLine), foundPos, rune(esc), err
   317  		}
   318  
   319  		switch v := next.(type) {
   320  		case rune:
   321  			switch v {
   322  			case ctrlR: // Search backwards
   323  				if historyPos > 0 && historyPos < len(history) {
   324  					historyPos--
   325  					foundLine = history[historyPos]
   326  					foundPos = positions[historyPos]
   327  				} else {
   328  					fmt.Print(beep)
   329  				}
   330  			case ctrlS: // Search forward
   331  				if historyPos < len(history)-1 && historyPos >= 0 {
   332  					historyPos++
   333  					foundLine = history[historyPos]
   334  					foundPos = positions[historyPos]
   335  				} else {
   336  					fmt.Print(beep)
   337  				}
   338  			case ctrlH, bs: // Backspace
   339  				if pos <= 0 {
   340  					fmt.Print(beep)
   341  				} else {
   342  					n := len(getSuffixGlyphs(line[:pos], 1))
   343  					line = append(line[:pos-n], line[pos:]...)
   344  					pos -= n
   345  
   346  					// For each char deleted, display the last matching line of history
   347  					history, positions := s.getHistoryByPattern(string(line))
   348  					historyPos = len(history) - 1
   349  					if len(history) > 0 {
   350  						foundLine = history[historyPos]
   351  						foundPos = positions[historyPos]
   352  					} else {
   353  						foundLine = ""
   354  						foundPos = 0
   355  					}
   356  				}
   357  			case ctrlG: // Cancel
   358  				return origLine, origPos, rune(esc), err
   359  
   360  			case tab, cr, lf, ctrlA, ctrlB, ctrlD, ctrlE, ctrlF, ctrlK,
   361  				ctrlL, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
   362  				fallthrough
   363  			case 0, ctrlC, esc, 28, 29, 30, 31:
   364  				return []rune(foundLine), foundPos, next, err
   365  			default:
   366  				line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
   367  				pos++
   368  
   369  				// For each keystroke typed, display the last matching line of history
   370  				history, positions = s.getHistoryByPattern(string(line))
   371  				historyPos = len(history) - 1
   372  				if len(history) > 0 {
   373  					foundLine = history[historyPos]
   374  					foundPos = positions[historyPos]
   375  				} else {
   376  					foundLine = ""
   377  					foundPos = 0
   378  				}
   379  			}
   380  		case action:
   381  			return []rune(foundLine), foundPos, next, err
   382  		}
   383  		s.refresh(getLine())
   384  	}
   385  }
   386  
   387  // addToKillRing adds some text to the kill ring. If mode is 0 it adds it to a
   388  // new node in the end of the kill ring, and move the current pointer to the new
   389  // node. If mode is 1 or 2 it appends or prepends the text to the current entry
   390  // of the killRing.
   391  func (s *State) addToKillRing(text []rune, mode int) {
   392  	// Don't use the same underlying array as text
   393  	killLine := make([]rune, len(text))
   394  	copy(killLine, text)
   395  
   396  	// Point killRing to a newNode, procedure depends on the killring state and
   397  	// append mode.
   398  	if mode == 0 { // Add new node to killRing
   399  		if s.killRing == nil { // if killring is empty, create a new one
   400  			s.killRing = ring.New(1)
   401  		} else if s.killRing.Len() >= KillRingMax { // if killring is "full"
   402  			s.killRing = s.killRing.Next()
   403  		} else { // Normal case
   404  			s.killRing.Link(ring.New(1))
   405  			s.killRing = s.killRing.Next()
   406  		}
   407  	} else {
   408  		if s.killRing == nil { // if killring is empty, create a new one
   409  			s.killRing = ring.New(1)
   410  			s.killRing.Value = []rune{}
   411  		}
   412  		if mode == 1 { // Append to last entry
   413  			killLine = append(s.killRing.Value.([]rune), killLine...)
   414  		} else if mode == 2 { // Prepend to last entry
   415  			killLine = append(killLine, s.killRing.Value.([]rune)...)
   416  		}
   417  	}
   418  
   419  	// Save text in the current killring node
   420  	s.killRing.Value = killLine
   421  }
   422  
   423  func (s *State) yank(p []rune, text []rune, pos int) ([]rune, int, interface{}, error) {
   424  	if s.killRing == nil {
   425  		return text, pos, rune(esc), nil
   426  	}
   427  
   428  	lineStart := text[:pos]
   429  	lineEnd := text[pos:]
   430  	var line []rune
   431  
   432  	for {
   433  		value := s.killRing.Value.([]rune)
   434  		line = make([]rune, 0)
   435  		line = append(line, lineStart...)
   436  		line = append(line, value...)
   437  		line = append(line, lineEnd...)
   438  
   439  		pos = len(lineStart) + len(value)
   440  		s.refresh(p, line, pos)
   441  
   442  		next, err := s.readNext()
   443  		if err != nil {
   444  			return line, pos, next, err
   445  		}
   446  
   447  		switch v := next.(type) {
   448  		case rune:
   449  			return line, pos, next, nil
   450  		case action:
   451  			switch v {
   452  			case altY:
   453  				s.killRing = s.killRing.Prev()
   454  			default:
   455  				return line, pos, next, nil
   456  			}
   457  		}
   458  	}
   459  
   460  	return line, pos, esc, nil
   461  }
   462  
   463  // Prompt displays p and returns a line of user input, not including a trailing
   464  // newline character. An io.EOF error is returned if the user signals end-of-file
   465  // by pressing Ctrl-D. Prompt allows line editing if the terminal supports it.
   466  func (s *State) Prompt(prompt string) (string, error) {
   467  	if s.inputRedirected || !s.terminalSupported {
   468  		return s.promptUnsupported(prompt)
   469  	}
   470  	if s.outputRedirected {
   471  		return "", ErrNotTerminalOutput
   472  	}
   473  
   474  	s.historyMutex.RLock()
   475  	defer s.historyMutex.RUnlock()
   476  
   477  	s.startPrompt()
   478  	defer s.stopPrompt()
   479  	s.getColumns()
   480  
   481  	fmt.Print(prompt)
   482  	p := []rune(prompt)
   483  	var line []rune
   484  	pos := 0
   485  	historyEnd := ""
   486  	prefixHistory := s.getHistoryByPrefix(string(line))
   487  	historyPos := len(prefixHistory)
   488  	historyAction := false // used to mark history related actions
   489  	killAction := 0        // used to mark kill related actions
   490  mainLoop:
   491  	for {
   492  		next, err := s.readNext()
   493  	haveNext:
   494  		if err != nil {
   495  			return "", err
   496  		}
   497  
   498  		historyAction = false
   499  		switch v := next.(type) {
   500  		case rune:
   501  			switch v {
   502  			case cr, lf:
   503  				fmt.Println()
   504  				break mainLoop
   505  			case ctrlA: // Start of line
   506  				pos = 0
   507  				s.refresh(p, line, pos)
   508  			case ctrlE: // End of line
   509  				pos = len(line)
   510  				s.refresh(p, line, pos)
   511  			case ctrlB: // left
   512  				if pos > 0 {
   513  					pos -= len(getSuffixGlyphs(line[:pos], 1))
   514  					s.refresh(p, line, pos)
   515  				} else {
   516  					fmt.Print(beep)
   517  				}
   518  			case ctrlF: // right
   519  				if pos < len(line) {
   520  					pos += len(getPrefixGlyphs(line[pos:], 1))
   521  					s.refresh(p, line, pos)
   522  				} else {
   523  					fmt.Print(beep)
   524  				}
   525  			case ctrlD: // del
   526  				if pos == 0 && len(line) == 0 {
   527  					// exit
   528  					return "", io.EOF
   529  				}
   530  
   531  				// ctrlD is a potential EOF, so the rune reader shuts down.
   532  				// Therefore, if it isn't actually an EOF, we must re-startPrompt.
   533  				s.restartPrompt()
   534  
   535  				if pos >= len(line) {
   536  					fmt.Print(beep)
   537  				} else {
   538  					n := len(getPrefixGlyphs(line[pos:], 1))
   539  					line = append(line[:pos], line[pos+n:]...)
   540  					s.refresh(p, line, pos)
   541  				}
   542  			case ctrlK: // delete remainder of line
   543  				if pos >= len(line) {
   544  					fmt.Print(beep)
   545  				} else {
   546  					if killAction > 0 {
   547  						s.addToKillRing(line[pos:], 1) // Add in apend mode
   548  					} else {
   549  						s.addToKillRing(line[pos:], 0) // Add in normal mode
   550  					}
   551  
   552  					killAction = 2 // Mark that there was a kill action
   553  					line = line[:pos]
   554  					s.refresh(p, line, pos)
   555  				}
   556  			case ctrlP: // up
   557  				historyAction = true
   558  				if historyPos > 0 {
   559  					if historyPos == len(prefixHistory) {
   560  						historyEnd = string(line)
   561  					}
   562  					historyPos--
   563  					line = []rune(prefixHistory[historyPos])
   564  					pos = len(line)
   565  					s.refresh(p, line, pos)
   566  				} else {
   567  					fmt.Print(beep)
   568  				}
   569  			case ctrlN: // down
   570  				historyAction = true
   571  				if historyPos < len(prefixHistory) {
   572  					historyPos++
   573  					if historyPos == len(prefixHistory) {
   574  						line = []rune(historyEnd)
   575  					} else {
   576  						line = []rune(prefixHistory[historyPos])
   577  					}
   578  					pos = len(line)
   579  					s.refresh(p, line, pos)
   580  				} else {
   581  					fmt.Print(beep)
   582  				}
   583  			case ctrlT: // transpose prev glyph with glyph under cursor
   584  				if len(line) < 2 || pos < 1 {
   585  					fmt.Print(beep)
   586  				} else {
   587  					if pos == len(line) {
   588  						pos -= len(getSuffixGlyphs(line, 1))
   589  					}
   590  					prev := getSuffixGlyphs(line[:pos], 1)
   591  					next := getPrefixGlyphs(line[pos:], 1)
   592  					scratch := make([]rune, len(prev))
   593  					copy(scratch, prev)
   594  					copy(line[pos-len(prev):], next)
   595  					copy(line[pos-len(prev)+len(next):], scratch)
   596  					pos += len(next)
   597  					s.refresh(p, line, pos)
   598  				}
   599  			case ctrlL: // clear screen
   600  				s.eraseScreen()
   601  				s.refresh(p, line, pos)
   602  			case ctrlC: // reset
   603  				fmt.Println("^C")
   604  				if s.ctrlCAborts {
   605  					return "", ErrPromptAborted
   606  				}
   607  				line = line[:0]
   608  				pos = 0
   609  				fmt.Print(prompt)
   610  				s.restartPrompt()
   611  			case ctrlH, bs: // Backspace
   612  				if pos <= 0 {
   613  					fmt.Print(beep)
   614  				} else {
   615  					n := len(getSuffixGlyphs(line[:pos], 1))
   616  					line = append(line[:pos-n], line[pos:]...)
   617  					pos -= n
   618  					s.refresh(p, line, pos)
   619  				}
   620  			case ctrlU: // Erase line before cursor
   621  				if killAction > 0 {
   622  					s.addToKillRing(line[:pos], 2) // Add in prepend mode
   623  				} else {
   624  					s.addToKillRing(line[:pos], 0) // Add in normal mode
   625  				}
   626  
   627  				killAction = 2 // Mark that there was some killing
   628  				line = line[pos:]
   629  				pos = 0
   630  				s.refresh(p, line, pos)
   631  			case ctrlW: // Erase word
   632  				if pos == 0 {
   633  					fmt.Print(beep)
   634  					break
   635  				}
   636  				// Remove whitespace to the left
   637  				var buf []rune // Store the deleted chars in a buffer
   638  				for {
   639  					if pos == 0 || !unicode.IsSpace(line[pos-1]) {
   640  						break
   641  					}
   642  					buf = append(buf, line[pos-1])
   643  					line = append(line[:pos-1], line[pos:]...)
   644  					pos--
   645  				}
   646  				// Remove non-whitespace to the left
   647  				for {
   648  					if pos == 0 || unicode.IsSpace(line[pos-1]) {
   649  						break
   650  					}
   651  					buf = append(buf, line[pos-1])
   652  					line = append(line[:pos-1], line[pos:]...)
   653  					pos--
   654  				}
   655  				// Invert the buffer and save the result on the killRing
   656  				var newBuf []rune
   657  				for i := len(buf) - 1; i >= 0; i-- {
   658  					newBuf = append(newBuf, buf[i])
   659  				}
   660  				if killAction > 0 {
   661  					s.addToKillRing(newBuf, 2) // Add in prepend mode
   662  				} else {
   663  					s.addToKillRing(newBuf, 0) // Add in normal mode
   664  				}
   665  				killAction = 2 // Mark that there was some killing
   666  
   667  				s.refresh(p, line, pos)
   668  			case ctrlY: // Paste from Yank buffer
   669  				line, pos, next, err = s.yank(p, line, pos)
   670  				goto haveNext
   671  			case ctrlR: // Reverse Search
   672  				line, pos, next, err = s.reverseISearch(line, pos)
   673  				s.refresh(p, line, pos)
   674  				goto haveNext
   675  			case tab: // Tab completion
   676  				line, pos, next, err = s.tabComplete(p, line, pos)
   677  				goto haveNext
   678  			// Catch keys that do nothing, but you don't want them to beep
   679  			case esc:
   680  				// DO NOTHING
   681  			// Unused keys
   682  			case ctrlG, ctrlO, ctrlQ, ctrlS, ctrlV, ctrlX, ctrlZ:
   683  				fallthrough
   684  			// Catch unhandled control codes (anything <= 31)
   685  			case 0, 28, 29, 30, 31:
   686  				fmt.Print(beep)
   687  			default:
   688  				if pos == len(line) && len(p)+len(line) < s.columns-1 {
   689  					line = append(line, v)
   690  					fmt.Printf("%c", v)
   691  					pos++
   692  				} else {
   693  					line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
   694  					pos++
   695  					s.refresh(p, line, pos)
   696  				}
   697  			}
   698  		case action:
   699  			switch v {
   700  			case del:
   701  				if pos >= len(line) {
   702  					fmt.Print(beep)
   703  				} else {
   704  					n := len(getPrefixGlyphs(line[pos:], 1))
   705  					line = append(line[:pos], line[pos+n:]...)
   706  				}
   707  			case left:
   708  				if pos > 0 {
   709  					pos -= len(getSuffixGlyphs(line[:pos], 1))
   710  				} else {
   711  					fmt.Print(beep)
   712  				}
   713  			case wordLeft:
   714  				if pos > 0 {
   715  					for {
   716  						pos--
   717  						if pos == 0 || unicode.IsSpace(line[pos-1]) {
   718  							break
   719  						}
   720  					}
   721  				} else {
   722  					fmt.Print(beep)
   723  				}
   724  			case right:
   725  				if pos < len(line) {
   726  					pos += len(getPrefixGlyphs(line[pos:], 1))
   727  				} else {
   728  					fmt.Print(beep)
   729  				}
   730  			case wordRight:
   731  				if pos < len(line) {
   732  					for {
   733  						pos++
   734  						if pos == len(line) || unicode.IsSpace(line[pos]) {
   735  							break
   736  						}
   737  					}
   738  				} else {
   739  					fmt.Print(beep)
   740  				}
   741  			case up:
   742  				historyAction = true
   743  				if historyPos > 0 {
   744  					if historyPos == len(prefixHistory) {
   745  						historyEnd = string(line)
   746  					}
   747  					historyPos--
   748  					line = []rune(prefixHistory[historyPos])
   749  					pos = len(line)
   750  				} else {
   751  					fmt.Print(beep)
   752  				}
   753  			case down:
   754  				historyAction = true
   755  				if historyPos < len(prefixHistory) {
   756  					historyPos++
   757  					if historyPos == len(prefixHistory) {
   758  						line = []rune(historyEnd)
   759  					} else {
   760  						line = []rune(prefixHistory[historyPos])
   761  					}
   762  					pos = len(line)
   763  				} else {
   764  					fmt.Print(beep)
   765  				}
   766  			case home: // Start of line
   767  				pos = 0
   768  			case end: // End of line
   769  				pos = len(line)
   770  			}
   771  			s.refresh(p, line, pos)
   772  		}
   773  		if !historyAction {
   774  			prefixHistory = s.getHistoryByPrefix(string(line))
   775  			historyPos = len(prefixHistory)
   776  		}
   777  		if killAction > 0 {
   778  			killAction--
   779  		}
   780  	}
   781  	return string(line), nil
   782  }
   783  
   784  // PasswordPrompt displays p, and then waits for user input. The input typed by
   785  // the user is not displayed in the terminal.
   786  func (s *State) PasswordPrompt(prompt string) (string, error) {
   787  	if !s.terminalSupported {
   788  		return "", errors.New("liner: function not supported in this terminal")
   789  	}
   790  	if s.inputRedirected {
   791  		return s.promptUnsupported(prompt)
   792  	}
   793  	if s.outputRedirected {
   794  		return "", ErrNotTerminalOutput
   795  	}
   796  
   797  	s.startPrompt()
   798  	defer s.stopPrompt()
   799  	s.getColumns()
   800  
   801  	fmt.Print(prompt)
   802  	p := []rune(prompt)
   803  	var line []rune
   804  	pos := 0
   805  
   806  mainLoop:
   807  	for {
   808  		next, err := s.readNext()
   809  		if err != nil {
   810  			return "", err
   811  		}
   812  
   813  		switch v := next.(type) {
   814  		case rune:
   815  			switch v {
   816  			case cr, lf:
   817  				fmt.Println()
   818  				break mainLoop
   819  			case ctrlD: // del
   820  				if pos == 0 && len(line) == 0 {
   821  					// exit
   822  					return "", io.EOF
   823  				}
   824  
   825  				// ctrlD is a potential EOF, so the rune reader shuts down.
   826  				// Therefore, if it isn't actually an EOF, we must re-startPrompt.
   827  				s.restartPrompt()
   828  			case ctrlL: // clear screen
   829  				s.eraseScreen()
   830  				s.refresh(p, []rune{}, 0)
   831  			case ctrlH, bs: // Backspace
   832  				if pos <= 0 {
   833  					fmt.Print(beep)
   834  				} else {
   835  					n := len(getSuffixGlyphs(line[:pos], 1))
   836  					line = append(line[:pos-n], line[pos:]...)
   837  					pos -= n
   838  				}
   839  			case ctrlC:
   840  				fmt.Println("^C")
   841  				if s.ctrlCAborts {
   842  					return "", ErrPromptAborted
   843  				}
   844  				line = line[:0]
   845  				pos = 0
   846  				fmt.Print(prompt)
   847  				s.restartPrompt()
   848  			// Unused keys
   849  			case esc, tab, ctrlA, ctrlB, ctrlE, ctrlF, ctrlG, ctrlK, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlR, ctrlS,
   850  				ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
   851  				fallthrough
   852  			// Catch unhandled control codes (anything <= 31)
   853  			case 0, 28, 29, 30, 31:
   854  				fmt.Print(beep)
   855  			default:
   856  				line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
   857  				pos++
   858  			}
   859  		}
   860  	}
   861  	return string(line), nil
   862  }