buffer.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. package readline
  2. import (
  3. "fmt"
  4. "os"
  5. "github.com/emirpasic/gods/lists/arraylist"
  6. "github.com/mattn/go-runewidth"
  7. "golang.org/x/term"
  8. )
  9. type Buffer struct {
  10. DisplayPos int
  11. Pos int
  12. Buf *arraylist.List
  13. //LineHasSpace is an arraylist of bools to keep track of whether a line has a space at the end
  14. LineHasSpace *arraylist.List
  15. Prompt *Prompt
  16. LineWidth int
  17. Width int
  18. Height int
  19. }
  20. func NewBuffer(prompt *Prompt) (*Buffer, error) {
  21. fd := int(os.Stdout.Fd())
  22. width, height := 80, 24
  23. if termWidth, termHeight, err := term.GetSize(fd); err == nil {
  24. width, height = termWidth, termHeight
  25. }
  26. lwidth := width - len(prompt.prompt())
  27. b := &Buffer{
  28. DisplayPos: 0,
  29. Pos: 0,
  30. Buf: arraylist.New(),
  31. LineHasSpace: arraylist.New(),
  32. Prompt: prompt,
  33. Width: width,
  34. Height: height,
  35. LineWidth: lwidth,
  36. }
  37. return b, nil
  38. }
  39. func (b *Buffer) GetLineSpacing(line int) bool {
  40. hasSpace, _ := b.LineHasSpace.Get(line)
  41. if hasSpace == nil {
  42. return false
  43. }
  44. return hasSpace.(bool)
  45. }
  46. func (b *Buffer) MoveLeft() {
  47. if b.Pos > 0 {
  48. //asserts that we retrieve a rune
  49. if e, ok := b.Buf.Get(b.Pos - 1); ok {
  50. if r, ok := e.(rune); ok {
  51. rLength := runewidth.RuneWidth(r)
  52. if b.DisplayPos%b.LineWidth == 0 {
  53. fmt.Printf(CursorUp + CursorBOL + cursorRightN(b.Width))
  54. if rLength == 2 {
  55. fmt.Print(CursorLeft)
  56. }
  57. line := b.DisplayPos/b.LineWidth - 1
  58. hasSpace := b.GetLineSpacing(line)
  59. if hasSpace {
  60. b.DisplayPos -= 1
  61. fmt.Print(CursorLeft)
  62. }
  63. } else {
  64. fmt.Print(cursorLeftN(rLength))
  65. }
  66. b.Pos -= 1
  67. b.DisplayPos -= rLength
  68. }
  69. }
  70. }
  71. }
  72. func (b *Buffer) MoveLeftWord() {
  73. if b.Pos > 0 {
  74. var foundNonspace bool
  75. for {
  76. v, _ := b.Buf.Get(b.Pos - 1)
  77. if v == ' ' {
  78. if foundNonspace {
  79. break
  80. }
  81. } else {
  82. foundNonspace = true
  83. }
  84. b.MoveLeft()
  85. if b.Pos == 0 {
  86. break
  87. }
  88. }
  89. }
  90. }
  91. func (b *Buffer) MoveRight() {
  92. if b.Pos < b.Buf.Size() {
  93. if e, ok := b.Buf.Get(b.Pos); ok {
  94. if r, ok := e.(rune); ok {
  95. rLength := runewidth.RuneWidth(r)
  96. b.Pos += 1
  97. hasSpace := b.GetLineSpacing(b.DisplayPos / b.LineWidth)
  98. b.DisplayPos += rLength
  99. if b.DisplayPos%b.LineWidth == 0 {
  100. fmt.Printf(CursorDown + CursorBOL + cursorRightN(len(b.Prompt.prompt())))
  101. } else if (b.DisplayPos-rLength)%b.LineWidth == b.LineWidth-1 && hasSpace {
  102. fmt.Printf(CursorDown + CursorBOL + cursorRightN(len(b.Prompt.prompt())+rLength))
  103. b.DisplayPos += 1
  104. } else if b.LineHasSpace.Size() > 0 && b.DisplayPos%b.LineWidth == b.LineWidth-1 && hasSpace {
  105. fmt.Printf(CursorDown + CursorBOL + cursorRightN(len(b.Prompt.prompt())))
  106. b.DisplayPos += 1
  107. } else {
  108. fmt.Print(cursorRightN(rLength))
  109. }
  110. }
  111. }
  112. }
  113. }
  114. func (b *Buffer) MoveRightWord() {
  115. if b.Pos < b.Buf.Size() {
  116. for {
  117. b.MoveRight()
  118. v, _ := b.Buf.Get(b.Pos)
  119. if v == ' ' {
  120. break
  121. }
  122. if b.Pos == b.Buf.Size() {
  123. break
  124. }
  125. }
  126. }
  127. }
  128. func (b *Buffer) MoveToStart() {
  129. if b.Pos > 0 {
  130. currLine := b.DisplayPos / b.LineWidth
  131. if currLine > 0 {
  132. for range currLine {
  133. fmt.Print(CursorUp)
  134. }
  135. }
  136. fmt.Printf(CursorBOL + cursorRightN(len(b.Prompt.prompt())))
  137. b.Pos = 0
  138. b.DisplayPos = 0
  139. }
  140. }
  141. func (b *Buffer) MoveToEnd() {
  142. if b.Pos < b.Buf.Size() {
  143. currLine := b.DisplayPos / b.LineWidth
  144. totalLines := b.DisplaySize() / b.LineWidth
  145. if currLine < totalLines {
  146. for range totalLines - currLine {
  147. fmt.Print(CursorDown)
  148. }
  149. remainder := b.DisplaySize() % b.LineWidth
  150. fmt.Printf(CursorBOL + cursorRightN(len(b.Prompt.prompt())+remainder))
  151. } else {
  152. fmt.Print(cursorRightN(b.DisplaySize() - b.DisplayPos))
  153. }
  154. b.Pos = b.Buf.Size()
  155. b.DisplayPos = b.DisplaySize()
  156. }
  157. }
  158. func (b *Buffer) DisplaySize() int {
  159. sum := 0
  160. for i := range b.Buf.Size() {
  161. if e, ok := b.Buf.Get(i); ok {
  162. if r, ok := e.(rune); ok {
  163. sum += runewidth.RuneWidth(r)
  164. }
  165. }
  166. }
  167. return sum
  168. }
  169. func (b *Buffer) Add(r rune) {
  170. if b.Pos == b.Buf.Size() {
  171. b.AddChar(r, false)
  172. } else {
  173. b.AddChar(r, true)
  174. }
  175. }
  176. func (b *Buffer) AddChar(r rune, insert bool) {
  177. rLength := runewidth.RuneWidth(r)
  178. b.DisplayPos += rLength
  179. if b.Pos > 0 {
  180. if b.DisplayPos%b.LineWidth == 0 {
  181. fmt.Printf("%c", r)
  182. fmt.Printf("\n%s", b.Prompt.AltPrompt)
  183. if insert {
  184. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth-1, false)
  185. } else {
  186. b.LineHasSpace.Add(false)
  187. }
  188. // this case occurs when a double-width rune crosses the line boundary
  189. } else if b.DisplayPos%b.LineWidth < (b.DisplayPos-rLength)%b.LineWidth {
  190. if insert {
  191. fmt.Print(ClearToEOL)
  192. }
  193. fmt.Printf("\n%s", b.Prompt.AltPrompt)
  194. b.DisplayPos += 1
  195. fmt.Printf("%c", r)
  196. if insert {
  197. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth-1, true)
  198. } else {
  199. b.LineHasSpace.Add(true)
  200. }
  201. } else {
  202. fmt.Printf("%c", r)
  203. }
  204. } else {
  205. fmt.Printf("%c", r)
  206. }
  207. if insert {
  208. b.Buf.Insert(b.Pos, r)
  209. } else {
  210. b.Buf.Add(r)
  211. }
  212. b.Pos += 1
  213. if insert {
  214. b.drawRemaining()
  215. }
  216. }
  217. func (b *Buffer) countRemainingLineWidth(place int) int {
  218. var sum int
  219. counter := -1
  220. var prevLen int
  221. for place <= b.LineWidth {
  222. counter += 1
  223. sum += prevLen
  224. if e, ok := b.Buf.Get(b.Pos + counter); ok {
  225. if r, ok := e.(rune); ok {
  226. place += runewidth.RuneWidth(r)
  227. prevLen = len(string(r))
  228. }
  229. } else {
  230. break
  231. }
  232. }
  233. return sum
  234. }
  235. func (b *Buffer) drawRemaining() {
  236. var place int
  237. remainingText := b.StringN(b.Pos)
  238. if b.Pos > 0 {
  239. place = b.DisplayPos % b.LineWidth
  240. }
  241. fmt.Print(CursorHide)
  242. // render the rest of the current line
  243. currLineLength := b.countRemainingLineWidth(place)
  244. currLine := remainingText[:min(currLineLength, len(remainingText))]
  245. currLineSpace := runewidth.StringWidth(currLine)
  246. remLength := runewidth.StringWidth(remainingText)
  247. if len(currLine) > 0 {
  248. fmt.Printf(ClearToEOL + currLine)
  249. fmt.Print(cursorLeftN(currLineSpace))
  250. } else {
  251. fmt.Print(ClearToEOL)
  252. }
  253. if currLineSpace != b.LineWidth-place && currLineSpace != remLength {
  254. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth, true)
  255. } else if currLineSpace != b.LineWidth-place {
  256. b.LineHasSpace.Remove(b.DisplayPos / b.LineWidth)
  257. } else {
  258. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth, false)
  259. }
  260. if (b.DisplayPos+currLineSpace)%b.LineWidth == 0 && currLine == remainingText {
  261. fmt.Print(cursorRightN(currLineSpace))
  262. fmt.Printf("\n%s", b.Prompt.AltPrompt)
  263. fmt.Printf(CursorUp + CursorBOL + cursorRightN(b.Width-currLineSpace))
  264. }
  265. // render the other lines
  266. if remLength > currLineSpace {
  267. remaining := (remainingText[len(currLine):])
  268. var totalLines int
  269. var displayLength int
  270. var lineLength int = currLineSpace
  271. for _, c := range remaining {
  272. if displayLength == 0 || (displayLength+runewidth.RuneWidth(c))%b.LineWidth < displayLength%b.LineWidth {
  273. fmt.Printf("\n%s", b.Prompt.AltPrompt)
  274. totalLines += 1
  275. if displayLength != 0 {
  276. if lineLength == b.LineWidth {
  277. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth+totalLines-1, false)
  278. } else {
  279. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth+totalLines-1, true)
  280. }
  281. }
  282. lineLength = 0
  283. }
  284. displayLength += runewidth.RuneWidth(c)
  285. lineLength += runewidth.RuneWidth(c)
  286. fmt.Printf("%c", c)
  287. }
  288. fmt.Print(ClearToEOL)
  289. fmt.Print(cursorUpN(totalLines))
  290. fmt.Printf(CursorBOL + cursorRightN(b.Width-currLineSpace))
  291. hasSpace := b.GetLineSpacing(b.DisplayPos / b.LineWidth)
  292. if hasSpace && b.DisplayPos%b.LineWidth != b.LineWidth-1 {
  293. fmt.Print(CursorLeft)
  294. }
  295. }
  296. fmt.Print(CursorShow)
  297. }
  298. func (b *Buffer) Remove() {
  299. if b.Buf.Size() > 0 && b.Pos > 0 {
  300. if e, ok := b.Buf.Get(b.Pos - 1); ok {
  301. if r, ok := e.(rune); ok {
  302. rLength := runewidth.RuneWidth(r)
  303. hasSpace := b.GetLineSpacing(b.DisplayPos/b.LineWidth - 1)
  304. if b.DisplayPos%b.LineWidth == 0 {
  305. // if the user backspaces over the word boundary, do this magic to clear the line
  306. // and move to the end of the previous line
  307. fmt.Printf(CursorBOL + ClearToEOL)
  308. fmt.Printf(CursorUp + CursorBOL + cursorRightN(b.Width))
  309. if b.DisplaySize()%b.LineWidth < (b.DisplaySize()-rLength)%b.LineWidth {
  310. b.LineHasSpace.Remove(b.DisplayPos/b.LineWidth - 1)
  311. }
  312. if hasSpace {
  313. b.DisplayPos -= 1
  314. fmt.Print(CursorLeft)
  315. }
  316. if rLength == 2 {
  317. fmt.Print(CursorLeft + " " + cursorLeftN(2))
  318. } else {
  319. fmt.Print(" " + CursorLeft)
  320. }
  321. } else if (b.DisplayPos-rLength)%b.LineWidth == 0 && hasSpace {
  322. fmt.Printf(CursorBOL + ClearToEOL)
  323. fmt.Printf(CursorUp + CursorBOL + cursorRightN(b.Width))
  324. if b.Pos == b.Buf.Size() {
  325. b.LineHasSpace.Remove(b.DisplayPos/b.LineWidth - 1)
  326. }
  327. b.DisplayPos -= 1
  328. } else {
  329. fmt.Print(cursorLeftN(rLength))
  330. for range rLength {
  331. fmt.Print(" ")
  332. }
  333. fmt.Print(cursorLeftN(rLength))
  334. }
  335. var eraseExtraLine bool
  336. if (b.DisplaySize()-1)%b.LineWidth == 0 || (rLength == 2 && ((b.DisplaySize()-2)%b.LineWidth == 0)) || b.DisplaySize()%b.LineWidth == 0 {
  337. eraseExtraLine = true
  338. }
  339. b.Pos -= 1
  340. b.DisplayPos -= rLength
  341. b.Buf.Remove(b.Pos)
  342. if b.Pos < b.Buf.Size() {
  343. b.drawRemaining()
  344. // this erases a line which is left over when backspacing in the middle of a line and there
  345. // are trailing characters which go over the line width boundary
  346. if eraseExtraLine {
  347. remainingLines := (b.DisplaySize() - b.DisplayPos) / b.LineWidth
  348. fmt.Printf(cursorDownN(remainingLines+1) + CursorBOL + ClearToEOL)
  349. place := b.DisplayPos % b.LineWidth
  350. fmt.Printf(cursorUpN(remainingLines+1) + cursorRightN(place+len(b.Prompt.prompt())))
  351. }
  352. }
  353. }
  354. }
  355. }
  356. }
  357. func (b *Buffer) Delete() {
  358. if b.Buf.Size() > 0 && b.Pos < b.Buf.Size() {
  359. b.Buf.Remove(b.Pos)
  360. b.drawRemaining()
  361. if b.DisplaySize()%b.LineWidth == 0 {
  362. if b.DisplayPos != b.DisplaySize() {
  363. remainingLines := (b.DisplaySize() - b.DisplayPos) / b.LineWidth
  364. fmt.Printf(cursorDownN(remainingLines) + CursorBOL + ClearToEOL)
  365. place := b.DisplayPos % b.LineWidth
  366. fmt.Printf(cursorUpN(remainingLines) + cursorRightN(place+len(b.Prompt.prompt())))
  367. }
  368. }
  369. }
  370. }
  371. func (b *Buffer) DeleteBefore() {
  372. if b.Pos > 0 {
  373. for cnt := b.Pos - 1; cnt >= 0; cnt-- {
  374. b.Remove()
  375. }
  376. }
  377. }
  378. func (b *Buffer) DeleteRemaining() {
  379. if b.DisplaySize() > 0 && b.Pos < b.DisplaySize() {
  380. charsToDel := b.Buf.Size() - b.Pos
  381. for range charsToDel {
  382. b.Delete()
  383. }
  384. }
  385. }
  386. func (b *Buffer) DeleteWord() {
  387. if b.Buf.Size() > 0 && b.Pos > 0 {
  388. var foundNonspace bool
  389. for {
  390. v, _ := b.Buf.Get(b.Pos - 1)
  391. if v == ' ' {
  392. if !foundNonspace {
  393. b.Remove()
  394. } else {
  395. break
  396. }
  397. } else {
  398. foundNonspace = true
  399. b.Remove()
  400. }
  401. if b.Pos == 0 {
  402. break
  403. }
  404. }
  405. }
  406. }
  407. func (b *Buffer) ClearScreen() {
  408. fmt.Printf(ClearScreen + CursorReset + b.Prompt.prompt())
  409. if b.IsEmpty() {
  410. ph := b.Prompt.placeholder()
  411. fmt.Printf(ColorGrey + ph + cursorLeftN(len(ph)) + ColorDefault)
  412. } else {
  413. currPos := b.DisplayPos
  414. currIndex := b.Pos
  415. b.Pos = 0
  416. b.DisplayPos = 0
  417. b.drawRemaining()
  418. fmt.Printf(CursorReset + cursorRightN(len(b.Prompt.prompt())))
  419. if currPos > 0 {
  420. targetLine := currPos / b.LineWidth
  421. if targetLine > 0 {
  422. for range targetLine {
  423. fmt.Print(CursorDown)
  424. }
  425. }
  426. remainder := currPos % b.LineWidth
  427. if remainder > 0 {
  428. fmt.Print(cursorRightN(remainder))
  429. }
  430. if currPos%b.LineWidth == 0 {
  431. fmt.Printf(CursorBOL + b.Prompt.AltPrompt)
  432. }
  433. }
  434. b.Pos = currIndex
  435. b.DisplayPos = currPos
  436. }
  437. }
  438. func (b *Buffer) IsEmpty() bool {
  439. return b.Buf.Empty()
  440. }
  441. func (b *Buffer) Replace(r []rune) {
  442. b.DisplayPos = 0
  443. b.Pos = 0
  444. lineNums := b.DisplaySize() / b.LineWidth
  445. b.Buf.Clear()
  446. fmt.Printf(CursorBOL + ClearToEOL)
  447. for range lineNums {
  448. fmt.Print(CursorUp + CursorBOL + ClearToEOL)
  449. }
  450. fmt.Printf(CursorBOL + b.Prompt.prompt())
  451. for _, c := range r {
  452. b.Add(c)
  453. }
  454. }
  455. func (b *Buffer) String() string {
  456. return b.StringN(0)
  457. }
  458. func (b *Buffer) StringN(n int) string {
  459. return b.StringNM(n, 0)
  460. }
  461. func (b *Buffer) StringNM(n, m int) string {
  462. var s string
  463. if m == 0 {
  464. m = b.Buf.Size()
  465. }
  466. for cnt := n; cnt < m; cnt++ {
  467. c, _ := b.Buf.Get(cnt)
  468. s += string(c.(rune))
  469. }
  470. return s
  471. }
  472. func cursorLeftN(n int) string {
  473. return fmt.Sprintf(CursorLeftN, n)
  474. }
  475. func cursorRightN(n int) string {
  476. return fmt.Sprintf(CursorRightN, n)
  477. }
  478. func cursorUpN(n int) string {
  479. return fmt.Sprintf(CursorUpN, n)
  480. }
  481. func cursorDownN(n int) string {
  482. return fmt.Sprintf(CursorDownN, n)
  483. }