Source file src/cmd/vendor/golang.org/x/term/terminal.go

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package term
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"io"
    11  	"runtime"
    12  	"strconv"
    13  	"sync"
    14  	"unicode/utf8"
    15  )
    16  
    17  // EscapeCodes contains escape sequences that can be written to the terminal in
    18  // order to achieve different styles of text.
    19  type EscapeCodes struct {
    20  	// Foreground colors
    21  	Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
    22  
    23  	// Reset all attributes
    24  	Reset []byte
    25  }
    26  
    27  var vt100EscapeCodes = EscapeCodes{
    28  	Black:   []byte{keyEscape, '[', '3', '0', 'm'},
    29  	Red:     []byte{keyEscape, '[', '3', '1', 'm'},
    30  	Green:   []byte{keyEscape, '[', '3', '2', 'm'},
    31  	Yellow:  []byte{keyEscape, '[', '3', '3', 'm'},
    32  	Blue:    []byte{keyEscape, '[', '3', '4', 'm'},
    33  	Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
    34  	Cyan:    []byte{keyEscape, '[', '3', '6', 'm'},
    35  	White:   []byte{keyEscape, '[', '3', '7', 'm'},
    36  
    37  	Reset: []byte{keyEscape, '[', '0', 'm'},
    38  }
    39  
    40  // A History provides a (possibly bounded) queue of input lines read by [Terminal.ReadLine].
    41  type History interface {
    42  	// Add will be called by [Terminal.ReadLine] to add
    43  	// a new, most recent entry to the history.
    44  	// It is allowed to drop any entry, including
    45  	// the entry being added (e.g., if it's deemed an invalid entry),
    46  	// the least-recent entry (e.g., to keep the history bounded),
    47  	// or any other entry.
    48  	Add(entry string)
    49  
    50  	// Len returns the number of entries in the history.
    51  	Len() int
    52  
    53  	// At returns an entry from the history.
    54  	// Index 0 is the most-recently added entry and
    55  	// index Len()-1 is the least-recently added entry.
    56  	// If index is < 0 or >= Len(), it panics.
    57  	At(idx int) string
    58  }
    59  
    60  // Terminal contains the state for running a VT100 terminal that is capable of
    61  // reading lines of input.
    62  type Terminal struct {
    63  	// AutoCompleteCallback, if non-null, is called for each keypress with
    64  	// the full input line and the current position of the cursor (in
    65  	// bytes, as an index into |line|). If it returns ok=false, the key
    66  	// press is processed normally. Otherwise it returns a replacement line
    67  	// and the new cursor position.
    68  	//
    69  	// This will be disabled during ReadPassword.
    70  	AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
    71  
    72  	// Escape contains a pointer to the escape codes for this terminal.
    73  	// It's always a valid pointer, although the escape codes themselves
    74  	// may be empty if the terminal doesn't support them.
    75  	Escape *EscapeCodes
    76  
    77  	// lock protects the terminal and the state in this object from
    78  	// concurrent processing of a key press and a Write() call.
    79  	lock sync.Mutex
    80  
    81  	c      io.ReadWriter
    82  	prompt []rune
    83  
    84  	// line is the current line being entered.
    85  	line []rune
    86  	// pos is the logical position of the cursor in line
    87  	pos int
    88  	// echo is true if local echo is enabled
    89  	echo bool
    90  	// pasteActive is true iff there is a bracketed paste operation in
    91  	// progress.
    92  	pasteActive bool
    93  
    94  	// cursorX contains the current X value of the cursor where the left
    95  	// edge is 0. cursorY contains the row number where the first row of
    96  	// the current line is 0.
    97  	cursorX, cursorY int
    98  	// maxLine is the greatest value of cursorY so far.
    99  	maxLine int
   100  
   101  	termWidth, termHeight int
   102  
   103  	// outBuf contains the terminal data to be sent.
   104  	outBuf []byte
   105  	// remainder contains the remainder of any partial key sequences after
   106  	// a read. It aliases into inBuf.
   107  	remainder []byte
   108  	inBuf     [256]byte
   109  
   110  	// History records and retrieves lines of input read by [ReadLine] which
   111  	// a user can retrieve and navigate using the up and down arrow keys.
   112  	//
   113  	// It is not safe to call ReadLine concurrently with any methods on History.
   114  	//
   115  	// [NewTerminal] sets this to a default implementation that records the
   116  	// last 100 lines of input.
   117  	History History
   118  	// historyIndex stores the currently accessed history entry, where zero
   119  	// means the immediately previous entry.
   120  	historyIndex int
   121  	// When navigating up and down the history it's possible to return to
   122  	// the incomplete, initial line. That value is stored in
   123  	// historyPending.
   124  	historyPending string
   125  }
   126  
   127  // NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
   128  // a local terminal, that terminal must first have been put into raw mode.
   129  // prompt is a string that is written at the start of each input line (i.e.
   130  // "> ").
   131  func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
   132  	return &Terminal{
   133  		Escape:       &vt100EscapeCodes,
   134  		c:            c,
   135  		prompt:       []rune(prompt),
   136  		termWidth:    80,
   137  		termHeight:   24,
   138  		echo:         true,
   139  		historyIndex: -1,
   140  		History:      &stRingBuffer{},
   141  	}
   142  }
   143  
   144  const (
   145  	keyCtrlC     = 3
   146  	keyCtrlD     = 4
   147  	keyCtrlU     = 21
   148  	keyEnter     = '\r'
   149  	keyEscape    = 27
   150  	keyBackspace = 127
   151  	keyUnknown   = 0xd800 /* UTF-16 surrogate area */ + iota
   152  	keyUp
   153  	keyDown
   154  	keyLeft
   155  	keyRight
   156  	keyAltLeft
   157  	keyAltRight
   158  	keyHome
   159  	keyEnd
   160  	keyDeleteWord
   161  	keyDeleteLine
   162  	keyClearScreen
   163  	keyPasteStart
   164  	keyPasteEnd
   165  )
   166  
   167  var (
   168  	crlf       = []byte{'\r', '\n'}
   169  	pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
   170  	pasteEnd   = []byte{keyEscape, '[', '2', '0', '1', '~'}
   171  )
   172  
   173  // bytesToKey tries to parse a key sequence from b. If successful, it returns
   174  // the key and the remainder of the input. Otherwise it returns utf8.RuneError.
   175  func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
   176  	if len(b) == 0 {
   177  		return utf8.RuneError, nil
   178  	}
   179  
   180  	if !pasteActive {
   181  		switch b[0] {
   182  		case 1: // ^A
   183  			return keyHome, b[1:]
   184  		case 2: // ^B
   185  			return keyLeft, b[1:]
   186  		case 5: // ^E
   187  			return keyEnd, b[1:]
   188  		case 6: // ^F
   189  			return keyRight, b[1:]
   190  		case 8: // ^H
   191  			return keyBackspace, b[1:]
   192  		case 11: // ^K
   193  			return keyDeleteLine, b[1:]
   194  		case 12: // ^L
   195  			return keyClearScreen, b[1:]
   196  		case 23: // ^W
   197  			return keyDeleteWord, b[1:]
   198  		case 14: // ^N
   199  			return keyDown, b[1:]
   200  		case 16: // ^P
   201  			return keyUp, b[1:]
   202  		}
   203  	}
   204  
   205  	if b[0] != keyEscape {
   206  		if !utf8.FullRune(b) {
   207  			return utf8.RuneError, b
   208  		}
   209  		r, l := utf8.DecodeRune(b)
   210  		return r, b[l:]
   211  	}
   212  
   213  	if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
   214  		switch b[2] {
   215  		case 'A':
   216  			return keyUp, b[3:]
   217  		case 'B':
   218  			return keyDown, b[3:]
   219  		case 'C':
   220  			return keyRight, b[3:]
   221  		case 'D':
   222  			return keyLeft, b[3:]
   223  		case 'H':
   224  			return keyHome, b[3:]
   225  		case 'F':
   226  			return keyEnd, b[3:]
   227  		}
   228  	}
   229  
   230  	if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
   231  		switch b[5] {
   232  		case 'C':
   233  			return keyAltRight, b[6:]
   234  		case 'D':
   235  			return keyAltLeft, b[6:]
   236  		}
   237  	}
   238  
   239  	if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
   240  		return keyPasteStart, b[6:]
   241  	}
   242  
   243  	if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
   244  		return keyPasteEnd, b[6:]
   245  	}
   246  
   247  	// If we get here then we have a key that we don't recognise, or a
   248  	// partial sequence. It's not clear how one should find the end of a
   249  	// sequence without knowing them all, but it seems that [a-zA-Z~] only
   250  	// appears at the end of a sequence.
   251  	for i, c := range b[0:] {
   252  		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
   253  			return keyUnknown, b[i+1:]
   254  		}
   255  	}
   256  
   257  	return utf8.RuneError, b
   258  }
   259  
   260  // queue appends data to the end of t.outBuf
   261  func (t *Terminal) queue(data []rune) {
   262  	t.outBuf = append(t.outBuf, []byte(string(data))...)
   263  }
   264  
   265  var space = []rune{' '}
   266  
   267  func isPrintable(key rune) bool {
   268  	isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
   269  	return key >= 32 && !isInSurrogateArea
   270  }
   271  
   272  // moveCursorToPos appends data to t.outBuf which will move the cursor to the
   273  // given, logical position in the text.
   274  func (t *Terminal) moveCursorToPos(pos int) {
   275  	if !t.echo {
   276  		return
   277  	}
   278  
   279  	x := visualLength(t.prompt) + pos
   280  	y := x / t.termWidth
   281  	x = x % t.termWidth
   282  
   283  	up := 0
   284  	if y < t.cursorY {
   285  		up = t.cursorY - y
   286  	}
   287  
   288  	down := 0
   289  	if y > t.cursorY {
   290  		down = y - t.cursorY
   291  	}
   292  
   293  	left := 0
   294  	if x < t.cursorX {
   295  		left = t.cursorX - x
   296  	}
   297  
   298  	right := 0
   299  	if x > t.cursorX {
   300  		right = x - t.cursorX
   301  	}
   302  
   303  	t.cursorX = x
   304  	t.cursorY = y
   305  	t.move(up, down, left, right)
   306  }
   307  
   308  func (t *Terminal) move(up, down, left, right int) {
   309  	m := []rune{}
   310  
   311  	// 1 unit up can be expressed as ^[[A or ^[A
   312  	// 5 units up can be expressed as ^[[5A
   313  
   314  	if up == 1 {
   315  		m = append(m, keyEscape, '[', 'A')
   316  	} else if up > 1 {
   317  		m = append(m, keyEscape, '[')
   318  		m = append(m, []rune(strconv.Itoa(up))...)
   319  		m = append(m, 'A')
   320  	}
   321  
   322  	if down == 1 {
   323  		m = append(m, keyEscape, '[', 'B')
   324  	} else if down > 1 {
   325  		m = append(m, keyEscape, '[')
   326  		m = append(m, []rune(strconv.Itoa(down))...)
   327  		m = append(m, 'B')
   328  	}
   329  
   330  	if right == 1 {
   331  		m = append(m, keyEscape, '[', 'C')
   332  	} else if right > 1 {
   333  		m = append(m, keyEscape, '[')
   334  		m = append(m, []rune(strconv.Itoa(right))...)
   335  		m = append(m, 'C')
   336  	}
   337  
   338  	if left == 1 {
   339  		m = append(m, keyEscape, '[', 'D')
   340  	} else if left > 1 {
   341  		m = append(m, keyEscape, '[')
   342  		m = append(m, []rune(strconv.Itoa(left))...)
   343  		m = append(m, 'D')
   344  	}
   345  
   346  	t.queue(m)
   347  }
   348  
   349  func (t *Terminal) clearLineToRight() {
   350  	op := []rune{keyEscape, '[', 'K'}
   351  	t.queue(op)
   352  }
   353  
   354  const maxLineLength = 4096
   355  
   356  func (t *Terminal) setLine(newLine []rune, newPos int) {
   357  	if t.echo {
   358  		t.moveCursorToPos(0)
   359  		t.writeLine(newLine)
   360  		for i := len(newLine); i < len(t.line); i++ {
   361  			t.writeLine(space)
   362  		}
   363  		t.moveCursorToPos(newPos)
   364  	}
   365  	t.line = newLine
   366  	t.pos = newPos
   367  }
   368  
   369  func (t *Terminal) advanceCursor(places int) {
   370  	t.cursorX += places
   371  	t.cursorY += t.cursorX / t.termWidth
   372  	if t.cursorY > t.maxLine {
   373  		t.maxLine = t.cursorY
   374  	}
   375  	t.cursorX = t.cursorX % t.termWidth
   376  
   377  	if places > 0 && t.cursorX == 0 {
   378  		// Normally terminals will advance the current position
   379  		// when writing a character. But that doesn't happen
   380  		// for the last character in a line. However, when
   381  		// writing a character (except a new line) that causes
   382  		// a line wrap, the position will be advanced two
   383  		// places.
   384  		//
   385  		// So, if we are stopping at the end of a line, we
   386  		// need to write a newline so that our cursor can be
   387  		// advanced to the next line.
   388  		t.outBuf = append(t.outBuf, '\r', '\n')
   389  	}
   390  }
   391  
   392  func (t *Terminal) eraseNPreviousChars(n int) {
   393  	if n == 0 {
   394  		return
   395  	}
   396  
   397  	if t.pos < n {
   398  		n = t.pos
   399  	}
   400  	t.pos -= n
   401  	t.moveCursorToPos(t.pos)
   402  
   403  	copy(t.line[t.pos:], t.line[n+t.pos:])
   404  	t.line = t.line[:len(t.line)-n]
   405  	if t.echo {
   406  		t.writeLine(t.line[t.pos:])
   407  		for i := 0; i < n; i++ {
   408  			t.queue(space)
   409  		}
   410  		t.advanceCursor(n)
   411  		t.moveCursorToPos(t.pos)
   412  	}
   413  }
   414  
   415  // countToLeftWord returns then number of characters from the cursor to the
   416  // start of the previous word.
   417  func (t *Terminal) countToLeftWord() int {
   418  	if t.pos == 0 {
   419  		return 0
   420  	}
   421  
   422  	pos := t.pos - 1
   423  	for pos > 0 {
   424  		if t.line[pos] != ' ' {
   425  			break
   426  		}
   427  		pos--
   428  	}
   429  	for pos > 0 {
   430  		if t.line[pos] == ' ' {
   431  			pos++
   432  			break
   433  		}
   434  		pos--
   435  	}
   436  
   437  	return t.pos - pos
   438  }
   439  
   440  // countToRightWord returns then number of characters from the cursor to the
   441  // start of the next word.
   442  func (t *Terminal) countToRightWord() int {
   443  	pos := t.pos
   444  	for pos < len(t.line) {
   445  		if t.line[pos] == ' ' {
   446  			break
   447  		}
   448  		pos++
   449  	}
   450  	for pos < len(t.line) {
   451  		if t.line[pos] != ' ' {
   452  			break
   453  		}
   454  		pos++
   455  	}
   456  	return pos - t.pos
   457  }
   458  
   459  // visualLength returns the number of visible glyphs in s.
   460  func visualLength(runes []rune) int {
   461  	inEscapeSeq := false
   462  	length := 0
   463  
   464  	for _, r := range runes {
   465  		switch {
   466  		case inEscapeSeq:
   467  			if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
   468  				inEscapeSeq = false
   469  			}
   470  		case r == '\x1b':
   471  			inEscapeSeq = true
   472  		default:
   473  			length++
   474  		}
   475  	}
   476  
   477  	return length
   478  }
   479  
   480  // histroryAt unlocks the terminal and relocks it while calling History.At.
   481  func (t *Terminal) historyAt(idx int) (string, bool) {
   482  	t.lock.Unlock()     // Unlock to avoid deadlock if History methods use the output writer.
   483  	defer t.lock.Lock() // panic in At (or Len) protection.
   484  	if idx < 0 || idx >= t.History.Len() {
   485  		return "", false
   486  	}
   487  	return t.History.At(idx), true
   488  }
   489  
   490  // historyAdd unlocks the terminal and relocks it while calling History.Add.
   491  func (t *Terminal) historyAdd(entry string) {
   492  	t.lock.Unlock()     // Unlock to avoid deadlock if History methods use the output writer.
   493  	defer t.lock.Lock() // panic in Add protection.
   494  	t.History.Add(entry)
   495  }
   496  
   497  // handleKey processes the given key and, optionally, returns a line of text
   498  // that the user has entered.
   499  func (t *Terminal) handleKey(key rune) (line string, ok bool) {
   500  	if t.pasteActive && key != keyEnter {
   501  		t.addKeyToLine(key)
   502  		return
   503  	}
   504  
   505  	switch key {
   506  	case keyBackspace:
   507  		if t.pos == 0 {
   508  			return
   509  		}
   510  		t.eraseNPreviousChars(1)
   511  	case keyAltLeft:
   512  		// move left by a word.
   513  		t.pos -= t.countToLeftWord()
   514  		t.moveCursorToPos(t.pos)
   515  	case keyAltRight:
   516  		// move right by a word.
   517  		t.pos += t.countToRightWord()
   518  		t.moveCursorToPos(t.pos)
   519  	case keyLeft:
   520  		if t.pos == 0 {
   521  			return
   522  		}
   523  		t.pos--
   524  		t.moveCursorToPos(t.pos)
   525  	case keyRight:
   526  		if t.pos == len(t.line) {
   527  			return
   528  		}
   529  		t.pos++
   530  		t.moveCursorToPos(t.pos)
   531  	case keyHome:
   532  		if t.pos == 0 {
   533  			return
   534  		}
   535  		t.pos = 0
   536  		t.moveCursorToPos(t.pos)
   537  	case keyEnd:
   538  		if t.pos == len(t.line) {
   539  			return
   540  		}
   541  		t.pos = len(t.line)
   542  		t.moveCursorToPos(t.pos)
   543  	case keyUp:
   544  		entry, ok := t.historyAt(t.historyIndex + 1)
   545  		if !ok {
   546  			return "", false
   547  		}
   548  		if t.historyIndex == -1 {
   549  			t.historyPending = string(t.line)
   550  		}
   551  		t.historyIndex++
   552  		runes := []rune(entry)
   553  		t.setLine(runes, len(runes))
   554  	case keyDown:
   555  		switch t.historyIndex {
   556  		case -1:
   557  			return
   558  		case 0:
   559  			runes := []rune(t.historyPending)
   560  			t.setLine(runes, len(runes))
   561  			t.historyIndex--
   562  		default:
   563  			entry, ok := t.historyAt(t.historyIndex - 1)
   564  			if ok {
   565  				t.historyIndex--
   566  				runes := []rune(entry)
   567  				t.setLine(runes, len(runes))
   568  			}
   569  		}
   570  	case keyEnter:
   571  		t.moveCursorToPos(len(t.line))
   572  		t.queue([]rune("\r\n"))
   573  		line = string(t.line)
   574  		ok = true
   575  		t.line = t.line[:0]
   576  		t.pos = 0
   577  		t.cursorX = 0
   578  		t.cursorY = 0
   579  		t.maxLine = 0
   580  	case keyDeleteWord:
   581  		// Delete zero or more spaces and then one or more characters.
   582  		t.eraseNPreviousChars(t.countToLeftWord())
   583  	case keyDeleteLine:
   584  		// Delete everything from the current cursor position to the
   585  		// end of line.
   586  		for i := t.pos; i < len(t.line); i++ {
   587  			t.queue(space)
   588  			t.advanceCursor(1)
   589  		}
   590  		t.line = t.line[:t.pos]
   591  		t.moveCursorToPos(t.pos)
   592  	case keyCtrlD:
   593  		// Erase the character under the current position.
   594  		// The EOF case when the line is empty is handled in
   595  		// readLine().
   596  		if t.pos < len(t.line) {
   597  			t.pos++
   598  			t.eraseNPreviousChars(1)
   599  		}
   600  	case keyCtrlU:
   601  		t.eraseNPreviousChars(t.pos)
   602  	case keyClearScreen:
   603  		// Erases the screen and moves the cursor to the home position.
   604  		t.queue([]rune("\x1b[2J\x1b[H"))
   605  		t.queue(t.prompt)
   606  		t.cursorX, t.cursorY = 0, 0
   607  		t.advanceCursor(visualLength(t.prompt))
   608  		t.setLine(t.line, t.pos)
   609  	default:
   610  		if t.AutoCompleteCallback != nil {
   611  			prefix := string(t.line[:t.pos])
   612  			suffix := string(t.line[t.pos:])
   613  
   614  			t.lock.Unlock()
   615  			newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
   616  			t.lock.Lock()
   617  
   618  			if completeOk {
   619  				t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
   620  				return
   621  			}
   622  		}
   623  		if !isPrintable(key) {
   624  			return
   625  		}
   626  		if len(t.line) == maxLineLength {
   627  			return
   628  		}
   629  		t.addKeyToLine(key)
   630  	}
   631  	return
   632  }
   633  
   634  // addKeyToLine inserts the given key at the current position in the current
   635  // line.
   636  func (t *Terminal) addKeyToLine(key rune) {
   637  	if len(t.line) == cap(t.line) {
   638  		newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
   639  		copy(newLine, t.line)
   640  		t.line = newLine
   641  	}
   642  	t.line = t.line[:len(t.line)+1]
   643  	copy(t.line[t.pos+1:], t.line[t.pos:])
   644  	t.line[t.pos] = key
   645  	if t.echo {
   646  		t.writeLine(t.line[t.pos:])
   647  	}
   648  	t.pos++
   649  	t.moveCursorToPos(t.pos)
   650  }
   651  
   652  func (t *Terminal) writeLine(line []rune) {
   653  	for len(line) != 0 {
   654  		remainingOnLine := t.termWidth - t.cursorX
   655  		todo := len(line)
   656  		if todo > remainingOnLine {
   657  			todo = remainingOnLine
   658  		}
   659  		t.queue(line[:todo])
   660  		t.advanceCursor(visualLength(line[:todo]))
   661  		line = line[todo:]
   662  	}
   663  }
   664  
   665  // writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
   666  func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
   667  	for len(buf) > 0 {
   668  		i := bytes.IndexByte(buf, '\n')
   669  		todo := len(buf)
   670  		if i >= 0 {
   671  			todo = i
   672  		}
   673  
   674  		var nn int
   675  		nn, err = w.Write(buf[:todo])
   676  		n += nn
   677  		if err != nil {
   678  			return n, err
   679  		}
   680  		buf = buf[todo:]
   681  
   682  		if i >= 0 {
   683  			if _, err = w.Write(crlf); err != nil {
   684  				return n, err
   685  			}
   686  			n++
   687  			buf = buf[1:]
   688  		}
   689  	}
   690  
   691  	return n, nil
   692  }
   693  
   694  func (t *Terminal) Write(buf []byte) (n int, err error) {
   695  	t.lock.Lock()
   696  	defer t.lock.Unlock()
   697  
   698  	if t.cursorX == 0 && t.cursorY == 0 {
   699  		// This is the easy case: there's nothing on the screen that we
   700  		// have to move out of the way.
   701  		return writeWithCRLF(t.c, buf)
   702  	}
   703  
   704  	// We have a prompt and possibly user input on the screen. We
   705  	// have to clear it first.
   706  	t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
   707  	t.cursorX = 0
   708  	t.clearLineToRight()
   709  
   710  	for t.cursorY > 0 {
   711  		t.move(1 /* up */, 0, 0, 0)
   712  		t.cursorY--
   713  		t.clearLineToRight()
   714  	}
   715  
   716  	if _, err = t.c.Write(t.outBuf); err != nil {
   717  		return
   718  	}
   719  	t.outBuf = t.outBuf[:0]
   720  
   721  	if n, err = writeWithCRLF(t.c, buf); err != nil {
   722  		return
   723  	}
   724  
   725  	t.writeLine(t.prompt)
   726  	if t.echo {
   727  		t.writeLine(t.line)
   728  	}
   729  
   730  	t.moveCursorToPos(t.pos)
   731  
   732  	if _, err = t.c.Write(t.outBuf); err != nil {
   733  		return
   734  	}
   735  	t.outBuf = t.outBuf[:0]
   736  	return
   737  }
   738  
   739  // ReadPassword temporarily changes the prompt and reads a password, without
   740  // echo, from the terminal.
   741  //
   742  // The AutoCompleteCallback is disabled during this call.
   743  func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
   744  	t.lock.Lock()
   745  	defer t.lock.Unlock()
   746  
   747  	oldPrompt := t.prompt
   748  	t.prompt = []rune(prompt)
   749  	t.echo = false
   750  	oldAutoCompleteCallback := t.AutoCompleteCallback
   751  	t.AutoCompleteCallback = nil
   752  	defer func() {
   753  		t.AutoCompleteCallback = oldAutoCompleteCallback
   754  	}()
   755  
   756  	line, err = t.readLine()
   757  
   758  	t.prompt = oldPrompt
   759  	t.echo = true
   760  
   761  	return
   762  }
   763  
   764  // ReadLine returns a line of input from the terminal.
   765  func (t *Terminal) ReadLine() (line string, err error) {
   766  	t.lock.Lock()
   767  	defer t.lock.Unlock()
   768  
   769  	return t.readLine()
   770  }
   771  
   772  func (t *Terminal) readLine() (line string, err error) {
   773  	// t.lock must be held at this point
   774  
   775  	if t.cursorX == 0 && t.cursorY == 0 {
   776  		t.writeLine(t.prompt)
   777  		t.c.Write(t.outBuf)
   778  		t.outBuf = t.outBuf[:0]
   779  	}
   780  
   781  	lineIsPasted := t.pasteActive
   782  
   783  	for {
   784  		rest := t.remainder
   785  		lineOk := false
   786  		for !lineOk {
   787  			var key rune
   788  			key, rest = bytesToKey(rest, t.pasteActive)
   789  			if key == utf8.RuneError {
   790  				break
   791  			}
   792  			if !t.pasteActive {
   793  				if key == keyCtrlD {
   794  					if len(t.line) == 0 {
   795  						return "", io.EOF
   796  					}
   797  				}
   798  				if key == keyCtrlC {
   799  					return "", io.EOF
   800  				}
   801  				if key == keyPasteStart {
   802  					t.pasteActive = true
   803  					if len(t.line) == 0 {
   804  						lineIsPasted = true
   805  					}
   806  					continue
   807  				}
   808  			} else if key == keyPasteEnd {
   809  				t.pasteActive = false
   810  				continue
   811  			}
   812  			if !t.pasteActive {
   813  				lineIsPasted = false
   814  			}
   815  			line, lineOk = t.handleKey(key)
   816  		}
   817  		if len(rest) > 0 {
   818  			n := copy(t.inBuf[:], rest)
   819  			t.remainder = t.inBuf[:n]
   820  		} else {
   821  			t.remainder = nil
   822  		}
   823  		t.c.Write(t.outBuf)
   824  		t.outBuf = t.outBuf[:0]
   825  		if lineOk {
   826  			if t.echo {
   827  				t.historyIndex = -1
   828  				t.historyAdd(line)
   829  			}
   830  			if lineIsPasted {
   831  				err = ErrPasteIndicator
   832  			}
   833  			return
   834  		}
   835  
   836  		// t.remainder is a slice at the beginning of t.inBuf
   837  		// containing a partial key sequence
   838  		readBuf := t.inBuf[len(t.remainder):]
   839  		var n int
   840  
   841  		t.lock.Unlock()
   842  		n, err = t.c.Read(readBuf)
   843  		t.lock.Lock()
   844  
   845  		if err != nil {
   846  			return
   847  		}
   848  
   849  		t.remainder = t.inBuf[:n+len(t.remainder)]
   850  	}
   851  }
   852  
   853  // SetPrompt sets the prompt to be used when reading subsequent lines.
   854  func (t *Terminal) SetPrompt(prompt string) {
   855  	t.lock.Lock()
   856  	defer t.lock.Unlock()
   857  
   858  	t.prompt = []rune(prompt)
   859  }
   860  
   861  func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
   862  	// Move cursor to column zero at the start of the line.
   863  	t.move(t.cursorY, 0, t.cursorX, 0)
   864  	t.cursorX, t.cursorY = 0, 0
   865  	t.clearLineToRight()
   866  	for t.cursorY < numPrevLines {
   867  		// Move down a line
   868  		t.move(0, 1, 0, 0)
   869  		t.cursorY++
   870  		t.clearLineToRight()
   871  	}
   872  	// Move back to beginning.
   873  	t.move(t.cursorY, 0, 0, 0)
   874  	t.cursorX, t.cursorY = 0, 0
   875  
   876  	t.queue(t.prompt)
   877  	t.advanceCursor(visualLength(t.prompt))
   878  	t.writeLine(t.line)
   879  	t.moveCursorToPos(t.pos)
   880  }
   881  
   882  func (t *Terminal) SetSize(width, height int) error {
   883  	t.lock.Lock()
   884  	defer t.lock.Unlock()
   885  
   886  	if width == 0 {
   887  		width = 1
   888  	}
   889  
   890  	oldWidth := t.termWidth
   891  	t.termWidth, t.termHeight = width, height
   892  
   893  	switch {
   894  	case width == oldWidth:
   895  		// If the width didn't change then nothing else needs to be
   896  		// done.
   897  		return nil
   898  	case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
   899  		// If there is nothing on current line and no prompt printed,
   900  		// just do nothing
   901  		return nil
   902  	case width < oldWidth:
   903  		// Some terminals (e.g. xterm) will truncate lines that were
   904  		// too long when shinking. Others, (e.g. gnome-terminal) will
   905  		// attempt to wrap them. For the former, repainting t.maxLine
   906  		// works great, but that behaviour goes badly wrong in the case
   907  		// of the latter because they have doubled every full line.
   908  
   909  		// We assume that we are working on a terminal that wraps lines
   910  		// and adjust the cursor position based on every previous line
   911  		// wrapping and turning into two. This causes the prompt on
   912  		// xterms to move upwards, which isn't great, but it avoids a
   913  		// huge mess with gnome-terminal.
   914  		if t.cursorX >= t.termWidth {
   915  			t.cursorX = t.termWidth - 1
   916  		}
   917  		t.cursorY *= 2
   918  		t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
   919  	case width > oldWidth:
   920  		// If the terminal expands then our position calculations will
   921  		// be wrong in the future because we think the cursor is
   922  		// |t.pos| chars into the string, but there will be a gap at
   923  		// the end of any wrapped line.
   924  		//
   925  		// But the position will actually be correct until we move, so
   926  		// we can move back to the beginning and repaint everything.
   927  		t.clearAndRepaintLinePlusNPrevious(t.maxLine)
   928  	}
   929  
   930  	_, err := t.c.Write(t.outBuf)
   931  	t.outBuf = t.outBuf[:0]
   932  	return err
   933  }
   934  
   935  type pasteIndicatorError struct{}
   936  
   937  func (pasteIndicatorError) Error() string {
   938  	return "terminal: ErrPasteIndicator not correctly handled"
   939  }
   940  
   941  // ErrPasteIndicator may be returned from ReadLine as the error, in addition
   942  // to valid line data. It indicates that bracketed paste mode is enabled and
   943  // that the returned line consists only of pasted data. Programs may wish to
   944  // interpret pasted data more literally than typed data.
   945  var ErrPasteIndicator = pasteIndicatorError{}
   946  
   947  // SetBracketedPasteMode requests that the terminal bracket paste operations
   948  // with markers. Not all terminals support this but, if it is supported, then
   949  // enabling this mode will stop any autocomplete callback from running due to
   950  // pastes. Additionally, any lines that are completely pasted will be returned
   951  // from ReadLine with the error set to ErrPasteIndicator.
   952  func (t *Terminal) SetBracketedPasteMode(on bool) {
   953  	if on {
   954  		io.WriteString(t.c, "\x1b[?2004h")
   955  	} else {
   956  		io.WriteString(t.c, "\x1b[?2004l")
   957  	}
   958  }
   959  
   960  // stRingBuffer is a ring buffer of strings.
   961  type stRingBuffer struct {
   962  	// entries contains max elements.
   963  	entries []string
   964  	max     int
   965  	// head contains the index of the element most recently added to the ring.
   966  	head int
   967  	// size contains the number of elements in the ring.
   968  	size int
   969  }
   970  
   971  func (s *stRingBuffer) Add(a string) {
   972  	if s.entries == nil {
   973  		const defaultNumEntries = 100
   974  		s.entries = make([]string, defaultNumEntries)
   975  		s.max = defaultNumEntries
   976  	}
   977  
   978  	s.head = (s.head + 1) % s.max
   979  	s.entries[s.head] = a
   980  	if s.size < s.max {
   981  		s.size++
   982  	}
   983  }
   984  
   985  func (s *stRingBuffer) Len() int {
   986  	return s.size
   987  }
   988  
   989  // At returns the value passed to the nth previous call to Add.
   990  // If n is zero then the immediately prior value is returned, if one, then the
   991  // next most recent, and so on. If such an element doesn't exist then ok is
   992  // false.
   993  func (s *stRingBuffer) At(n int) string {
   994  	if n < 0 || n >= s.size {
   995  		panic(fmt.Sprintf("term: history index [%d] out of range [0,%d)", n, s.size))
   996  	}
   997  	index := s.head - n
   998  	if index < 0 {
   999  		index += s.max
  1000  	}
  1001  	return s.entries[index]
  1002  }
  1003  
  1004  // readPasswordLine reads from reader until it finds \n or io.EOF.
  1005  // The slice returned does not include the \n.
  1006  // readPasswordLine also ignores any \r it finds.
  1007  // Windows uses \r as end of line. So, on Windows, readPasswordLine
  1008  // reads until it finds \r and ignores any \n it finds during processing.
  1009  func readPasswordLine(reader io.Reader) ([]byte, error) {
  1010  	var buf [1]byte
  1011  	var ret []byte
  1012  
  1013  	for {
  1014  		n, err := reader.Read(buf[:])
  1015  		if n > 0 {
  1016  			switch buf[0] {
  1017  			case '\b':
  1018  				if len(ret) > 0 {
  1019  					ret = ret[:len(ret)-1]
  1020  				}
  1021  			case '\n':
  1022  				if runtime.GOOS != "windows" {
  1023  					return ret, nil
  1024  				}
  1025  				// otherwise ignore \n
  1026  			case '\r':
  1027  				if runtime.GOOS == "windows" {
  1028  					return ret, nil
  1029  				}
  1030  				// otherwise ignore \r
  1031  			default:
  1032  				ret = append(ret, buf[0])
  1033  			}
  1034  			continue
  1035  		}
  1036  		if err != nil {
  1037  			if err == io.EOF && len(ret) > 0 {
  1038  				return ret, nil
  1039  			}
  1040  			return ret, err
  1041  		}
  1042  	}
  1043  }
  1044  

View as plain text