buffer.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  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.Print(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.Print(CursorDown + CursorBOL + CursorRightN(len(b.Prompt.prompt())))
  101. } else if (b.DisplayPos-rLength)%b.LineWidth == b.LineWidth-1 && hasSpace {
  102. fmt.Print(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.Print(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.Print(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.Print(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.Print(ClearToEOL + currLine + CursorLeftN(currLineSpace))
  249. } else {
  250. fmt.Print(ClearToEOL)
  251. }
  252. if currLineSpace != b.LineWidth-place && currLineSpace != remLength {
  253. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth, true)
  254. } else if currLineSpace != b.LineWidth-place {
  255. b.LineHasSpace.Remove(b.DisplayPos / b.LineWidth)
  256. } else {
  257. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth, false)
  258. }
  259. if (b.DisplayPos+currLineSpace)%b.LineWidth == 0 && currLine == remainingText {
  260. fmt.Print(CursorRightN(currLineSpace))
  261. fmt.Printf("\n%s", b.Prompt.AltPrompt)
  262. fmt.Print(CursorUp + CursorBOL + CursorRightN(b.Width-currLineSpace))
  263. }
  264. // render the other lines
  265. if remLength > currLineSpace {
  266. remaining := (remainingText[len(currLine):])
  267. var totalLines int
  268. var displayLength int
  269. var lineLength int = currLineSpace
  270. for _, c := range remaining {
  271. if displayLength == 0 || (displayLength+runewidth.RuneWidth(c))%b.LineWidth < displayLength%b.LineWidth {
  272. fmt.Printf("\n%s", b.Prompt.AltPrompt)
  273. totalLines += 1
  274. if displayLength != 0 {
  275. if lineLength == b.LineWidth {
  276. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth+totalLines-1, false)
  277. } else {
  278. b.LineHasSpace.Set(b.DisplayPos/b.LineWidth+totalLines-1, true)
  279. }
  280. }
  281. lineLength = 0
  282. }
  283. displayLength += runewidth.RuneWidth(c)
  284. lineLength += runewidth.RuneWidth(c)
  285. fmt.Printf("%c", c)
  286. }
  287. fmt.Print(ClearToEOL + CursorUpN(totalLines) + CursorBOL + CursorRightN(b.Width-currLineSpace))
  288. hasSpace := b.GetLineSpacing(b.DisplayPos / b.LineWidth)
  289. if hasSpace && b.DisplayPos%b.LineWidth != b.LineWidth-1 {
  290. fmt.Print(CursorLeft)
  291. }
  292. }
  293. fmt.Print(CursorShow)
  294. }
  295. func (b *Buffer) Remove() {
  296. if b.Buf.Size() > 0 && b.Pos > 0 {
  297. if e, ok := b.Buf.Get(b.Pos - 1); ok {
  298. if r, ok := e.(rune); ok {
  299. rLength := runewidth.RuneWidth(r)
  300. hasSpace := b.GetLineSpacing(b.DisplayPos/b.LineWidth - 1)
  301. if b.DisplayPos%b.LineWidth == 0 {
  302. // if the user backspaces over the word boundary, do this magic to clear the line
  303. // and move to the end of the previous line
  304. fmt.Print(CursorBOL + ClearToEOL + CursorUp + CursorBOL + CursorRightN(b.Width))
  305. if b.DisplaySize()%b.LineWidth < (b.DisplaySize()-rLength)%b.LineWidth {
  306. b.LineHasSpace.Remove(b.DisplayPos/b.LineWidth - 1)
  307. }
  308. if hasSpace {
  309. b.DisplayPos -= 1
  310. fmt.Print(CursorLeft)
  311. }
  312. if rLength == 2 {
  313. fmt.Print(CursorLeft + " " + CursorLeftN(2))
  314. } else {
  315. fmt.Print(" " + CursorLeft)
  316. }
  317. } else if (b.DisplayPos-rLength)%b.LineWidth == 0 && hasSpace {
  318. fmt.Print(CursorBOL + ClearToEOL + CursorUp + CursorBOL + CursorRightN(b.Width))
  319. if b.Pos == b.Buf.Size() {
  320. b.LineHasSpace.Remove(b.DisplayPos/b.LineWidth - 1)
  321. }
  322. b.DisplayPos -= 1
  323. } else {
  324. fmt.Print(CursorLeftN(rLength))
  325. for range rLength {
  326. fmt.Print(" ")
  327. }
  328. fmt.Print(CursorLeftN(rLength))
  329. }
  330. var eraseExtraLine bool
  331. if (b.DisplaySize()-1)%b.LineWidth == 0 || (rLength == 2 && ((b.DisplaySize()-2)%b.LineWidth == 0)) || b.DisplaySize()%b.LineWidth == 0 {
  332. eraseExtraLine = true
  333. }
  334. b.Pos -= 1
  335. b.DisplayPos -= rLength
  336. b.Buf.Remove(b.Pos)
  337. if b.Pos < b.Buf.Size() {
  338. b.drawRemaining()
  339. // this erases a line which is left over when backspacing in the middle of a line and there
  340. // are trailing characters which go over the line width boundary
  341. if eraseExtraLine {
  342. remainingLines := (b.DisplaySize() - b.DisplayPos) / b.LineWidth
  343. fmt.Print(CursorDownN(remainingLines+1) + CursorBOL + ClearToEOL)
  344. place := b.DisplayPos % b.LineWidth
  345. fmt.Print(CursorUpN(remainingLines+1) + CursorRightN(place+len(b.Prompt.prompt())))
  346. }
  347. }
  348. }
  349. }
  350. }
  351. }
  352. func (b *Buffer) Delete() {
  353. if b.Buf.Size() > 0 && b.Pos < b.Buf.Size() {
  354. b.Buf.Remove(b.Pos)
  355. b.drawRemaining()
  356. if b.DisplaySize()%b.LineWidth == 0 {
  357. if b.DisplayPos != b.DisplaySize() {
  358. remainingLines := (b.DisplaySize() - b.DisplayPos) / b.LineWidth
  359. fmt.Print(CursorDownN(remainingLines) + CursorBOL + ClearToEOL)
  360. place := b.DisplayPos % b.LineWidth
  361. fmt.Print(CursorUpN(remainingLines) + CursorRightN(place+len(b.Prompt.prompt())))
  362. }
  363. }
  364. }
  365. }
  366. func (b *Buffer) DeleteBefore() {
  367. if b.Pos > 0 {
  368. for cnt := b.Pos - 1; cnt >= 0; cnt-- {
  369. b.Remove()
  370. }
  371. }
  372. }
  373. func (b *Buffer) DeleteRemaining() {
  374. if b.DisplaySize() > 0 && b.Pos < b.DisplaySize() {
  375. charsToDel := b.Buf.Size() - b.Pos
  376. for range charsToDel {
  377. b.Delete()
  378. }
  379. }
  380. }
  381. func (b *Buffer) DeleteWord() {
  382. if b.Buf.Size() > 0 && b.Pos > 0 {
  383. var foundNonspace bool
  384. for {
  385. v, _ := b.Buf.Get(b.Pos - 1)
  386. if v == ' ' {
  387. if !foundNonspace {
  388. b.Remove()
  389. } else {
  390. break
  391. }
  392. } else {
  393. foundNonspace = true
  394. b.Remove()
  395. }
  396. if b.Pos == 0 {
  397. break
  398. }
  399. }
  400. }
  401. }
  402. func (b *Buffer) ClearScreen() {
  403. fmt.Print(ClearScreen + CursorReset + b.Prompt.prompt())
  404. if b.IsEmpty() {
  405. ph := b.Prompt.placeholder()
  406. fmt.Print(ColorGrey + ph + CursorLeftN(len(ph)) + ColorDefault)
  407. } else {
  408. currPos := b.DisplayPos
  409. currIndex := b.Pos
  410. b.Pos = 0
  411. b.DisplayPos = 0
  412. b.drawRemaining()
  413. fmt.Print(CursorReset + CursorRightN(len(b.Prompt.prompt())))
  414. if currPos > 0 {
  415. targetLine := currPos / b.LineWidth
  416. if targetLine > 0 {
  417. for range targetLine {
  418. fmt.Print(CursorDown)
  419. }
  420. }
  421. remainder := currPos % b.LineWidth
  422. if remainder > 0 {
  423. fmt.Print(CursorRightN(remainder))
  424. }
  425. if currPos%b.LineWidth == 0 {
  426. fmt.Print(CursorBOL + b.Prompt.AltPrompt)
  427. }
  428. }
  429. b.Pos = currIndex
  430. b.DisplayPos = currPos
  431. }
  432. }
  433. func (b *Buffer) IsEmpty() bool {
  434. return b.Buf.Empty()
  435. }
  436. func (b *Buffer) Replace(r []rune) {
  437. b.DisplayPos = 0
  438. b.Pos = 0
  439. lineNums := b.DisplaySize() / b.LineWidth
  440. b.Buf.Clear()
  441. fmt.Print(CursorBOL + ClearToEOL)
  442. for range lineNums {
  443. fmt.Print(CursorUp + CursorBOL + ClearToEOL)
  444. }
  445. fmt.Print(CursorBOL + b.Prompt.prompt())
  446. for _, c := range r {
  447. b.Add(c)
  448. }
  449. }
  450. func (b *Buffer) String() string {
  451. return b.StringN(0)
  452. }
  453. func (b *Buffer) StringN(n int) string {
  454. return b.StringNM(n, 0)
  455. }
  456. func (b *Buffer) StringNM(n, m int) string {
  457. var s string
  458. if m == 0 {
  459. m = b.Buf.Size()
  460. }
  461. for cnt := n; cnt < m; cnt++ {
  462. c, _ := b.Buf.Get(cnt)
  463. s += string(c.(rune))
  464. }
  465. return s
  466. }