terminal.go 18 KB

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