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