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

View as plain text