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