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