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