Files
Elias Naur 5fcfc40ab8 text,widget: remove dead code and fields
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-02-05 11:09:36 +00:00

129 lines
2.9 KiB
Go

// SPDX-License-Identifier: Unlicense OR MIT
package widget
import (
"io"
"unicode/utf8"
"golang.org/x/text/runes"
)
// editBuffer implements a gap buffer for text editing.
type editBuffer struct {
// The gap start and end in bytes.
gapstart, gapend int
text []byte
// changed tracks whether the buffer content
// has changed since the last call to Changed.
changed bool
}
var _ textSource = (*editBuffer)(nil)
const minSpace = 5
func (e *editBuffer) Changed() bool {
c := e.changed
e.changed = false
return c
}
func (e *editBuffer) deleteRunes(caret, count int) (bytes int, runes int) {
e.moveGap(caret, 0)
for ; count < 0 && e.gapstart > 0; count++ {
_, s := utf8.DecodeLastRune(e.text[:e.gapstart])
e.gapstart -= s
bytes += s
runes++
e.changed = e.changed || s > 0
}
for ; count > 0 && e.gapend < len(e.text); count-- {
_, s := utf8.DecodeRune(e.text[e.gapend:])
e.gapend += s
e.changed = e.changed || s > 0
}
return
}
// moveGap moves the gap to the caret position. After returning,
// the gap is guaranteed to be at least space bytes long.
func (e *editBuffer) moveGap(caret, space int) {
if e.gapLen() < space {
if space < minSpace {
space = minSpace
}
txt := make([]byte, int(e.Size())+space)
// Expand to capacity.
txt = txt[:cap(txt)]
gaplen := len(txt) - int(e.Size())
if caret > e.gapstart {
copy(txt, e.text[:e.gapstart])
copy(txt[caret+gaplen:], e.text[caret:])
copy(txt[e.gapstart:], e.text[e.gapend:caret+e.gapLen()])
} else {
copy(txt, e.text[:caret])
copy(txt[e.gapstart+gaplen:], e.text[e.gapend:])
copy(txt[caret+gaplen:], e.text[caret:e.gapstart])
}
e.text = txt
e.gapstart = caret
e.gapend = e.gapstart + gaplen
} else {
if caret > e.gapstart {
copy(e.text[e.gapstart:], e.text[e.gapend:caret+e.gapLen()])
} else {
copy(e.text[caret+e.gapLen():], e.text[caret:e.gapstart])
}
l := e.gapLen()
e.gapstart = caret
e.gapend = e.gapstart + l
}
}
func (e *editBuffer) Size() int64 {
return int64(len(e.text) - e.gapLen())
}
func (e *editBuffer) gapLen() int {
return e.gapend - e.gapstart
}
func (e *editBuffer) ReadAt(p []byte, offset int64) (int, error) {
if len(p) == 0 {
return 0, nil
}
if offset == e.Size() {
return 0, io.EOF
}
var total int
if offset < int64(e.gapstart) {
n := copy(p, e.text[offset:e.gapstart])
p = p[n:]
total += n
offset += int64(n)
}
if offset >= int64(e.gapstart) {
n := copy(p, e.text[offset+int64(e.gapLen()):])
total += n
}
return total, nil
}
func (e *editBuffer) ReplaceRunes(byteOffset, runeCount int64, s string) {
e.deleteRunes(int(byteOffset), int(runeCount))
e.prepend(int(byteOffset), s)
}
func (e *editBuffer) prepend(caret int, s string) {
if !utf8.ValidString(s) {
s = runes.ReplaceIllFormed().String(s)
}
e.moveGap(caret, len(s))
copy(e.text[caret:], s)
e.gapstart += len(s)
e.changed = e.changed || len(s) > 0
}