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.
15 // EscapeCodes contains escape sequences that can be written to the terminal in
16 // order to achieve different styles of text.
17 type EscapeCodes struct {
19 Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
21 // Reset all attributes
25 var vt100EscapeCodes = EscapeCodes{
26 Black: []byte{keyEscape, '[', '3', '0', 'm'},
27 Red: []byte{keyEscape, '[', '3', '1', 'm'},
28 Green: []byte{keyEscape, '[', '3', '2', 'm'},
29 Yellow: []byte{keyEscape, '[', '3', '3', 'm'},
30 Blue: []byte{keyEscape, '[', '3', '4', 'm'},
31 Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
32 Cyan: []byte{keyEscape, '[', '3', '6', 'm'},
33 White: []byte{keyEscape, '[', '3', '7', 'm'},
35 Reset: []byte{keyEscape, '[', '0', 'm'},
38 // Terminal contains the state for running a VT100 terminal that is capable of
39 // reading lines of input.
40 type Terminal struct {
41 // AutoCompleteCallback, if non-null, is called for each keypress with
42 // the full input line and the current position of the cursor (in
43 // bytes, as an index into |line|). If it returns ok=false, the key
44 // press is processed normally. Otherwise it returns a replacement line
45 // and the new cursor position.
46 AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool)
48 // Escape contains a pointer to the escape codes for this terminal.
49 // It's always a valid pointer, although the escape codes themselves
50 // may be empty if the terminal doesn't support them.
53 // lock protects the terminal and the state in this object from
54 // concurrent processing of a key press and a Write() call.
60 // line is the current line being entered.
62 // pos is the logical position of the cursor in line
64 // echo is true if local echo is enabled
66 // pasteActive is true iff there is a bracketed paste operation in
70 // cursorX contains the current X value of the cursor where the left
71 // edge is 0. cursorY contains the row number where the first row of
72 // the current line is 0.
74 // maxLine is the greatest value of cursorY so far.
77 termWidth, termHeight int
79 // outBuf contains the terminal data to be sent.
81 // remainder contains the remainder of any partial key sequences after
82 // a read. It aliases into inBuf.
86 // history contains previously entered commands so that they can be
87 // accessed with the up and down keys.
89 // historyIndex stores the currently accessed history entry, where zero
90 // means the immediately previous entry.
92 // When navigating up and down the history it's possible to return to
93 // the incomplete, initial line. That value is stored in
98 // NewTerminal runs a VT100 terminal on the given ReadWriter. If the ReadWriter is
99 // a local terminal, that terminal must first have been put into raw mode.
100 // prompt is a string that is written at the start of each input line (i.e.
102 func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
104 Escape: &vt100EscapeCodes,
106 prompt: []rune(prompt),
120 keyUnknown = 0xd800 /* UTF-16 surrogate area */ + iota
137 crlf = []byte{'\r', '\n'}
138 pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
139 pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'}
142 // bytesToKey tries to parse a key sequence from b. If successful, it returns
143 // the key and the remainder of the input. Otherwise it returns utf8.RuneError.
144 func bytesToKey(b []byte, pasteActive bool) (rune, []byte) {
146 return utf8.RuneError, nil
152 return keyHome, b[1:]
156 return keyBackspace, b[1:]
158 return keyDeleteLine, b[1:]
160 return keyClearScreen, b[1:]
162 return keyDeleteWord, b[1:]
164 return keyDown, b[1:]
170 if b[0] != keyEscape {
171 if !utf8.FullRune(b) {
172 return utf8.RuneError, b
174 r, l := utf8.DecodeRune(b)
178 if !pasteActive && len(b) >= 3 && b[0] == keyEscape && b[1] == '[' {
183 return keyDown, b[3:]
185 return keyRight, b[3:]
187 return keyLeft, b[3:]
189 return keyHome, b[3:]
195 if !pasteActive && len(b) >= 6 && b[0] == keyEscape && b[1] == '[' && b[2] == '1' && b[3] == ';' && b[4] == '3' {
198 return keyAltRight, b[6:]
200 return keyAltLeft, b[6:]
204 if !pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteStart) {
205 return keyPasteStart, b[6:]
208 if pasteActive && len(b) >= 6 && bytes.Equal(b[:6], pasteEnd) {
209 return keyPasteEnd, b[6:]
212 // If we get here then we have a key that we don't recognise, or a
213 // partial sequence. It's not clear how one should find the end of a
214 // sequence without knowing them all, but it seems that [a-zA-Z~] only
215 // appears at the end of a sequence.
216 for i, c := range b[0:] {
217 if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '~' {
218 return keyUnknown, b[i+1:]
222 return utf8.RuneError, b
225 // queue appends data to the end of t.outBuf
226 func (t *Terminal) queue(data []rune) {
227 t.outBuf = append(t.outBuf, []byte(string(data))...)
230 var eraseUnderCursor = []rune{' ', keyEscape, '[', 'D'}
231 var space = []rune{' '}
233 func isPrintable(key rune) bool {
234 isInSurrogateArea := key >= 0xd800 && key <= 0xdbff
235 return key >= 32 && !isInSurrogateArea
238 // moveCursorToPos appends data to t.outBuf which will move the cursor to the
239 // given, logical position in the text.
240 func (t *Terminal) moveCursorToPos(pos int) {
245 x := visualLength(t.prompt) + pos
266 right = x - t.cursorX
271 t.move(up, down, left, right)
274 func (t *Terminal) move(up, down, left, right int) {
277 // 1 unit up can be expressed as ^[[A or ^[A
278 // 5 units up can be expressed as ^[[5A
281 m = append(m, keyEscape, '[', 'A')
283 m = append(m, keyEscape, '[')
284 m = append(m, []rune(strconv.Itoa(up))...)
289 m = append(m, keyEscape, '[', 'B')
291 m = append(m, keyEscape, '[')
292 m = append(m, []rune(strconv.Itoa(down))...)
297 m = append(m, keyEscape, '[', 'C')
298 } else if right > 1 {
299 m = append(m, keyEscape, '[')
300 m = append(m, []rune(strconv.Itoa(right))...)
305 m = append(m, keyEscape, '[', 'D')
307 m = append(m, keyEscape, '[')
308 m = append(m, []rune(strconv.Itoa(left))...)
315 func (t *Terminal) clearLineToRight() {
316 op := []rune{keyEscape, '[', 'K'}
320 const maxLineLength = 4096
322 func (t *Terminal) setLine(newLine []rune, newPos int) {
326 for i := len(newLine); i < len(t.line); i++ {
329 t.moveCursorToPos(newPos)
335 func (t *Terminal) advanceCursor(places int) {
337 t.cursorY += t.cursorX / t.termWidth
338 if t.cursorY > t.maxLine {
339 t.maxLine = t.cursorY
341 t.cursorX = t.cursorX % t.termWidth
343 if places > 0 && t.cursorX == 0 {
344 // Normally terminals will advance the current position
345 // when writing a character. But that doesn't happen
346 // for the last character in a line. However, when
347 // writing a character (except a new line) that causes
348 // a line wrap, the position will be advanced two
351 // So, if we are stopping at the end of a line, we
352 // need to write a newline so that our cursor can be
353 // advanced to the next line.
354 t.outBuf = append(t.outBuf, '\r', '\n')
358 func (t *Terminal) eraseNPreviousChars(n int) {
367 t.moveCursorToPos(t.pos)
369 copy(t.line[t.pos:], t.line[n+t.pos:])
370 t.line = t.line[:len(t.line)-n]
372 t.writeLine(t.line[t.pos:])
373 for i := 0; i < n; i++ {
377 t.moveCursorToPos(t.pos)
381 // countToLeftWord returns then number of characters from the cursor to the
382 // start of the previous word.
383 func (t *Terminal) countToLeftWord() int {
390 if t.line[pos] != ' ' {
396 if t.line[pos] == ' ' {
406 // countToRightWord returns then number of characters from the cursor to the
407 // start of the next word.
408 func (t *Terminal) countToRightWord() int {
410 for pos < len(t.line) {
411 if t.line[pos] == ' ' {
416 for pos < len(t.line) {
417 if t.line[pos] != ' ' {
425 // visualLength returns the number of visible glyphs in s.
426 func visualLength(runes []rune) int {
430 for _, r := range runes {
433 if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') {
446 // handleKey processes the given key and, optionally, returns a line of text
447 // that the user has entered.
448 func (t *Terminal) handleKey(key rune) (line string, ok bool) {
449 if t.pasteActive && key != keyEnter {
459 t.eraseNPreviousChars(1)
461 // move left by a word.
462 t.pos -= t.countToLeftWord()
463 t.moveCursorToPos(t.pos)
465 // move right by a word.
466 t.pos += t.countToRightWord()
467 t.moveCursorToPos(t.pos)
473 t.moveCursorToPos(t.pos)
475 if t.pos == len(t.line) {
479 t.moveCursorToPos(t.pos)
485 t.moveCursorToPos(t.pos)
487 if t.pos == len(t.line) {
491 t.moveCursorToPos(t.pos)
493 entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
497 if t.historyIndex == -1 {
498 t.historyPending = string(t.line)
501 runes := []rune(entry)
502 t.setLine(runes, len(runes))
504 switch t.historyIndex {
508 runes := []rune(t.historyPending)
509 t.setLine(runes, len(runes))
512 entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
515 runes := []rune(entry)
516 t.setLine(runes, len(runes))
520 t.moveCursorToPos(len(t.line))
521 t.queue([]rune("\r\n"))
522 line = string(t.line)
530 // Delete zero or more spaces and then one or more characters.
531 t.eraseNPreviousChars(t.countToLeftWord())
533 // Delete everything from the current cursor position to the
535 for i := t.pos; i < len(t.line); i++ {
539 t.line = t.line[:t.pos]
540 t.moveCursorToPos(t.pos)
542 // Erase the character under the current position.
543 // The EOF case when the line is empty is handled in
545 if t.pos < len(t.line) {
547 t.eraseNPreviousChars(1)
550 t.eraseNPreviousChars(t.pos)
552 // Erases the screen and moves the cursor to the home position.
553 t.queue([]rune("\x1b[2J\x1b[H"))
555 t.cursorX, t.cursorY = 0, 0
556 t.advanceCursor(visualLength(t.prompt))
557 t.setLine(t.line, t.pos)
559 if t.AutoCompleteCallback != nil {
560 prefix := string(t.line[:t.pos])
561 suffix := string(t.line[t.pos:])
564 newLine, newPos, completeOk := t.AutoCompleteCallback(prefix+suffix, len(prefix), key)
568 t.setLine([]rune(newLine), utf8.RuneCount([]byte(newLine)[:newPos]))
572 if !isPrintable(key) {
575 if len(t.line) == maxLineLength {
583 // addKeyToLine inserts the given key at the current position in the current
585 func (t *Terminal) addKeyToLine(key rune) {
586 if len(t.line) == cap(t.line) {
587 newLine := make([]rune, len(t.line), 2*(1+len(t.line)))
588 copy(newLine, t.line)
591 t.line = t.line[:len(t.line)+1]
592 copy(t.line[t.pos+1:], t.line[t.pos:])
595 t.writeLine(t.line[t.pos:])
598 t.moveCursorToPos(t.pos)
601 func (t *Terminal) writeLine(line []rune) {
603 remainingOnLine := t.termWidth - t.cursorX
605 if todo > remainingOnLine {
606 todo = remainingOnLine
609 t.advanceCursor(visualLength(line[:todo]))
614 // writeWithCRLF writes buf to w but replaces all occurrences of \n with \r\n.
615 func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
617 i := bytes.IndexByte(buf, '\n')
624 nn, err = w.Write(buf[:todo])
632 if _, err = w.Write(crlf); err != nil {
643 func (t *Terminal) Write(buf []byte) (n int, err error) {
645 defer t.lock.Unlock()
647 if t.cursorX == 0 && t.cursorY == 0 {
648 // This is the easy case: there's nothing on the screen that we
649 // have to move out of the way.
650 return writeWithCRLF(t.c, buf)
653 // We have a prompt and possibly user input on the screen. We
654 // have to clear it first.
655 t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
660 t.move(1 /* up */, 0, 0, 0)
665 if _, err = t.c.Write(t.outBuf); err != nil {
668 t.outBuf = t.outBuf[:0]
670 if n, err = writeWithCRLF(t.c, buf); err != nil {
674 t.writeLine(t.prompt)
679 t.moveCursorToPos(t.pos)
681 if _, err = t.c.Write(t.outBuf); err != nil {
684 t.outBuf = t.outBuf[:0]
688 // ReadPassword temporarily changes the prompt and reads a password, without
689 // echo, from the terminal.
690 func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
692 defer t.lock.Unlock()
694 oldPrompt := t.prompt
695 t.prompt = []rune(prompt)
698 line, err = t.readLine()
706 // ReadLine returns a line of input from the terminal.
707 func (t *Terminal) ReadLine() (line string, err error) {
709 defer t.lock.Unlock()
714 func (t *Terminal) readLine() (line string, err error) {
715 // t.lock must be held at this point
717 if t.cursorX == 0 && t.cursorY == 0 {
718 t.writeLine(t.prompt)
720 t.outBuf = t.outBuf[:0]
723 lineIsPasted := t.pasteActive
730 key, rest = bytesToKey(rest, t.pasteActive)
731 if key == utf8.RuneError {
736 if len(t.line) == 0 {
740 if key == keyPasteStart {
742 if len(t.line) == 0 {
747 } else if key == keyPasteEnd {
748 t.pasteActive = false
754 line, lineOk = t.handleKey(key)
757 n := copy(t.inBuf[:], rest)
758 t.remainder = t.inBuf[:n]
763 t.outBuf = t.outBuf[:0]
770 err = ErrPasteIndicator
775 // t.remainder is a slice at the beginning of t.inBuf
776 // containing a partial key sequence
777 readBuf := t.inBuf[len(t.remainder):]
781 n, err = t.c.Read(readBuf)
788 t.remainder = t.inBuf[:n+len(t.remainder)]
792 // SetPrompt sets the prompt to be used when reading subsequent lines.
793 func (t *Terminal) SetPrompt(prompt string) {
795 defer t.lock.Unlock()
797 t.prompt = []rune(prompt)
800 func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
801 // Move cursor to column zero at the start of the line.
802 t.move(t.cursorY, 0, t.cursorX, 0)
803 t.cursorX, t.cursorY = 0, 0
805 for t.cursorY < numPrevLines {
811 // Move back to beginning.
812 t.move(t.cursorY, 0, 0, 0)
813 t.cursorX, t.cursorY = 0, 0
816 t.advanceCursor(visualLength(t.prompt))
818 t.moveCursorToPos(t.pos)
821 func (t *Terminal) SetSize(width, height int) error {
823 defer t.lock.Unlock()
829 oldWidth := t.termWidth
830 t.termWidth, t.termHeight = width, height
833 case width == oldWidth:
834 // If the width didn't change then nothing else needs to be
837 case len(t.line) == 0 && t.cursorX == 0 && t.cursorY == 0:
838 // If there is nothing on current line and no prompt printed,
841 case width < oldWidth:
842 // Some terminals (e.g. xterm) will truncate lines that were
843 // too long when shinking. Others, (e.g. gnome-terminal) will
844 // attempt to wrap them. For the former, repainting t.maxLine
845 // works great, but that behaviour goes badly wrong in the case
846 // of the latter because they have doubled every full line.
848 // We assume that we are working on a terminal that wraps lines
849 // and adjust the cursor position based on every previous line
850 // wrapping and turning into two. This causes the prompt on
851 // xterms to move upwards, which isn't great, but it avoids a
852 // huge mess with gnome-terminal.
853 if t.cursorX >= t.termWidth {
854 t.cursorX = t.termWidth - 1
857 t.clearAndRepaintLinePlusNPrevious(t.maxLine * 2)
858 case width > oldWidth:
859 // If the terminal expands then our position calculations will
860 // be wrong in the future because we think the cursor is
861 // |t.pos| chars into the string, but there will be a gap at
862 // the end of any wrapped line.
864 // But the position will actually be correct until we move, so
865 // we can move back to the beginning and repaint everything.
866 t.clearAndRepaintLinePlusNPrevious(t.maxLine)
869 _, err := t.c.Write(t.outBuf)
870 t.outBuf = t.outBuf[:0]
874 type pasteIndicatorError struct{}
876 func (pasteIndicatorError) Error() string {
877 return "terminal: ErrPasteIndicator not correctly handled"
880 // ErrPasteIndicator may be returned from ReadLine as the error, in addition
881 // to valid line data. It indicates that bracketed paste mode is enabled and
882 // that the returned line consists only of pasted data. Programs may wish to
883 // interpret pasted data more literally than typed data.
884 var ErrPasteIndicator = pasteIndicatorError{}
886 // SetBracketedPasteMode requests that the terminal bracket paste operations
887 // with markers. Not all terminals support this but, if it is supported, then
888 // enabling this mode will stop any autocomplete callback from running due to
889 // pastes. Additionally, any lines that are completely pasted will be returned
890 // from ReadLine with the error set to ErrPasteIndicator.
891 func (t *Terminal) SetBracketedPasteMode(on bool) {
893 io.WriteString(t.c, "\x1b[?2004h")
895 io.WriteString(t.c, "\x1b[?2004l")
899 // stRingBuffer is a ring buffer of strings.
900 type stRingBuffer struct {
901 // entries contains max elements.
904 // head contains the index of the element most recently added to the ring.
906 // size contains the number of elements in the ring.
910 func (s *stRingBuffer) Add(a string) {
911 if s.entries == nil {
912 const defaultNumEntries = 100
913 s.entries = make([]string, defaultNumEntries)
914 s.max = defaultNumEntries
917 s.head = (s.head + 1) % s.max
918 s.entries[s.head] = a
924 // NthPreviousEntry returns the value passed to the nth previous call to Add.
925 // If n is zero then the immediately prior value is returned, if one, then the
926 // next most recent, and so on. If such an element doesn't exist then ok is
928 func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
936 return s.entries[index], true
939 // readPasswordLine reads from reader until it finds \n or io.EOF.
940 // The slice returned does not include the \n.
941 // readPasswordLine also ignores any \r it finds.
942 func readPasswordLine(reader io.Reader) ([]byte, error) {
947 n, err := reader.Read(buf[:])
953 // remove \r from passwords on Windows
955 ret = append(ret, buf[0])
960 if err == io.EOF && len(ret) > 0 {