buffer.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package widget
  3. import (
  4. "io"
  5. "unicode/utf8"
  6. "golang.org/x/text/runes"
  7. )
  8. // editBuffer implements a gap buffer for text editing.
  9. type editBuffer struct {
  10. // The gap start and end in bytes.
  11. gapstart, gapend int
  12. text []byte
  13. // changed tracks whether the buffer content
  14. // has changed since the last call to Changed.
  15. changed bool
  16. }
  17. var _ textSource = (*editBuffer)(nil)
  18. const minSpace = 5
  19. func (e *editBuffer) Changed() bool {
  20. c := e.changed
  21. e.changed = false
  22. return c
  23. }
  24. func (e *editBuffer) deleteRunes(caret, count int) (bytes int, runes int) {
  25. e.moveGap(caret, 0)
  26. for ; count < 0 && e.gapstart > 0; count++ {
  27. _, s := utf8.DecodeLastRune(e.text[:e.gapstart])
  28. e.gapstart -= s
  29. bytes += s
  30. runes++
  31. e.changed = e.changed || s > 0
  32. }
  33. for ; count > 0 && e.gapend < len(e.text); count-- {
  34. _, s := utf8.DecodeRune(e.text[e.gapend:])
  35. e.gapend += s
  36. e.changed = e.changed || s > 0
  37. }
  38. return
  39. }
  40. // moveGap moves the gap to the caret position. After returning,
  41. // the gap is guaranteed to be at least space bytes long.
  42. func (e *editBuffer) moveGap(caret, space int) {
  43. if e.gapLen() < space {
  44. if space < minSpace {
  45. space = minSpace
  46. }
  47. txt := make([]byte, int(e.Size())+space)
  48. // Expand to capacity.
  49. txt = txt[:cap(txt)]
  50. gaplen := len(txt) - int(e.Size())
  51. if caret > e.gapstart {
  52. copy(txt, e.text[:e.gapstart])
  53. copy(txt[caret+gaplen:], e.text[caret:])
  54. copy(txt[e.gapstart:], e.text[e.gapend:caret+e.gapLen()])
  55. } else {
  56. copy(txt, e.text[:caret])
  57. copy(txt[e.gapstart+gaplen:], e.text[e.gapend:])
  58. copy(txt[caret+gaplen:], e.text[caret:e.gapstart])
  59. }
  60. e.text = txt
  61. e.gapstart = caret
  62. e.gapend = e.gapstart + gaplen
  63. } else {
  64. if caret > e.gapstart {
  65. copy(e.text[e.gapstart:], e.text[e.gapend:caret+e.gapLen()])
  66. } else {
  67. copy(e.text[caret+e.gapLen():], e.text[caret:e.gapstart])
  68. }
  69. l := e.gapLen()
  70. e.gapstart = caret
  71. e.gapend = e.gapstart + l
  72. }
  73. }
  74. func (e *editBuffer) Size() int64 {
  75. return int64(len(e.text) - e.gapLen())
  76. }
  77. func (e *editBuffer) gapLen() int {
  78. return e.gapend - e.gapstart
  79. }
  80. func (e *editBuffer) ReadAt(p []byte, offset int64) (int, error) {
  81. if len(p) == 0 {
  82. return 0, nil
  83. }
  84. if offset == e.Size() {
  85. return 0, io.EOF
  86. }
  87. var total int
  88. if offset < int64(e.gapstart) {
  89. n := copy(p, e.text[offset:e.gapstart])
  90. p = p[n:]
  91. total += n
  92. offset += int64(n)
  93. }
  94. if offset >= int64(e.gapstart) {
  95. n := copy(p, e.text[offset+int64(e.gapLen()):])
  96. total += n
  97. }
  98. return total, nil
  99. }
  100. func (e *editBuffer) ReplaceRunes(byteOffset, runeCount int64, s string) {
  101. e.deleteRunes(int(byteOffset), int(runeCount))
  102. e.prepend(int(byteOffset), s)
  103. }
  104. func (e *editBuffer) prepend(caret int, s string) {
  105. if !utf8.ValidString(s) {
  106. s = runes.ReplaceIllFormed().String(s)
  107. }
  108. e.moveGap(caret, len(s))
  109. copy(e.text[caret:], s)
  110. e.gapstart += len(s)
  111. e.changed = e.changed || len(s) > 0
  112. }