github.com/pengwynn/gh@v1.0.1-0.20140118055701-14327ca3942e/Godeps/_workspace/src/code.google.com/p/go.crypto/ssh/terminal/terminal.go (about)

     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 terminal
     6  
     7  import (
     8  	"io"
     9  	"sync"
    10  	"unicode/utf8"
    11  )
    12  
    13  // EscapeCodes contains escape sequences that can be written to the terminal in
    14  // order to achieve different styles of text.
    15  type EscapeCodes struct {
    16  	// Foreground colors
    17  	Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
    18  
    19  	// Reset all attributes
    20  	Reset []byte
    21  }
    22  
    23  var vt100EscapeCodes = EscapeCodes{
    24  	Black:   []byte{keyEscape, '[', '3', '0', 'm'},
    25  	Red:     []byte{keyEscape, '[', '3', '1', 'm'},
    26  	Green:   []byte{keyEscape, '[', '3', '2', 'm'},
    27  	Yellow:  []byte{keyEscape, '[', '3', '3', 'm'},
    28  	Blue:    []byte{keyEscape, '[', '3', '4', 'm'},
    29  	Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
    30  	Cyan:    []byte{keyEscape, '[', '3', '6', 'm'},
    31  	White:   []byte{keyEscape, '[', '3', '7', 'm'},
    32  
    33  	Reset: []byte{keyEscape, '[', '0', 'm'},
    34  }
    35  
    36  // Terminal contains the state for running a VT100 terminal that is capable of
    37  // reading lines of input.
    38  type Terminal struct {
    39  	// AutoCompleteCallback, if non-null, is called for each keypress with
    40  	// the full input line and the current position of the cursor (in
    41  	// bytes, as an index into |line|). If it returns ok=false, the key
    42  	// press is processed normally. Otherwise it returns a replacement line
    43  	// and the new cursor position.
    44  	AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
    45  
    46  	// Escape contains a pointer to the escape codes for this terminal.
    47  	// It's always a valid pointer, although the escape codes themselves
    48  	// may be empty if the terminal doesn't support them.
    49  	Escape *EscapeCodes
    50  
    51  	// lock protects the terminal and the state in this object from
    52  	// concurrent processing of a key press and a Write() call.
    53  	lock sync.Mutex
    54  
    55  	c      io.ReadWriter
    56  	prompt string
    57  
    58  	// line is the current line being entered.
    59  	line []rune
    60  	// pos is the logical position of the cursor in line
    61  	pos int
    62  	// echo is true if local echo is enabled
    63  	echo bool
    64  
    65  	// cursorX contains the current X value of the cursor where the left
    66  	// edge is 0. cursorY contains the row number where the first row of
    67  	// the current line is 0.
    68  	cursorX, cursorY int
    69  	// maxLine is the greatest value of cursorY so far.
    70  	maxLine int
    71  
    72  	termWidth, termHeight int
    73  
    74  	// outBuf contains the terminal data to be sent.
    75  	outBuf []byte
    76  	// remainder contains the remainder of any partial key sequences after
    77  	// a read. It aliases into inBuf.
    78  	remainder []byte
    79  	inBuf     [256]byte
    80  
    81  	// history contains previously entered commands so that they can be
    82  	// accessed with the up and down keys.
    83  	history stRingBuffer
    84  	// historyIndex stores the currently accessed history entry, where zero
    85  	// means the immediately previous entry.
    86  	historyIndex int
    87  	// When navigating up and down the history it's possible to return to
    88  	// the incomplete, initial line. That value is stored in
    89  	// historyPending.
    90  	historyPending string
    91  }
    92  
    93  // NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
    94  // a local terminal, that terminal must first have been put into raw mode.
    95  // prompt is a string that is written at the start of each input line (i.e.
    96  // "> ").
    97  func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
    98  	return &Terminal{
    99  		Escape:       &vt100EscapeCodes,
   100  		c:            c,
   101  		prompt:       prompt,
   102  		termWidth:    80,
   103  		termHeight:   24,
   104  		echo:         true,
   105  		historyIndex: -1,
   106  	}
   107  }
   108  
   109  const (
   110  	keyCtrlD     = 4
   111  	keyEnter     = '\r'
   112  	keyEscape    = 27
   113  	keyBackspace = 127
   114  	keyUnknown   = 0xd800 /* UTF-16 surrogate area */ + iota
   115  	keyUp
   116  	keyDown
   117  	keyLeft
   118  	keyRight
   119  	keyAltLeft
   120  	keyAltRight
   121  	keyHome
   122  	keyEnd
   123  	keyDeleteWord
   124  	keyDeleteLine
   125  )
   126  
   127  // bytesToKey tries to parse a key sequence from b. If successful, it returns
   128  // the key and the remainder of the input. Otherwise it returns utf8.RuneError.
   129  func bytesToKey(b []byte) (rune, []byte) {
   130  	if len(b) == 0 {
   131  		return utf8.RuneError, nil
   132  	}
   133  
   134  	switch b[0] {
   135  	case 1: // ^A
   136  		return keyHome, b[1:]
   137  	case 5: // ^E
   138  		return keyEnd, b[1:]
   139  	case 8: // ^H
   140  		return keyBackspace, b[1:]
   141  	case 11: // ^K
   142  		return keyDeleteLine, b[1:]
   143  	case 23: // ^W
   144  		return keyDeleteWord, b[1:]
   145  	}
   146  
   147  	if b[0] != keyEscape {
   148  		if !utf8.FullRune(b) {
   149  			return utf8.RuneError, b
   150  		}
   151  		r, l := utf8.DecodeRune(b)
   152  		return r, b[l:]
   153  	}
   154  
   155  	if len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
   156  		switch b[2] {
   157  		case 'A':
   158  			return keyUp, b[3:]
   159  		case 'B':
   160  			return keyDown, b[3:]
   161  		case 'C':
   162  			return keyRight, b[3:]
   163  		case 'D':
   164  			return keyLeft, b[3:]
   165  		}
   166  	}
   167  
   168  	if len(b) >= 3 && b[0] == keyEscape && b[1] == 'O' {
   169  		switch b[2] {
   170  		case 'H':
   171  			return keyHome, b[3:]
   172  		case 'F':
   173  			return keyEnd, b[3:]
   174  		}
   175  	}
   176  
   177  	if len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
   178  		switch b[5] {
   179  		case 'C':
   180  			return keyAltRight, b[6:]
   181  		case 'D':
   182  			return keyAltLeft, b[6:]
   183  		}
   184  	}
   185  
   186  	// If we get here then we have a key that we don't recognise, or a
   187  	// partial sequence. It's not clear how one should find the end of a
   188  	// sequence without knowing them all, but it seems that [a-zA-Z] only
   189  	// appears at the end of a sequence.
   190  	for i, c := range b[0:] {
   191  		if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' {
   192  			return keyUnknown, b[i+1:]
   193  		}
   194  	}
   195  
   196  	return utf8.RuneError, b
   197  }
   198  
   199  // queue appends data to the end of t.outBuf
   200  func (t *Terminal) queue(data []rune) {
   201  	t.outBuf = append(t.outBuf, []byte(string(data))...)
   202  }
   203  
   204  var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
   205  var space = []rune{' '}
   206  
   207  func isPrintable(key rune) bool {
   208  	isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
   209  	return key >= 32 && !isInSurrogateArea
   210  }
   211  
   212  // moveCursorToPos appends data to t.outBuf which will move the cursor to the
   213  // given, logical position in the text.
   214  func (t *Terminal) moveCursorToPos(pos int) {
   215  	if !t.echo {
   216  		return
   217  	}
   218  
   219  	x := len(t.prompt) + pos
   220  	y := x / t.termWidth
   221  	x = x % t.termWidth
   222  
   223  	up := 0
   224  	if y < t.cursorY {
   225  		up = t.cursorY - y
   226  	}
   227  
   228  	down := 0
   229  	if y > t.cursorY {
   230  		down = y - t.cursorY
   231  	}
   232  
   233  	left := 0
   234  	if x < t.cursorX {
   235  		left = t.cursorX - x
   236  	}
   237  
   238  	right := 0
   239  	if x > t.cursorX {
   240  		right = x - t.cursorX
   241  	}
   242  
   243  	t.cursorX = x
   244  	t.cursorY = y
   245  	t.move(up, down, left, right)
   246  }
   247  
   248  func (t *Terminal) move(up, down, left, right int) {
   249  	movement := make([]rune, 3*(up+down+left+right))
   250  	m := movement
   251  	for i := 0; i < up; i++ {
   252  		m[0] = keyEscape
   253  		m[1] = '['
   254  		m[2] = 'A'
   255  		m = m[3:]
   256  	}
   257  	for i := 0; i < down; i++ {
   258  		m[0] = keyEscape
   259  		m[1] = '['
   260  		m[2] = 'B'
   261  		m = m[3:]
   262  	}
   263  	for i := 0; i < left; i++ {
   264  		m[0] = keyEscape
   265  		m[1] = '['
   266  		m[2] = 'D'
   267  		m = m[3:]
   268  	}
   269  	for i := 0; i < right; i++ {
   270  		m[0] = keyEscape
   271  		m[1] = '['
   272  		m[2] = 'C'
   273  		m = m[3:]
   274  	}
   275  
   276  	t.queue(movement)
   277  }
   278  
   279  func (t *Terminal) clearLineToRight() {
   280  	op := []rune{keyEscape, '[', 'K'}
   281  	t.queue(op)
   282  }
   283  
   284  const maxLineLength = 4096
   285  
   286  func (t *Terminal) setLine(newLine []rune, newPos int) {
   287  	if t.echo {
   288  		t.moveCursorToPos(0)
   289  		t.writeLine(newLine)
   290  		for i := len(newLine); i < len(t.line); i++ {
   291  			t.writeLine(space)
   292  		}
   293  		t.moveCursorToPos(newPos)
   294  	}
   295  	t.line = newLine
   296  	t.pos = newPos
   297  }
   298  
   299  func (t *Terminal) eraseNPreviousChars(n int) {
   300  	if n == 0 {
   301  		return
   302  	}
   303  
   304  	if t.pos < n {
   305  		n = t.pos
   306  	}
   307  	t.pos -= n
   308  	t.moveCursorToPos(t.pos)
   309  
   310  	copy(t.line[t.pos:], t.line[n+t.pos:])
   311  	t.line = t.line[:len(t.line)-n]
   312  	if t.echo {
   313  		t.writeLine(t.line[t.pos:])
   314  		for i := 0; i < n; i++ {
   315  			t.queue(space)
   316  		}
   317  		t.cursorX += n
   318  		t.moveCursorToPos(t.pos)
   319  	}
   320  }
   321  
   322  // countToLeftWord returns then number of characters from the cursor to the
   323  // start of the previous word.
   324  func (t *Terminal) countToLeftWord() int {
   325  	if t.pos == 0 {
   326  		return 0
   327  	}
   328  
   329  	pos := t.pos - 1
   330  	for pos > 0 {
   331  		if t.line[pos] != ' ' {
   332  			break
   333  		}
   334  		pos--
   335  	}
   336  	for pos > 0 {
   337  		if t.line[pos] == ' ' {
   338  			pos++
   339  			break
   340  		}
   341  		pos--
   342  	}
   343  
   344  	return t.pos - pos
   345  }
   346  
   347  // countToRightWord returns then number of characters from the cursor to the
   348  // start of the next word.
   349  func (t *Terminal) countToRightWord() int {
   350  	pos := t.pos
   351  	for pos < len(t.line) {
   352  		if t.line[pos] == ' ' {
   353  			break
   354  		}
   355  		pos++
   356  	}
   357  	for pos < len(t.line) {
   358  		if t.line[pos] != ' ' {
   359  			break
   360  		}
   361  		pos++
   362  	}
   363  	return pos - t.pos
   364  }
   365  
   366  // handleKey processes the given key and, optionally, returns a line of text
   367  // that the user has entered.
   368  func (t *Terminal) handleKey(key rune) (line string, ok bool) {
   369  	switch key {
   370  	case keyBackspace:
   371  		if t.pos == 0 {
   372  			return
   373  		}
   374  		t.eraseNPreviousChars(1)
   375  	case keyAltLeft:
   376  		// move left by a word.
   377  		t.pos -= t.countToLeftWord()
   378  		t.moveCursorToPos(t.pos)
   379  	case keyAltRight:
   380  		// move right by a word.
   381  		t.pos += t.countToRightWord()
   382  		t.moveCursorToPos(t.pos)
   383  	case keyLeft:
   384  		if t.pos == 0 {
   385  			return
   386  		}
   387  		t.pos--
   388  		t.moveCursorToPos(t.pos)
   389  	case keyRight:
   390  		if t.pos == len(t.line) {
   391  			return
   392  		}
   393  		t.pos++
   394  		t.moveCursorToPos(t.pos)
   395  	case keyHome:
   396  		if t.pos == 0 {
   397  			return
   398  		}
   399  		t.pos = 0
   400  		t.moveCursorToPos(t.pos)
   401  	case keyEnd:
   402  		if t.pos == len(t.line) {
   403  			return
   404  		}
   405  		t.pos = len(t.line)
   406  		t.moveCursorToPos(t.pos)
   407  	case keyUp:
   408  		entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
   409  		if !ok {
   410  			return "", false
   411  		}
   412  		if t.historyIndex == -1 {
   413  			t.historyPending = string(t.line)
   414  		}
   415  		t.historyIndex++
   416  		runes := []rune(entry)
   417  		t.setLine(runes, len(runes))
   418  	case keyDown:
   419  		switch t.historyIndex {
   420  		case -1:
   421  			return
   422  		case 0:
   423  			runes := []rune(t.historyPending)
   424  			t.setLine(runes, len(runes))
   425  			t.historyIndex--
   426  		default:
   427  			entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
   428  			if ok {
   429  				t.historyIndex--
   430  				runes := []rune(entry)
   431  				t.setLine(runes, len(runes))
   432  			}
   433  		}
   434  	case keyEnter:
   435  		t.moveCursorToPos(len(t.line))
   436  		t.queue([]rune("\r\n"))
   437  		line = string(t.line)
   438  		ok = true
   439  		t.line = t.line[:0]
   440  		t.pos = 0
   441  		t.cursorX = 0
   442  		t.cursorY = 0
   443  		t.maxLine = 0
   444  	case keyDeleteWord:
   445  		// Delete zero or more spaces and then one or more characters.
   446  		t.eraseNPreviousChars(t.countToLeftWord())
   447  	case keyDeleteLine:
   448  		// Delete everything from the current cursor position to the
   449  		// end of line.
   450  		for i := t.pos; i < len(t.line); i++ {
   451  			t.queue(space)
   452  			t.cursorX++
   453  		}
   454  		t.line = t.line[:t.pos]
   455  		t.moveCursorToPos(t.pos)
   456  	default:
   457  		if t.AutoCompleteCallback != nil {
   458  			prefix := string(t.line[:t.pos])
   459  			suffix := string(t.line[t.pos:])
   460  
   461  			t.lock.Unlock()
   462  			newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
   463  			t.lock.Lock()
   464  
   465  			if completeOk {
   466  				t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
   467  				return
   468  			}
   469  		}
   470  		if !isPrintable(key) {
   471  			return
   472  		}
   473  		if len(t.line) == maxLineLength {
   474  			return
   475  		}
   476  		if len(t.line) == cap(t.line) {
   477  			newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
   478  			copy(newLine, t.line)
   479  			t.line = newLine
   480  		}
   481  		t.line = t.line[:len(t.line)+1]
   482  		copy(t.line[t.pos+1:], t.line[t.pos:])
   483  		t.line[t.pos] = key
   484  		if t.echo {
   485  			t.writeLine(t.line[t.pos:])
   486  		}
   487  		t.pos++
   488  		t.moveCursorToPos(t.pos)
   489  	}
   490  	return
   491  }
   492  
   493  func (t *Terminal) writeLine(line []rune) {
   494  	for len(line) != 0 {
   495  		remainingOnLine := t.termWidth - t.cursorX
   496  		todo := len(line)
   497  		if todo > remainingOnLine {
   498  			todo = remainingOnLine
   499  		}
   500  		t.queue(line[:todo])
   501  		t.cursorX += todo
   502  		line = line[todo:]
   503  
   504  		if t.cursorX == t.termWidth {
   505  			t.cursorX = 0
   506  			t.cursorY++
   507  			if t.cursorY > t.maxLine {
   508  				t.maxLine = t.cursorY
   509  			}
   510  		}
   511  	}
   512  }
   513  
   514  func (t *Terminal) Write(buf []byte) (n int, err error) {
   515  	t.lock.Lock()
   516  	defer t.lock.Unlock()
   517  
   518  	if t.cursorX == 0 && t.cursorY == 0 {
   519  		// This is the easy case: there's nothing on the screen that we
   520  		// have to move out of the way.
   521  		return t.c.Write(buf)
   522  	}
   523  
   524  	// We have a prompt and possibly user input on the screen. We
   525  	// have to clear it first.
   526  	t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
   527  	t.cursorX = 0
   528  	t.clearLineToRight()
   529  
   530  	for t.cursorY > 0 {
   531  		t.move(1 /* up */, 0, 0, 0)
   532  		t.cursorY--
   533  		t.clearLineToRight()
   534  	}
   535  
   536  	if _, err = t.c.Write(t.outBuf); err != nil {
   537  		return
   538  	}
   539  	t.outBuf = t.outBuf[:0]
   540  
   541  	if n, err = t.c.Write(buf); err != nil {
   542  		return
   543  	}
   544  
   545  	t.queue([]rune(t.prompt))
   546  	chars := len(t.prompt)
   547  	if t.echo {
   548  		t.queue(t.line)
   549  		chars += len(t.line)
   550  	}
   551  	t.cursorX = chars % t.termWidth
   552  	t.cursorY = chars / t.termWidth
   553  	t.moveCursorToPos(t.pos)
   554  
   555  	if _, err = t.c.Write(t.outBuf); err != nil {
   556  		return
   557  	}
   558  	t.outBuf = t.outBuf[:0]
   559  	return
   560  }
   561  
   562  // ReadPassword temporarily changes the prompt and reads a password, without
   563  // echo, from the terminal.
   564  func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
   565  	t.lock.Lock()
   566  	defer t.lock.Unlock()
   567  
   568  	oldPrompt := t.prompt
   569  	t.prompt = prompt
   570  	t.echo = false
   571  
   572  	line, err = t.readLine()
   573  
   574  	t.prompt = oldPrompt
   575  	t.echo = true
   576  
   577  	return
   578  }
   579  
   580  // ReadLine returns a line of input from the terminal.
   581  func (t *Terminal) ReadLine() (line string, err error) {
   582  	t.lock.Lock()
   583  	defer t.lock.Unlock()
   584  
   585  	return t.readLine()
   586  }
   587  
   588  func (t *Terminal) readLine() (line string, err error) {
   589  	// t.lock must be held at this point
   590  
   591  	if t.cursorX == 0 && t.cursorY == 0 {
   592  		t.writeLine([]rune(t.prompt))
   593  		t.c.Write(t.outBuf)
   594  		t.outBuf = t.outBuf[:0]
   595  	}
   596  
   597  	for {
   598  		rest := t.remainder
   599  		lineOk := false
   600  		for !lineOk {
   601  			var key rune
   602  			key, rest = bytesToKey(rest)
   603  			if key == utf8.RuneError {
   604  				break
   605  			}
   606  			if key == keyCtrlD {
   607  				return "", io.EOF
   608  			}
   609  			line, lineOk = t.handleKey(key)
   610  		}
   611  		if len(rest) > 0 {
   612  			n := copy(t.inBuf[:], rest)
   613  			t.remainder = t.inBuf[:n]
   614  		} else {
   615  			t.remainder = nil
   616  		}
   617  		t.c.Write(t.outBuf)
   618  		t.outBuf = t.outBuf[:0]
   619  		if lineOk {
   620  			if t.echo {
   621  				t.historyIndex = -1
   622  				t.history.Add(line)
   623  			}
   624  			return
   625  		}
   626  
   627  		// t.remainder is a slice at the beginning of t.inBuf
   628  		// containing a partial key sequence
   629  		readBuf := t.inBuf[len(t.remainder):]
   630  		var n int
   631  
   632  		t.lock.Unlock()
   633  		n, err = t.c.Read(readBuf)
   634  		t.lock.Lock()
   635  
   636  		if err != nil {
   637  			return
   638  		}
   639  
   640  		t.remainder = t.inBuf[:n+len(t.remainder)]
   641  	}
   642  
   643  	panic("unreachable") // for Go 1.0.
   644  }
   645  
   646  // SetPrompt sets the prompt to be used when reading subsequent lines.
   647  func (t *Terminal) SetPrompt(prompt string) {
   648  	t.lock.Lock()
   649  	defer t.lock.Unlock()
   650  
   651  	t.prompt = prompt
   652  }
   653  
   654  func (t *Terminal) SetSize(width, height int) {
   655  	t.lock.Lock()
   656  	defer t.lock.Unlock()
   657  
   658  	t.termWidth, t.termHeight = width, height
   659  }
   660  
   661  // stRingBuffer is a ring buffer of strings.
   662  type stRingBuffer struct {
   663  	// entries contains max elements.
   664  	entries []string
   665  	max     int
   666  	// head contains the index of the element most recently added to the ring.
   667  	head int
   668  	// size contains the number of elements in the ring.
   669  	size int
   670  }
   671  
   672  func (s *stRingBuffer) Add(a string) {
   673  	if s.entries == nil {
   674  		const defaultNumEntries = 100
   675  		s.entries = make([]string, defaultNumEntries)
   676  		s.max = defaultNumEntries
   677  	}
   678  
   679  	s.head = (s.head + 1) % s.max
   680  	s.entries[s.head] = a
   681  	if s.size < s.max {
   682  		s.size++
   683  	}
   684  }
   685  
   686  // NthPreviousEntry returns the value passed to the nth previous call to Add.
   687  // If n is zero then the immediately prior value is returned, if one, then the
   688  // next most recent, and so on. If such an element doesn't exist then ok is
   689  // false.
   690  func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
   691  	if n >= s.size {
   692  		return "", false
   693  	}
   694  	index := s.head - n
   695  	if index < 0 {
   696  		index += s.max
   697  	}
   698  	return s.entries[index], true
   699  }