You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

terminal.go 22KB

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