aboutsummaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/peterh/liner/line.go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/github.com/peterh/liner/line.go')
-rw-r--r--Godeps/_workspace/src/github.com/peterh/liner/line.go1018
1 files changed, 0 insertions, 1018 deletions
diff --git a/Godeps/_workspace/src/github.com/peterh/liner/line.go b/Godeps/_workspace/src/github.com/peterh/liner/line.go
deleted file mode 100644
index 95364ae56..000000000
--- a/Godeps/_workspace/src/github.com/peterh/liner/line.go
+++ /dev/null
@@ -1,1018 +0,0 @@
-// +build windows linux darwin openbsd freebsd netbsd
-
-package liner
-
-import (
- "container/ring"
- "errors"
- "fmt"
- "io"
- "strings"
- "unicode"
- "unicode/utf8"
-)
-
-type action int
-
-const (
- left action = iota
- right
- up
- down
- home
- end
- insert
- del
- pageUp
- pageDown
- f1
- f2
- f3
- f4
- f5
- f6
- f7
- f8
- f9
- f10
- f11
- f12
- altB
- altF
- altY
- shiftTab
- wordLeft
- wordRight
- winch
- unknown
-)
-
-const (
- ctrlA = 1
- ctrlB = 2
- ctrlC = 3
- ctrlD = 4
- ctrlE = 5
- ctrlF = 6
- ctrlG = 7
- ctrlH = 8
- tab = 9
- lf = 10
- ctrlK = 11
- ctrlL = 12
- cr = 13
- ctrlN = 14
- ctrlO = 15
- ctrlP = 16
- ctrlQ = 17
- ctrlR = 18
- ctrlS = 19
- ctrlT = 20
- ctrlU = 21
- ctrlV = 22
- ctrlW = 23
- ctrlX = 24
- ctrlY = 25
- ctrlZ = 26
- esc = 27
- bs = 127
-)
-
-const (
- beep = "\a"
-)
-
-type tabDirection int
-
-const (
- tabForward tabDirection = iota
- tabReverse
-)
-
-func (s *State) refresh(prompt []rune, buf []rune, pos int) error {
- if s.multiLineMode {
- return s.refreshMultiLine(prompt, buf, pos)
- } else {
- return s.refreshSingleLine(prompt, buf, pos)
- }
-}
-
-func (s *State) refreshSingleLine(prompt []rune, buf []rune, pos int) error {
- s.cursorPos(0)
- _, err := fmt.Print(string(prompt))
- if err != nil {
- return err
- }
-
- pLen := countGlyphs(prompt)
- bLen := countGlyphs(buf)
- pos = countGlyphs(buf[:pos])
- if pLen+bLen < s.columns {
- _, err = fmt.Print(string(buf))
- s.eraseLine()
- s.cursorPos(pLen + pos)
- } else {
- // Find space available
- space := s.columns - pLen
- space-- // space for cursor
- start := pos - space/2
- end := start + space
- if end > bLen {
- end = bLen
- start = end - space
- }
- if start < 0 {
- start = 0
- end = space
- }
- pos -= start
-
- // Leave space for markers
- if start > 0 {
- start++
- }
- if end < bLen {
- end--
- }
- startRune := len(getPrefixGlyphs(buf, start))
- line := getPrefixGlyphs(buf[startRune:], end-start)
-
- // Output
- if start > 0 {
- fmt.Print("{")
- }
- fmt.Print(string(line))
- if end < bLen {
- fmt.Print("}")
- }
-
- // Set cursor position
- s.eraseLine()
- s.cursorPos(pLen + pos)
- }
- return err
-}
-
-func (s *State) refreshMultiLine(prompt []rune, buf []rune, pos int) error {
- promptColumns := countMultiLineGlyphs(prompt, s.columns, 0)
- totalColumns := countMultiLineGlyphs(buf, s.columns, promptColumns)
- totalRows := (totalColumns + s.columns - 1) / s.columns
- maxRows := s.maxRows
- if totalRows > s.maxRows {
- s.maxRows = totalRows
- }
- cursorRows := s.cursorRows
- if cursorRows == 0 {
- cursorRows = 1
- }
-
- /* First step: clear all the lines used before. To do so start by
- * going to the last row. */
- if maxRows-cursorRows > 0 {
- s.moveDown(maxRows - cursorRows)
- }
-
- /* Now for every row clear it, go up. */
- for i := 0; i < maxRows-1; i++ {
- s.cursorPos(0)
- s.eraseLine()
- s.moveUp(1)
- }
-
- /* Clean the top line. */
- s.cursorPos(0)
- s.eraseLine()
-
- /* Write the prompt and the current buffer content */
- if _, err := fmt.Print(string(prompt)); err != nil {
- return err
- }
- if _, err := fmt.Print(string(buf)); err != nil {
- return err
- }
-
- /* If we are at the very end of the screen with our prompt, we need to
- * emit a newline and move the prompt to the first column. */
- cursorColumns := countMultiLineGlyphs(buf[:pos], s.columns, promptColumns)
- if cursorColumns == totalColumns && totalColumns%s.columns == 0 {
- s.emitNewLine()
- s.cursorPos(0)
- totalRows++
- if totalRows > s.maxRows {
- s.maxRows = totalRows
- }
- }
-
- /* Move cursor to right position. */
- cursorRows = (cursorColumns + s.columns) / s.columns
- if s.cursorRows > 0 && totalRows-cursorRows > 0 {
- s.moveUp(totalRows - cursorRows)
- }
- /* Set column. */
- s.cursorPos(cursorColumns % s.columns)
-
- s.cursorRows = cursorRows
- return nil
-}
-
-func (s *State) resetMultiLine(prompt []rune, buf []rune, pos int) {
- columns := countMultiLineGlyphs(prompt, s.columns, 0)
- columns = countMultiLineGlyphs(buf[:pos], s.columns, columns)
- columns += 2 // ^C
- cursorRows := (columns + s.columns) / s.columns
- if s.maxRows-cursorRows > 0 {
- for i := 0; i < s.maxRows-cursorRows; i++ {
- fmt.Println() // always moves the cursor down or scrolls the window up as needed
- }
- }
- s.maxRows = 1
- s.cursorRows = 0
-}
-
-func longestCommonPrefix(strs []string) string {
- if len(strs) == 0 {
- return ""
- }
- longest := strs[0]
-
- for _, str := range strs[1:] {
- for !strings.HasPrefix(str, longest) {
- longest = longest[:len(longest)-1]
- }
- }
- // Remove trailing partial runes
- longest = strings.TrimRight(longest, "\uFFFD")
- return longest
-}
-
-func (s *State) circularTabs(items []string) func(tabDirection) (string, error) {
- item := -1
- return func(direction tabDirection) (string, error) {
- if direction == tabForward {
- if item < len(items)-1 {
- item++
- } else {
- item = 0
- }
- } else if direction == tabReverse {
- if item > 0 {
- item--
- } else {
- item = len(items) - 1
- }
- }
- return items[item], nil
- }
-}
-
-func calculateColumns(screenWidth int, items []string) (numColumns, numRows, maxWidth int) {
- for _, item := range items {
- if len(item) >= screenWidth {
- return 1, len(items), screenWidth - 1
- }
- if len(item) >= maxWidth {
- maxWidth = len(item) + 1
- }
- }
-
- numColumns = screenWidth / maxWidth
- numRows = len(items) / numColumns
- if len(items)%numColumns > 0 {
- numRows++
- }
-
- if len(items) <= numColumns {
- maxWidth = 0
- }
-
- return
-}
-
-func (s *State) printedTabs(items []string) func(tabDirection) (string, error) {
- numTabs := 1
- prefix := longestCommonPrefix(items)
- return func(direction tabDirection) (string, error) {
- if len(items) == 1 {
- return items[0], nil
- }
-
- if numTabs == 2 {
- if len(items) > 100 {
- fmt.Printf("\nDisplay all %d possibilities? (y or n) ", len(items))
- prompt:
- for {
- next, err := s.readNext()
- if err != nil {
- return prefix, err
- }
-
- if key, ok := next.(rune); ok {
- switch key {
- case 'n', 'N':
- return prefix, nil
- case 'y', 'Y':
- break prompt
- case ctrlC, ctrlD, cr, lf:
- s.restartPrompt()
- }
- }
- }
- }
- fmt.Println("")
-
- numColumns, numRows, maxWidth := calculateColumns(s.columns, items)
-
- for i := 0; i < numRows; i++ {
- for j := 0; j < numColumns*numRows; j += numRows {
- if i+j < len(items) {
- if maxWidth > 0 {
- fmt.Printf("%-*.[1]*s", maxWidth, items[i+j])
- } else {
- fmt.Printf("%v ", items[i+j])
- }
- }
- }
- fmt.Println("")
- }
- } else {
- numTabs++
- }
- return prefix, nil
- }
-}
-
-func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interface{}, error) {
- if s.completer == nil {
- return line, pos, rune(esc), nil
- }
- head, list, tail := s.completer(string(line), pos)
- if len(list) <= 0 {
- return line, pos, rune(esc), nil
- }
- hl := utf8.RuneCountInString(head)
- if len(list) == 1 {
- s.refresh(p, []rune(head+list[0]+tail), hl+utf8.RuneCountInString(list[0]))
- return []rune(head + list[0] + tail), hl + utf8.RuneCountInString(list[0]), rune(esc), nil
- }
-
- direction := tabForward
- tabPrinter := s.circularTabs(list)
- if s.tabStyle == TabPrints {
- tabPrinter = s.printedTabs(list)
- }
-
- for {
- pick, err := tabPrinter(direction)
- if err != nil {
- return line, pos, rune(esc), err
- }
- s.refresh(p, []rune(head+pick+tail), hl+utf8.RuneCountInString(pick))
-
- next, err := s.readNext()
- if err != nil {
- return line, pos, rune(esc), err
- }
- if key, ok := next.(rune); ok {
- if key == tab {
- direction = tabForward
- continue
- }
- if key == esc {
- return line, pos, rune(esc), nil
- }
- }
- if a, ok := next.(action); ok && a == shiftTab {
- direction = tabReverse
- continue
- }
- return []rune(head + pick + tail), hl + utf8.RuneCountInString(pick), next, nil
- }
- // Not reached
- return line, pos, rune(esc), nil
-}
-
-// reverse intelligent search, implements a bash-like history search.
-func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, interface{}, error) {
- p := "(reverse-i-search)`': "
- s.refresh([]rune(p), origLine, origPos)
-
- line := []rune{}
- pos := 0
- foundLine := string(origLine)
- foundPos := origPos
-
- getLine := func() ([]rune, []rune, int) {
- search := string(line)
- prompt := "(reverse-i-search)`%s': "
- return []rune(fmt.Sprintf(prompt, search)), []rune(foundLine), foundPos
- }
-
- history, positions := s.getHistoryByPattern(string(line))
- historyPos := len(history) - 1
-
- for {
- next, err := s.readNext()
- if err != nil {
- return []rune(foundLine), foundPos, rune(esc), err
- }
-
- switch v := next.(type) {
- case rune:
- switch v {
- case ctrlR: // Search backwards
- if historyPos > 0 && historyPos < len(history) {
- historyPos--
- foundLine = history[historyPos]
- foundPos = positions[historyPos]
- } else {
- fmt.Print(beep)
- }
- case ctrlS: // Search forward
- if historyPos < len(history)-1 && historyPos >= 0 {
- historyPos++
- foundLine = history[historyPos]
- foundPos = positions[historyPos]
- } else {
- fmt.Print(beep)
- }
- case ctrlH, bs: // Backspace
- if pos <= 0 {
- fmt.Print(beep)
- } else {
- n := len(getSuffixGlyphs(line[:pos], 1))
- line = append(line[:pos-n], line[pos:]...)
- pos -= n
-
- // For each char deleted, display the last matching line of history
- history, positions := s.getHistoryByPattern(string(line))
- historyPos = len(history) - 1
- if len(history) > 0 {
- foundLine = history[historyPos]
- foundPos = positions[historyPos]
- } else {
- foundLine = ""
- foundPos = 0
- }
- }
- case ctrlG: // Cancel
- return origLine, origPos, rune(esc), err
-
- case tab, cr, lf, ctrlA, ctrlB, ctrlD, ctrlE, ctrlF, ctrlK,
- ctrlL, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
- fallthrough
- case 0, ctrlC, esc, 28, 29, 30, 31:
- return []rune(foundLine), foundPos, next, err
- default:
- line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
- pos++
-
- // For each keystroke typed, display the last matching line of history
- history, positions = s.getHistoryByPattern(string(line))
- historyPos = len(history) - 1
- if len(history) > 0 {
- foundLine = history[historyPos]
- foundPos = positions[historyPos]
- } else {
- foundLine = ""
- foundPos = 0
- }
- }
- case action:
- return []rune(foundLine), foundPos, next, err
- }
- s.refresh(getLine())
- }
-}
-
-// addToKillRing adds some text to the kill ring. If mode is 0 it adds it to a
-// new node in the end of the kill ring, and move the current pointer to the new
-// node. If mode is 1 or 2 it appends or prepends the text to the current entry
-// of the killRing.
-func (s *State) addToKillRing(text []rune, mode int) {
- // Don't use the same underlying array as text
- killLine := make([]rune, len(text))
- copy(killLine, text)
-
- // Point killRing to a newNode, procedure depends on the killring state and
- // append mode.
- if mode == 0 { // Add new node to killRing
- if s.killRing == nil { // if killring is empty, create a new one
- s.killRing = ring.New(1)
- } else if s.killRing.Len() >= KillRingMax { // if killring is "full"
- s.killRing = s.killRing.Next()
- } else { // Normal case
- s.killRing.Link(ring.New(1))
- s.killRing = s.killRing.Next()
- }
- } else {
- if s.killRing == nil { // if killring is empty, create a new one
- s.killRing = ring.New(1)
- s.killRing.Value = []rune{}
- }
- if mode == 1 { // Append to last entry
- killLine = append(s.killRing.Value.([]rune), killLine...)
- } else if mode == 2 { // Prepend to last entry
- killLine = append(killLine, s.killRing.Value.([]rune)...)
- }
- }
-
- // Save text in the current killring node
- s.killRing.Value = killLine
-}
-
-func (s *State) yank(p []rune, text []rune, pos int) ([]rune, int, interface{}, error) {
- if s.killRing == nil {
- return text, pos, rune(esc), nil
- }
-
- lineStart := text[:pos]
- lineEnd := text[pos:]
- var line []rune
-
- for {
- value := s.killRing.Value.([]rune)
- line = make([]rune, 0)
- line = append(line, lineStart...)
- line = append(line, value...)
- line = append(line, lineEnd...)
-
- pos = len(lineStart) + len(value)
- s.refresh(p, line, pos)
-
- next, err := s.readNext()
- if err != nil {
- return line, pos, next, err
- }
-
- switch v := next.(type) {
- case rune:
- return line, pos, next, nil
- case action:
- switch v {
- case altY:
- s.killRing = s.killRing.Prev()
- default:
- return line, pos, next, nil
- }
- }
- }
-
- return line, pos, esc, nil
-}
-
-// Prompt displays p and returns a line of user input, not including a trailing
-// newline character. An io.EOF error is returned if the user signals end-of-file
-// by pressing Ctrl-D. Prompt allows line editing if the terminal supports it.
-func (s *State) Prompt(prompt string) (string, error) {
- if s.inputRedirected || !s.terminalSupported {
- return s.promptUnsupported(prompt)
- }
- if s.outputRedirected {
- return "", ErrNotTerminalOutput
- }
-
- s.historyMutex.RLock()
- defer s.historyMutex.RUnlock()
-
- fmt.Print(prompt)
- p := []rune(prompt)
- var line []rune
- pos := 0
- historyEnd := ""
- prefixHistory := s.getHistoryByPrefix(string(line))
- historyPos := len(prefixHistory)
- historyAction := false // used to mark history related actions
- killAction := 0 // used to mark kill related actions
-
- defer s.stopPrompt()
-
-restart:
- s.startPrompt()
- s.getColumns()
-
-mainLoop:
- for {
- next, err := s.readNext()
- haveNext:
- if err != nil {
- if s.shouldRestart != nil && s.shouldRestart(err) {
- goto restart
- }
- return "", err
- }
-
- historyAction = false
- switch v := next.(type) {
- case rune:
- switch v {
- case cr, lf:
- if s.multiLineMode {
- s.resetMultiLine(p, line, pos)
- }
- fmt.Println()
- break mainLoop
- case ctrlA: // Start of line
- pos = 0
- s.refresh(p, line, pos)
- case ctrlE: // End of line
- pos = len(line)
- s.refresh(p, line, pos)
- case ctrlB: // left
- if pos > 0 {
- pos -= len(getSuffixGlyphs(line[:pos], 1))
- s.refresh(p, line, pos)
- } else {
- fmt.Print(beep)
- }
- case ctrlF: // right
- if pos < len(line) {
- pos += len(getPrefixGlyphs(line[pos:], 1))
- s.refresh(p, line, pos)
- } else {
- fmt.Print(beep)
- }
- case ctrlD: // del
- if pos == 0 && len(line) == 0 {
- // exit
- return "", io.EOF
- }
-
- // ctrlD is a potential EOF, so the rune reader shuts down.
- // Therefore, if it isn't actually an EOF, we must re-startPrompt.
- s.restartPrompt()
-
- if pos >= len(line) {
- fmt.Print(beep)
- } else {
- n := len(getPrefixGlyphs(line[pos:], 1))
- line = append(line[:pos], line[pos+n:]...)
- s.refresh(p, line, pos)
- }
- case ctrlK: // delete remainder of line
- if pos >= len(line) {
- fmt.Print(beep)
- } else {
- if killAction > 0 {
- s.addToKillRing(line[pos:], 1) // Add in apend mode
- } else {
- s.addToKillRing(line[pos:], 0) // Add in normal mode
- }
-
- killAction = 2 // Mark that there was a kill action
- line = line[:pos]
- s.refresh(p, line, pos)
- }
- case ctrlP: // up
- historyAction = true
- if historyPos > 0 {
- if historyPos == len(prefixHistory) {
- historyEnd = string(line)
- }
- historyPos--
- line = []rune(prefixHistory[historyPos])
- pos = len(line)
- s.refresh(p, line, pos)
- } else {
- fmt.Print(beep)
- }
- case ctrlN: // down
- historyAction = true
- if historyPos < len(prefixHistory) {
- historyPos++
- if historyPos == len(prefixHistory) {
- line = []rune(historyEnd)
- } else {
- line = []rune(prefixHistory[historyPos])
- }
- pos = len(line)
- s.refresh(p, line, pos)
- } else {
- fmt.Print(beep)
- }
- case ctrlT: // transpose prev glyph with glyph under cursor
- if len(line) < 2 || pos < 1 {
- fmt.Print(beep)
- } else {
- if pos == len(line) {
- pos -= len(getSuffixGlyphs(line, 1))
- }
- prev := getSuffixGlyphs(line[:pos], 1)
- next := getPrefixGlyphs(line[pos:], 1)
- scratch := make([]rune, len(prev))
- copy(scratch, prev)
- copy(line[pos-len(prev):], next)
- copy(line[pos-len(prev)+len(next):], scratch)
- pos += len(next)
- s.refresh(p, line, pos)
- }
- case ctrlL: // clear screen
- s.eraseScreen()
- s.refresh(p, line, pos)
- case ctrlC: // reset
- fmt.Println("^C")
- if s.multiLineMode {
- s.resetMultiLine(p, line, pos)
- }
- if s.ctrlCAborts {
- return "", ErrPromptAborted
- }
- line = line[:0]
- pos = 0
- fmt.Print(prompt)
- s.restartPrompt()
- case ctrlH, bs: // Backspace
- if pos <= 0 {
- fmt.Print(beep)
- } else {
- n := len(getSuffixGlyphs(line[:pos], 1))
- line = append(line[:pos-n], line[pos:]...)
- pos -= n
- s.refresh(p, line, pos)
- }
- case ctrlU: // Erase line before cursor
- if killAction > 0 {
- s.addToKillRing(line[:pos], 2) // Add in prepend mode
- } else {
- s.addToKillRing(line[:pos], 0) // Add in normal mode
- }
-
- killAction = 2 // Mark that there was some killing
- line = line[pos:]
- pos = 0
- s.refresh(p, line, pos)
- case ctrlW: // Erase word
- if pos == 0 {
- fmt.Print(beep)
- break
- }
- // Remove whitespace to the left
- var buf []rune // Store the deleted chars in a buffer
- for {
- if pos == 0 || !unicode.IsSpace(line[pos-1]) {
- break
- }
- buf = append(buf, line[pos-1])
- line = append(line[:pos-1], line[pos:]...)
- pos--
- }
- // Remove non-whitespace to the left
- for {
- if pos == 0 || unicode.IsSpace(line[pos-1]) {
- break
- }
- buf = append(buf, line[pos-1])
- line = append(line[:pos-1], line[pos:]...)
- pos--
- }
- // Invert the buffer and save the result on the killRing
- var newBuf []rune
- for i := len(buf) - 1; i >= 0; i-- {
- newBuf = append(newBuf, buf[i])
- }
- if killAction > 0 {
- s.addToKillRing(newBuf, 2) // Add in prepend mode
- } else {
- s.addToKillRing(newBuf, 0) // Add in normal mode
- }
- killAction = 2 // Mark that there was some killing
-
- s.refresh(p, line, pos)
- case ctrlY: // Paste from Yank buffer
- line, pos, next, err = s.yank(p, line, pos)
- goto haveNext
- case ctrlR: // Reverse Search
- line, pos, next, err = s.reverseISearch(line, pos)
- s.refresh(p, line, pos)
- goto haveNext
- case tab: // Tab completion
- line, pos, next, err = s.tabComplete(p, line, pos)
- goto haveNext
- // Catch keys that do nothing, but you don't want them to beep
- case esc:
- // DO NOTHING
- // Unused keys
- case ctrlG, ctrlO, ctrlQ, ctrlS, ctrlV, ctrlX, ctrlZ:
- fallthrough
- // Catch unhandled control codes (anything <= 31)
- case 0, 28, 29, 30, 31:
- fmt.Print(beep)
- default:
- if pos == len(line) && !s.multiLineMode && countGlyphs(p)+countGlyphs(line) < s.columns-1 {
- line = append(line, v)
- fmt.Printf("%c", v)
- pos++
- } else {
- line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
- pos++
- s.refresh(p, line, pos)
- }
- }
- case action:
- switch v {
- case del:
- if pos >= len(line) {
- fmt.Print(beep)
- } else {
- n := len(getPrefixGlyphs(line[pos:], 1))
- line = append(line[:pos], line[pos+n:]...)
- }
- case left:
- if pos > 0 {
- pos -= len(getSuffixGlyphs(line[:pos], 1))
- } else {
- fmt.Print(beep)
- }
- case wordLeft, altB:
- if pos > 0 {
- var spaceHere, spaceLeft, leftKnown bool
- for {
- pos--
- if pos == 0 {
- break
- }
- if leftKnown {
- spaceHere = spaceLeft
- } else {
- spaceHere = unicode.IsSpace(line[pos])
- }
- spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true
- if !spaceHere && spaceLeft {
- break
- }
- }
- } else {
- fmt.Print(beep)
- }
- case right:
- if pos < len(line) {
- pos += len(getPrefixGlyphs(line[pos:], 1))
- } else {
- fmt.Print(beep)
- }
- case wordRight, altF:
- if pos < len(line) {
- var spaceHere, spaceLeft, hereKnown bool
- for {
- pos++
- if pos == len(line) {
- break
- }
- if hereKnown {
- spaceLeft = spaceHere
- } else {
- spaceLeft = unicode.IsSpace(line[pos-1])
- }
- spaceHere, hereKnown = unicode.IsSpace(line[pos]), true
- if spaceHere && !spaceLeft {
- break
- }
- }
- } else {
- fmt.Print(beep)
- }
- case up:
- historyAction = true
- if historyPos > 0 {
- if historyPos == len(prefixHistory) {
- historyEnd = string(line)
- }
- historyPos--
- line = []rune(prefixHistory[historyPos])
- pos = len(line)
- } else {
- fmt.Print(beep)
- }
- case down:
- historyAction = true
- if historyPos < len(prefixHistory) {
- historyPos++
- if historyPos == len(prefixHistory) {
- line = []rune(historyEnd)
- } else {
- line = []rune(prefixHistory[historyPos])
- }
- pos = len(line)
- } else {
- fmt.Print(beep)
- }
- case home: // Start of line
- pos = 0
- case end: // End of line
- pos = len(line)
- case winch: // Window change
- if s.multiLineMode {
- if s.maxRows-s.cursorRows > 0 {
- s.moveDown(s.maxRows - s.cursorRows)
- }
- for i := 0; i < s.maxRows-1; i++ {
- s.cursorPos(0)
- s.eraseLine()
- s.moveUp(1)
- }
- s.maxRows = 1
- s.cursorRows = 1
- }
- }
- s.refresh(p, line, pos)
- }
- if !historyAction {
- prefixHistory = s.getHistoryByPrefix(string(line))
- historyPos = len(prefixHistory)
- }
- if killAction > 0 {
- killAction--
- }
- }
- return string(line), nil
-}
-
-// PasswordPrompt displays p, and then waits for user input. The input typed by
-// the user is not displayed in the terminal.
-func (s *State) PasswordPrompt(prompt string) (string, error) {
- if !s.terminalSupported {
- return "", errors.New("liner: function not supported in this terminal")
- }
- if s.inputRedirected {
- return s.promptUnsupported(prompt)
- }
- if s.outputRedirected {
- return "", ErrNotTerminalOutput
- }
-
- defer s.stopPrompt()
-
-restart:
- s.startPrompt()
- s.getColumns()
-
- fmt.Print(prompt)
- p := []rune(prompt)
- var line []rune
- pos := 0
-
-mainLoop:
- for {
- next, err := s.readNext()
- if err != nil {
- if s.shouldRestart != nil && s.shouldRestart(err) {
- goto restart
- }
- return "", err
- }
-
- switch v := next.(type) {
- case rune:
- switch v {
- case cr, lf:
- if s.multiLineMode {
- s.resetMultiLine(p, line, pos)
- }
- fmt.Println()
- break mainLoop
- case ctrlD: // del
- if pos == 0 && len(line) == 0 {
- // exit
- return "", io.EOF
- }
-
- // ctrlD is a potential EOF, so the rune reader shuts down.
- // Therefore, if it isn't actually an EOF, we must re-startPrompt.
- s.restartPrompt()
- case ctrlL: // clear screen
- s.eraseScreen()
- s.refresh(p, []rune{}, 0)
- case ctrlH, bs: // Backspace
- if pos <= 0 {
- fmt.Print(beep)
- } else {
- n := len(getSuffixGlyphs(line[:pos], 1))
- line = append(line[:pos-n], line[pos:]...)
- pos -= n
- }
- case ctrlC:
- fmt.Println("^C")
- if s.multiLineMode {
- s.resetMultiLine(p, line, pos)
- }
- if s.ctrlCAborts {
- return "", ErrPromptAborted
- }
- line = line[:0]
- pos = 0
- fmt.Print(prompt)
- s.restartPrompt()
- // Unused keys
- case esc, tab, ctrlA, ctrlB, ctrlE, ctrlF, ctrlG, ctrlK, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlR, ctrlS,
- ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
- fallthrough
- // Catch unhandled control codes (anything <= 31)
- case 0, 28, 29, 30, 31:
- fmt.Print(beep)
- default:
- line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
- pos++
- }
- }
- }
- return string(line), nil
-}