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