line.go 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169
  1. // +build windows linux darwin openbsd freebsd netbsd
  2. package liner
  3. import (
  4. "bufio"
  5. "container/ring"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "os"
  10. "strings"
  11. "unicode"
  12. "unicode/utf8"
  13. )
  14. type action int
  15. const (
  16. left action = iota
  17. right
  18. up
  19. down
  20. home
  21. end
  22. insert
  23. del
  24. pageUp
  25. pageDown
  26. f1
  27. f2
  28. f3
  29. f4
  30. f5
  31. f6
  32. f7
  33. f8
  34. f9
  35. f10
  36. f11
  37. f12
  38. altB
  39. altBs // Alt+Backspace
  40. altD
  41. altF
  42. altY
  43. shiftTab
  44. wordLeft
  45. wordRight
  46. winch
  47. unknown
  48. )
  49. const (
  50. ctrlA = 1
  51. ctrlB = 2
  52. ctrlC = 3
  53. ctrlD = 4
  54. ctrlE = 5
  55. ctrlF = 6
  56. ctrlG = 7
  57. ctrlH = 8
  58. tab = 9
  59. lf = 10
  60. ctrlK = 11
  61. ctrlL = 12
  62. cr = 13
  63. ctrlN = 14
  64. ctrlO = 15
  65. ctrlP = 16
  66. ctrlQ = 17
  67. ctrlR = 18
  68. ctrlS = 19
  69. ctrlT = 20
  70. ctrlU = 21
  71. ctrlV = 22
  72. ctrlW = 23
  73. ctrlX = 24
  74. ctrlY = 25
  75. ctrlZ = 26
  76. esc = 27
  77. bs = 127
  78. )
  79. const (
  80. beep = "\a"
  81. )
  82. type tabDirection int
  83. const (
  84. tabForward tabDirection = iota
  85. tabReverse
  86. )
  87. func (s *State) refresh(prompt []rune, buf []rune, pos int) error {
  88. if s.columns == 0 {
  89. return ErrInternal
  90. }
  91. s.needRefresh = false
  92. if s.multiLineMode {
  93. return s.refreshMultiLine(prompt, buf, pos)
  94. }
  95. return s.refreshSingleLine(prompt, buf, pos)
  96. }
  97. func (s *State) refreshSingleLine(prompt []rune, buf []rune, pos int) error {
  98. s.cursorPos(0)
  99. _, err := fmt.Print(string(prompt))
  100. if err != nil {
  101. return err
  102. }
  103. pLen := countGlyphs(prompt)
  104. bLen := countGlyphs(buf)
  105. // on some OS / terminals extra column is needed to place the cursor char
  106. if cursorColumn {
  107. bLen++
  108. }
  109. pos = countGlyphs(buf[:pos])
  110. if pLen+bLen < s.columns {
  111. _, err = fmt.Print(string(buf))
  112. s.eraseLine()
  113. s.cursorPos(pLen + pos)
  114. } else {
  115. // Find space available
  116. space := s.columns - pLen
  117. space-- // space for cursor
  118. start := pos - space/2
  119. end := start + space
  120. if end > bLen {
  121. end = bLen
  122. start = end - space
  123. }
  124. if start < 0 {
  125. start = 0
  126. end = space
  127. }
  128. pos -= start
  129. // Leave space for markers
  130. if start > 0 {
  131. start++
  132. }
  133. if end < bLen {
  134. end--
  135. }
  136. startRune := len(getPrefixGlyphs(buf, start))
  137. line := getPrefixGlyphs(buf[startRune:], end-start)
  138. // Output
  139. if start > 0 {
  140. fmt.Print("{")
  141. }
  142. fmt.Print(string(line))
  143. if end < bLen {
  144. fmt.Print("}")
  145. }
  146. // Set cursor position
  147. s.eraseLine()
  148. s.cursorPos(pLen + pos)
  149. }
  150. return err
  151. }
  152. func (s *State) refreshMultiLine(prompt []rune, buf []rune, pos int) error {
  153. promptColumns := countMultiLineGlyphs(prompt, s.columns, 0)
  154. totalColumns := countMultiLineGlyphs(buf, s.columns, promptColumns)
  155. // on some OS / terminals extra column is needed to place the cursor char
  156. // if cursorColumn {
  157. // totalColumns++
  158. // }
  159. // it looks like Multiline mode always assume that a cursor need an extra column,
  160. // and always emit a newline if we are at the screen end, so no worarounds needed there
  161. totalRows := (totalColumns + s.columns - 1) / s.columns
  162. maxRows := s.maxRows
  163. if totalRows > s.maxRows {
  164. s.maxRows = totalRows
  165. }
  166. cursorRows := s.cursorRows
  167. if cursorRows == 0 {
  168. cursorRows = 1
  169. }
  170. /* First step: clear all the lines used before. To do so start by
  171. * going to the last row. */
  172. if maxRows-cursorRows > 0 {
  173. s.moveDown(maxRows - cursorRows)
  174. }
  175. /* Now for every row clear it, go up. */
  176. for i := 0; i < maxRows-1; i++ {
  177. s.cursorPos(0)
  178. s.eraseLine()
  179. s.moveUp(1)
  180. }
  181. /* Clean the top line. */
  182. s.cursorPos(0)
  183. s.eraseLine()
  184. /* Write the prompt and the current buffer content */
  185. if _, err := fmt.Print(string(prompt)); err != nil {
  186. return err
  187. }
  188. if _, err := fmt.Print(string(buf)); err != nil {
  189. return err
  190. }
  191. /* If we are at the very end of the screen with our prompt, we need to
  192. * emit a newline and move the prompt to the first column. */
  193. cursorColumns := countMultiLineGlyphs(buf[:pos], s.columns, promptColumns)
  194. if cursorColumns == totalColumns && totalColumns%s.columns == 0 {
  195. s.emitNewLine()
  196. s.cursorPos(0)
  197. totalRows++
  198. if totalRows > s.maxRows {
  199. s.maxRows = totalRows
  200. }
  201. }
  202. /* Move cursor to right position. */
  203. cursorRows = (cursorColumns + s.columns) / s.columns
  204. if s.cursorRows > 0 && totalRows-cursorRows > 0 {
  205. s.moveUp(totalRows - cursorRows)
  206. }
  207. /* Set column. */
  208. s.cursorPos(cursorColumns % s.columns)
  209. s.cursorRows = cursorRows
  210. return nil
  211. }
  212. func (s *State) resetMultiLine(prompt []rune, buf []rune, pos int) {
  213. columns := countMultiLineGlyphs(prompt, s.columns, 0)
  214. columns = countMultiLineGlyphs(buf[:pos], s.columns, columns)
  215. columns += 2 // ^C
  216. cursorRows := (columns + s.columns) / s.columns
  217. if s.maxRows-cursorRows > 0 {
  218. for i := 0; i < s.maxRows-cursorRows; i++ {
  219. fmt.Println() // always moves the cursor down or scrolls the window up as needed
  220. }
  221. }
  222. s.maxRows = 1
  223. s.cursorRows = 0
  224. }
  225. func longestCommonPrefix(strs []string) string {
  226. if len(strs) == 0 {
  227. return ""
  228. }
  229. longest := strs[0]
  230. for _, str := range strs[1:] {
  231. for !strings.HasPrefix(str, longest) {
  232. longest = longest[:len(longest)-1]
  233. }
  234. }
  235. // Remove trailing partial runes
  236. longest = strings.TrimRight(longest, "\uFFFD")
  237. return longest
  238. }
  239. func (s *State) circularTabs(items []string) func(tabDirection) (string, error) {
  240. item := -1
  241. return func(direction tabDirection) (string, error) {
  242. if direction == tabForward {
  243. if item < len(items)-1 {
  244. item++
  245. } else {
  246. item = 0
  247. }
  248. } else if direction == tabReverse {
  249. if item > 0 {
  250. item--
  251. } else {
  252. item = len(items) - 1
  253. }
  254. }
  255. return items[item], nil
  256. }
  257. }
  258. func calculateColumns(screenWidth int, items []string) (numColumns, numRows, maxWidth int) {
  259. for _, item := range items {
  260. if len(item) >= screenWidth {
  261. return 1, len(items), screenWidth - 1
  262. }
  263. if len(item) >= maxWidth {
  264. maxWidth = len(item) + 1
  265. }
  266. }
  267. numColumns = screenWidth / maxWidth
  268. numRows = len(items) / numColumns
  269. if len(items)%numColumns > 0 {
  270. numRows++
  271. }
  272. if len(items) <= numColumns {
  273. maxWidth = 0
  274. }
  275. return
  276. }
  277. func (s *State) printedTabs(items []string) func(tabDirection) (string, error) {
  278. numTabs := 1
  279. prefix := longestCommonPrefix(items)
  280. return func(direction tabDirection) (string, error) {
  281. if len(items) == 1 {
  282. return items[0], nil
  283. }
  284. if numTabs == 2 {
  285. if len(items) > 100 {
  286. fmt.Printf("\nDisplay all %d possibilities? (y or n) ", len(items))
  287. prompt:
  288. for {
  289. next, err := s.readNext()
  290. if err != nil {
  291. return prefix, err
  292. }
  293. if key, ok := next.(rune); ok {
  294. switch key {
  295. case 'n', 'N':
  296. return prefix, nil
  297. case 'y', 'Y':
  298. break prompt
  299. case ctrlC, ctrlD, cr, lf:
  300. s.restartPrompt()
  301. }
  302. }
  303. }
  304. }
  305. fmt.Println("")
  306. numColumns, numRows, maxWidth := calculateColumns(s.columns, items)
  307. for i := 0; i < numRows; i++ {
  308. for j := 0; j < numColumns*numRows; j += numRows {
  309. if i+j < len(items) {
  310. if maxWidth > 0 {
  311. fmt.Printf("%-*.[1]*s", maxWidth, items[i+j])
  312. } else {
  313. fmt.Printf("%v ", items[i+j])
  314. }
  315. }
  316. }
  317. fmt.Println("")
  318. }
  319. } else {
  320. numTabs++
  321. }
  322. return prefix, nil
  323. }
  324. }
  325. func (s *State) tabComplete(p []rune, line []rune, pos int) ([]rune, int, interface{}, error) {
  326. if s.completer == nil {
  327. return line, pos, rune(esc), nil
  328. }
  329. head, list, tail := s.completer(string(line), pos)
  330. if len(list) <= 0 {
  331. return line, pos, rune(esc), nil
  332. }
  333. hl := utf8.RuneCountInString(head)
  334. if len(list) == 1 {
  335. err := s.refresh(p, []rune(head+list[0]+tail), hl+utf8.RuneCountInString(list[0]))
  336. return []rune(head + list[0] + tail), hl + utf8.RuneCountInString(list[0]), rune(esc), err
  337. }
  338. direction := tabForward
  339. tabPrinter := s.circularTabs(list)
  340. if s.tabStyle == TabPrints {
  341. tabPrinter = s.printedTabs(list)
  342. }
  343. for {
  344. pick, err := tabPrinter(direction)
  345. if err != nil {
  346. return line, pos, rune(esc), err
  347. }
  348. err = s.refresh(p, []rune(head+pick+tail), hl+utf8.RuneCountInString(pick))
  349. if err != nil {
  350. return line, pos, rune(esc), err
  351. }
  352. next, err := s.readNext()
  353. if err != nil {
  354. return line, pos, rune(esc), err
  355. }
  356. if key, ok := next.(rune); ok {
  357. if key == tab {
  358. direction = tabForward
  359. continue
  360. }
  361. if key == esc {
  362. return line, pos, rune(esc), nil
  363. }
  364. }
  365. if a, ok := next.(action); ok && a == shiftTab {
  366. direction = tabReverse
  367. continue
  368. }
  369. return []rune(head + pick + tail), hl + utf8.RuneCountInString(pick), next, nil
  370. }
  371. }
  372. // reverse intelligent search, implements a bash-like history search.
  373. func (s *State) reverseISearch(origLine []rune, origPos int) ([]rune, int, interface{}, error) {
  374. p := "(reverse-i-search)`': "
  375. err := s.refresh([]rune(p), origLine, origPos)
  376. if err != nil {
  377. return origLine, origPos, rune(esc), err
  378. }
  379. line := []rune{}
  380. pos := 0
  381. foundLine := string(origLine)
  382. foundPos := origPos
  383. getLine := func() ([]rune, []rune, int) {
  384. search := string(line)
  385. prompt := "(reverse-i-search)`%s': "
  386. return []rune(fmt.Sprintf(prompt, search)), []rune(foundLine), foundPos
  387. }
  388. history, positions := s.getHistoryByPattern(string(line))
  389. historyPos := len(history) - 1
  390. for {
  391. next, err := s.readNext()
  392. if err != nil {
  393. return []rune(foundLine), foundPos, rune(esc), err
  394. }
  395. switch v := next.(type) {
  396. case rune:
  397. switch v {
  398. case ctrlR: // Search backwards
  399. if historyPos > 0 && historyPos < len(history) {
  400. historyPos--
  401. foundLine = history[historyPos]
  402. foundPos = positions[historyPos]
  403. } else {
  404. s.doBeep()
  405. }
  406. case ctrlS: // Search forward
  407. if historyPos < len(history)-1 && historyPos >= 0 {
  408. historyPos++
  409. foundLine = history[historyPos]
  410. foundPos = positions[historyPos]
  411. } else {
  412. s.doBeep()
  413. }
  414. case ctrlH, bs: // Backspace
  415. if pos <= 0 {
  416. s.doBeep()
  417. } else {
  418. n := len(getSuffixGlyphs(line[:pos], 1))
  419. line = append(line[:pos-n], line[pos:]...)
  420. pos -= n
  421. // For each char deleted, display the last matching line of history
  422. history, positions := s.getHistoryByPattern(string(line))
  423. historyPos = len(history) - 1
  424. if len(history) > 0 {
  425. foundLine = history[historyPos]
  426. foundPos = positions[historyPos]
  427. } else {
  428. foundLine = ""
  429. foundPos = 0
  430. }
  431. }
  432. case ctrlG: // Cancel
  433. return origLine, origPos, rune(esc), err
  434. case tab, cr, lf, ctrlA, ctrlB, ctrlD, ctrlE, ctrlF, ctrlK,
  435. ctrlL, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
  436. fallthrough
  437. case 0, ctrlC, esc, 28, 29, 30, 31:
  438. return []rune(foundLine), foundPos, next, err
  439. default:
  440. line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
  441. pos++
  442. // For each keystroke typed, display the last matching line of history
  443. history, positions = s.getHistoryByPattern(string(line))
  444. historyPos = len(history) - 1
  445. if len(history) > 0 {
  446. foundLine = history[historyPos]
  447. foundPos = positions[historyPos]
  448. } else {
  449. foundLine = ""
  450. foundPos = 0
  451. }
  452. }
  453. case action:
  454. return []rune(foundLine), foundPos, next, err
  455. }
  456. err = s.refresh(getLine())
  457. if err != nil {
  458. return []rune(foundLine), foundPos, rune(esc), err
  459. }
  460. }
  461. }
  462. // addToKillRing adds some text to the kill ring. If mode is 0 it adds it to a
  463. // new node in the end of the kill ring, and move the current pointer to the new
  464. // node. If mode is 1 or 2 it appends or prepends the text to the current entry
  465. // of the killRing.
  466. func (s *State) addToKillRing(text []rune, mode int) {
  467. // Don't use the same underlying array as text
  468. killLine := make([]rune, len(text))
  469. copy(killLine, text)
  470. // Point killRing to a newNode, procedure depends on the killring state and
  471. // append mode.
  472. if mode == 0 { // Add new node to killRing
  473. if s.killRing == nil { // if killring is empty, create a new one
  474. s.killRing = ring.New(1)
  475. } else if s.killRing.Len() >= KillRingMax { // if killring is "full"
  476. s.killRing = s.killRing.Next()
  477. } else { // Normal case
  478. s.killRing.Link(ring.New(1))
  479. s.killRing = s.killRing.Next()
  480. }
  481. } else {
  482. if s.killRing == nil { // if killring is empty, create a new one
  483. s.killRing = ring.New(1)
  484. s.killRing.Value = []rune{}
  485. }
  486. if mode == 1 { // Append to last entry
  487. killLine = append(s.killRing.Value.([]rune), killLine...)
  488. } else if mode == 2 { // Prepend to last entry
  489. killLine = append(killLine, s.killRing.Value.([]rune)...)
  490. }
  491. }
  492. // Save text in the current killring node
  493. s.killRing.Value = killLine
  494. }
  495. func (s *State) yank(p []rune, text []rune, pos int) ([]rune, int, interface{}, error) {
  496. if s.killRing == nil {
  497. return text, pos, rune(esc), nil
  498. }
  499. lineStart := text[:pos]
  500. lineEnd := text[pos:]
  501. var line []rune
  502. for {
  503. value := s.killRing.Value.([]rune)
  504. line = make([]rune, 0)
  505. line = append(line, lineStart...)
  506. line = append(line, value...)
  507. line = append(line, lineEnd...)
  508. pos = len(lineStart) + len(value)
  509. err := s.refresh(p, line, pos)
  510. if err != nil {
  511. return line, pos, 0, err
  512. }
  513. next, err := s.readNext()
  514. if err != nil {
  515. return line, pos, next, err
  516. }
  517. switch v := next.(type) {
  518. case rune:
  519. return line, pos, next, nil
  520. case action:
  521. switch v {
  522. case altY:
  523. s.killRing = s.killRing.Prev()
  524. default:
  525. return line, pos, next, nil
  526. }
  527. }
  528. }
  529. }
  530. // Prompt displays p and returns a line of user input, not including a trailing
  531. // newline character. An io.EOF error is returned if the user signals end-of-file
  532. // by pressing Ctrl-D. Prompt allows line editing if the terminal supports it.
  533. func (s *State) Prompt(prompt string) (string, error) {
  534. return s.PromptWithSuggestion(prompt, "", 0)
  535. }
  536. // PromptWithSuggestion displays prompt and an editable text with cursor at
  537. // given position. The cursor will be set to the end of the line if given position
  538. // is negative or greater than length of text (in runes). Returns a line of user input, not
  539. // including a trailing newline character. An io.EOF error is returned if the user
  540. // signals end-of-file by pressing Ctrl-D.
  541. func (s *State) PromptWithSuggestion(prompt string, text string, pos int) (string, error) {
  542. for _, r := range prompt {
  543. if unicode.Is(unicode.C, r) {
  544. return "", ErrInvalidPrompt
  545. }
  546. }
  547. if s.inputRedirected || !s.terminalSupported {
  548. return s.promptUnsupported(prompt)
  549. }
  550. p := []rune(prompt)
  551. const minWorkingSpace = 10
  552. if s.columns < countGlyphs(p)+minWorkingSpace {
  553. return s.tooNarrow(prompt)
  554. }
  555. if s.outputRedirected {
  556. return "", ErrNotTerminalOutput
  557. }
  558. s.historyMutex.RLock()
  559. defer s.historyMutex.RUnlock()
  560. fmt.Print(prompt)
  561. var line = []rune(text)
  562. historyEnd := ""
  563. var historyPrefix []string
  564. historyPos := 0
  565. historyStale := true
  566. historyAction := false // used to mark history related actions
  567. killAction := 0 // used to mark kill related actions
  568. defer s.stopPrompt()
  569. if pos < 0 || len(line) < pos {
  570. pos = len(line)
  571. }
  572. if len(line) > 0 {
  573. err := s.refresh(p, line, pos)
  574. if err != nil {
  575. return "", err
  576. }
  577. }
  578. restart:
  579. s.startPrompt()
  580. s.getColumns()
  581. mainLoop:
  582. for {
  583. next, err := s.readNext()
  584. haveNext:
  585. if err != nil {
  586. if s.shouldRestart != nil && s.shouldRestart(err) {
  587. goto restart
  588. }
  589. return "", err
  590. }
  591. historyAction = false
  592. switch v := next.(type) {
  593. case rune:
  594. switch v {
  595. case cr, lf:
  596. if s.needRefresh {
  597. err := s.refresh(p, line, pos)
  598. if err != nil {
  599. return "", err
  600. }
  601. }
  602. if s.multiLineMode {
  603. s.resetMultiLine(p, line, pos)
  604. }
  605. fmt.Println()
  606. break mainLoop
  607. case ctrlA: // Start of line
  608. pos = 0
  609. s.needRefresh = true
  610. case ctrlE: // End of line
  611. pos = len(line)
  612. s.needRefresh = true
  613. case ctrlB: // left
  614. if pos > 0 {
  615. pos -= len(getSuffixGlyphs(line[:pos], 1))
  616. s.needRefresh = true
  617. } else {
  618. s.doBeep()
  619. }
  620. case ctrlF: // right
  621. if pos < len(line) {
  622. pos += len(getPrefixGlyphs(line[pos:], 1))
  623. s.needRefresh = true
  624. } else {
  625. s.doBeep()
  626. }
  627. case ctrlD: // del
  628. if pos == 0 && len(line) == 0 {
  629. // exit
  630. return "", io.EOF
  631. }
  632. // ctrlD is a potential EOF, so the rune reader shuts down.
  633. // Therefore, if it isn't actually an EOF, we must re-startPrompt.
  634. s.restartPrompt()
  635. if pos >= len(line) {
  636. s.doBeep()
  637. } else {
  638. n := len(getPrefixGlyphs(line[pos:], 1))
  639. line = append(line[:pos], line[pos+n:]...)
  640. s.needRefresh = true
  641. }
  642. case ctrlK: // delete remainder of line
  643. if pos >= len(line) {
  644. s.doBeep()
  645. } else {
  646. if killAction > 0 {
  647. s.addToKillRing(line[pos:], 1) // Add in apend mode
  648. } else {
  649. s.addToKillRing(line[pos:], 0) // Add in normal mode
  650. }
  651. killAction = 2 // Mark that there was a kill action
  652. line = line[:pos]
  653. s.needRefresh = true
  654. }
  655. case ctrlP: // up
  656. historyAction = true
  657. if historyStale {
  658. historyPrefix = s.getHistoryByPrefix(string(line))
  659. historyPos = len(historyPrefix)
  660. historyStale = false
  661. }
  662. if historyPos > 0 {
  663. if historyPos == len(historyPrefix) {
  664. historyEnd = string(line)
  665. }
  666. historyPos--
  667. line = []rune(historyPrefix[historyPos])
  668. pos = len(line)
  669. s.needRefresh = true
  670. } else {
  671. s.doBeep()
  672. }
  673. case ctrlN: // down
  674. historyAction = true
  675. if historyStale {
  676. historyPrefix = s.getHistoryByPrefix(string(line))
  677. historyPos = len(historyPrefix)
  678. historyStale = false
  679. }
  680. if historyPos < len(historyPrefix) {
  681. historyPos++
  682. if historyPos == len(historyPrefix) {
  683. line = []rune(historyEnd)
  684. } else {
  685. line = []rune(historyPrefix[historyPos])
  686. }
  687. pos = len(line)
  688. s.needRefresh = true
  689. } else {
  690. s.doBeep()
  691. }
  692. case ctrlT: // transpose prev glyph with glyph under cursor
  693. if len(line) < 2 || pos < 1 {
  694. s.doBeep()
  695. } else {
  696. if pos == len(line) {
  697. pos -= len(getSuffixGlyphs(line, 1))
  698. }
  699. prev := getSuffixGlyphs(line[:pos], 1)
  700. next := getPrefixGlyphs(line[pos:], 1)
  701. scratch := make([]rune, len(prev))
  702. copy(scratch, prev)
  703. copy(line[pos-len(prev):], next)
  704. copy(line[pos-len(prev)+len(next):], scratch)
  705. pos += len(next)
  706. s.needRefresh = true
  707. }
  708. case ctrlL: // clear screen
  709. s.eraseScreen()
  710. s.needRefresh = true
  711. case ctrlC: // reset
  712. fmt.Println("^C")
  713. if s.multiLineMode {
  714. s.resetMultiLine(p, line, pos)
  715. }
  716. if s.ctrlCAborts {
  717. return "", ErrPromptAborted
  718. }
  719. line = line[:0]
  720. pos = 0
  721. fmt.Print(prompt)
  722. s.restartPrompt()
  723. case ctrlH, bs: // Backspace
  724. if pos <= 0 {
  725. s.doBeep()
  726. } else {
  727. n := len(getSuffixGlyphs(line[:pos], 1))
  728. line = append(line[:pos-n], line[pos:]...)
  729. pos -= n
  730. s.needRefresh = true
  731. }
  732. case ctrlU: // Erase line before cursor
  733. if killAction > 0 {
  734. s.addToKillRing(line[:pos], 2) // Add in prepend mode
  735. } else {
  736. s.addToKillRing(line[:pos], 0) // Add in normal mode
  737. }
  738. killAction = 2 // Mark that there was some killing
  739. line = line[pos:]
  740. pos = 0
  741. s.needRefresh = true
  742. case ctrlW: // Erase word
  743. pos, line, killAction = s.eraseWord(pos, line, killAction)
  744. case ctrlY: // Paste from Yank buffer
  745. line, pos, next, err = s.yank(p, line, pos)
  746. goto haveNext
  747. case ctrlR: // Reverse Search
  748. line, pos, next, err = s.reverseISearch(line, pos)
  749. s.needRefresh = true
  750. goto haveNext
  751. case tab: // Tab completion
  752. line, pos, next, err = s.tabComplete(p, line, pos)
  753. goto haveNext
  754. // Catch keys that do nothing, but you don't want them to beep
  755. case esc:
  756. // DO NOTHING
  757. // Unused keys
  758. case ctrlG, ctrlO, ctrlQ, ctrlS, ctrlV, ctrlX, ctrlZ:
  759. fallthrough
  760. // Catch unhandled control codes (anything <= 31)
  761. case 0, 28, 29, 30, 31:
  762. s.doBeep()
  763. default:
  764. if pos == len(line) && !s.multiLineMode &&
  765. len(p)+len(line) < s.columns*4 && // Avoid countGlyphs on large lines
  766. countGlyphs(p)+countGlyphs(line) < s.columns-1 {
  767. line = append(line, v)
  768. fmt.Printf("%c", v)
  769. pos++
  770. } else {
  771. line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
  772. pos++
  773. s.needRefresh = true
  774. }
  775. }
  776. case action:
  777. switch v {
  778. case del:
  779. if pos >= len(line) {
  780. s.doBeep()
  781. } else {
  782. n := len(getPrefixGlyphs(line[pos:], 1))
  783. line = append(line[:pos], line[pos+n:]...)
  784. }
  785. case left:
  786. if pos > 0 {
  787. pos -= len(getSuffixGlyphs(line[:pos], 1))
  788. } else {
  789. s.doBeep()
  790. }
  791. case wordLeft, altB:
  792. if pos > 0 {
  793. var spaceHere, spaceLeft, leftKnown bool
  794. for {
  795. pos--
  796. if pos == 0 {
  797. break
  798. }
  799. if leftKnown {
  800. spaceHere = spaceLeft
  801. } else {
  802. spaceHere = unicode.IsSpace(line[pos])
  803. }
  804. spaceLeft, leftKnown = unicode.IsSpace(line[pos-1]), true
  805. if !spaceHere && spaceLeft {
  806. break
  807. }
  808. }
  809. } else {
  810. s.doBeep()
  811. }
  812. case right:
  813. if pos < len(line) {
  814. pos += len(getPrefixGlyphs(line[pos:], 1))
  815. } else {
  816. s.doBeep()
  817. }
  818. case wordRight, altF:
  819. if pos < len(line) {
  820. var spaceHere, spaceLeft, hereKnown bool
  821. for {
  822. pos++
  823. if pos == len(line) {
  824. break
  825. }
  826. if hereKnown {
  827. spaceLeft = spaceHere
  828. } else {
  829. spaceLeft = unicode.IsSpace(line[pos-1])
  830. }
  831. spaceHere, hereKnown = unicode.IsSpace(line[pos]), true
  832. if spaceHere && !spaceLeft {
  833. break
  834. }
  835. }
  836. } else {
  837. s.doBeep()
  838. }
  839. case up:
  840. historyAction = true
  841. if historyStale {
  842. historyPrefix = s.getHistoryByPrefix(string(line))
  843. historyPos = len(historyPrefix)
  844. historyStale = false
  845. }
  846. if historyPos > 0 {
  847. if historyPos == len(historyPrefix) {
  848. historyEnd = string(line)
  849. }
  850. historyPos--
  851. line = []rune(historyPrefix[historyPos])
  852. pos = len(line)
  853. } else {
  854. s.doBeep()
  855. }
  856. case down:
  857. historyAction = true
  858. if historyStale {
  859. historyPrefix = s.getHistoryByPrefix(string(line))
  860. historyPos = len(historyPrefix)
  861. historyStale = false
  862. }
  863. if historyPos < len(historyPrefix) {
  864. historyPos++
  865. if historyPos == len(historyPrefix) {
  866. line = []rune(historyEnd)
  867. } else {
  868. line = []rune(historyPrefix[historyPos])
  869. }
  870. pos = len(line)
  871. } else {
  872. s.doBeep()
  873. }
  874. case home: // Start of line
  875. pos = 0
  876. case end: // End of line
  877. pos = len(line)
  878. case altD: // Delete next word
  879. if pos == len(line) {
  880. s.doBeep()
  881. break
  882. }
  883. // Remove whitespace to the right
  884. var buf []rune // Store the deleted chars in a buffer
  885. for {
  886. if pos == len(line) || !unicode.IsSpace(line[pos]) {
  887. break
  888. }
  889. buf = append(buf, line[pos])
  890. line = append(line[:pos], line[pos+1:]...)
  891. }
  892. // Remove non-whitespace to the right
  893. for {
  894. if pos == len(line) || unicode.IsSpace(line[pos]) {
  895. break
  896. }
  897. buf = append(buf, line[pos])
  898. line = append(line[:pos], line[pos+1:]...)
  899. }
  900. // Save the result on the killRing
  901. if killAction > 0 {
  902. s.addToKillRing(buf, 2) // Add in prepend mode
  903. } else {
  904. s.addToKillRing(buf, 0) // Add in normal mode
  905. }
  906. killAction = 2 // Mark that there was some killing
  907. case altBs: // Erase word
  908. pos, line, killAction = s.eraseWord(pos, line, killAction)
  909. case winch: // Window change
  910. if s.multiLineMode {
  911. if s.maxRows-s.cursorRows > 0 {
  912. s.moveDown(s.maxRows - s.cursorRows)
  913. }
  914. for i := 0; i < s.maxRows-1; i++ {
  915. s.cursorPos(0)
  916. s.eraseLine()
  917. s.moveUp(1)
  918. }
  919. s.maxRows = 1
  920. s.cursorRows = 1
  921. }
  922. }
  923. s.needRefresh = true
  924. }
  925. if s.needRefresh && !s.inputWaiting() {
  926. err := s.refresh(p, line, pos)
  927. if err != nil {
  928. return "", err
  929. }
  930. }
  931. if !historyAction {
  932. historyStale = true
  933. }
  934. if killAction > 0 {
  935. killAction--
  936. }
  937. }
  938. return string(line), nil
  939. }
  940. // PasswordPrompt displays p, and then waits for user input. The input typed by
  941. // the user is not displayed in the terminal.
  942. func (s *State) PasswordPrompt(prompt string) (string, error) {
  943. for _, r := range prompt {
  944. if unicode.Is(unicode.C, r) {
  945. return "", ErrInvalidPrompt
  946. }
  947. }
  948. if !s.terminalSupported || s.columns == 0 {
  949. return "", errors.New("liner: function not supported in this terminal")
  950. }
  951. if s.inputRedirected {
  952. return s.promptUnsupported(prompt)
  953. }
  954. if s.outputRedirected {
  955. return "", ErrNotTerminalOutput
  956. }
  957. p := []rune(prompt)
  958. defer s.stopPrompt()
  959. restart:
  960. s.startPrompt()
  961. s.getColumns()
  962. fmt.Print(prompt)
  963. var line []rune
  964. pos := 0
  965. mainLoop:
  966. for {
  967. next, err := s.readNext()
  968. if err != nil {
  969. if s.shouldRestart != nil && s.shouldRestart(err) {
  970. goto restart
  971. }
  972. return "", err
  973. }
  974. switch v := next.(type) {
  975. case rune:
  976. switch v {
  977. case cr, lf:
  978. fmt.Println()
  979. break mainLoop
  980. case ctrlD: // del
  981. if pos == 0 && len(line) == 0 {
  982. // exit
  983. return "", io.EOF
  984. }
  985. // ctrlD is a potential EOF, so the rune reader shuts down.
  986. // Therefore, if it isn't actually an EOF, we must re-startPrompt.
  987. s.restartPrompt()
  988. case ctrlL: // clear screen
  989. s.eraseScreen()
  990. err := s.refresh(p, []rune{}, 0)
  991. if err != nil {
  992. return "", err
  993. }
  994. case ctrlH, bs: // Backspace
  995. if pos <= 0 {
  996. s.doBeep()
  997. } else {
  998. n := len(getSuffixGlyphs(line[:pos], 1))
  999. line = append(line[:pos-n], line[pos:]...)
  1000. pos -= n
  1001. }
  1002. case ctrlC:
  1003. fmt.Println("^C")
  1004. if s.ctrlCAborts {
  1005. return "", ErrPromptAborted
  1006. }
  1007. line = line[:0]
  1008. pos = 0
  1009. fmt.Print(prompt)
  1010. s.restartPrompt()
  1011. // Unused keys
  1012. case esc, tab, ctrlA, ctrlB, ctrlE, ctrlF, ctrlG, ctrlK, ctrlN, ctrlO, ctrlP, ctrlQ, ctrlR, ctrlS,
  1013. ctrlT, ctrlU, ctrlV, ctrlW, ctrlX, ctrlY, ctrlZ:
  1014. fallthrough
  1015. // Catch unhandled control codes (anything <= 31)
  1016. case 0, 28, 29, 30, 31:
  1017. s.doBeep()
  1018. default:
  1019. line = append(line[:pos], append([]rune{v}, line[pos:]...)...)
  1020. pos++
  1021. }
  1022. }
  1023. }
  1024. return string(line), nil
  1025. }
  1026. func (s *State) tooNarrow(prompt string) (string, error) {
  1027. // Docker and OpenWRT and etc sometimes return 0 column width
  1028. // Reset mode temporarily. Restore baked mode in case the terminal
  1029. // is wide enough for the next Prompt attempt.
  1030. m, merr := TerminalMode()
  1031. s.origMode.ApplyMode()
  1032. if merr == nil {
  1033. defer m.ApplyMode()
  1034. }
  1035. if s.r == nil {
  1036. // Windows does not always set s.r
  1037. s.r = bufio.NewReader(os.Stdin)
  1038. defer func() { s.r = nil }()
  1039. }
  1040. return s.promptUnsupported(prompt)
  1041. }
  1042. func (s *State) eraseWord(pos int, line []rune, killAction int) (int, []rune, int) {
  1043. if pos == 0 {
  1044. s.doBeep()
  1045. return pos, line, killAction
  1046. }
  1047. // Remove whitespace to the left
  1048. var buf []rune // Store the deleted chars in a buffer
  1049. for {
  1050. if pos == 0 || !unicode.IsSpace(line[pos-1]) {
  1051. break
  1052. }
  1053. buf = append(buf, line[pos-1])
  1054. line = append(line[:pos-1], line[pos:]...)
  1055. pos--
  1056. }
  1057. // Remove non-whitespace to the left
  1058. for {
  1059. if pos == 0 || unicode.IsSpace(line[pos-1]) {
  1060. break
  1061. }
  1062. buf = append(buf, line[pos-1])
  1063. line = append(line[:pos-1], line[pos:]...)
  1064. pos--
  1065. }
  1066. // Invert the buffer and save the result on the killRing
  1067. var newBuf []rune
  1068. for i := len(buf) - 1; i >= 0; i-- {
  1069. newBuf = append(newBuf, buf[i])
  1070. }
  1071. if killAction > 0 {
  1072. s.addToKillRing(newBuf, 2) // Add in prepend mode
  1073. } else {
  1074. s.addToKillRing(newBuf, 0) // Add in normal mode
  1075. }
  1076. killAction = 2 // Mark that there was some killing
  1077. s.needRefresh = true
  1078. return pos, line, killAction
  1079. }
  1080. func (s *State) doBeep() {
  1081. if !s.noBeep {
  1082. fmt.Print(beep)
  1083. }
  1084. }