terminal.go 14 KB

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