gob/shell/console/atom/buffer.go

316 lines
6.0 KiB
Go
Raw Normal View History

2023-10-07 19:13:39 +00:00
package atom
import (
"gitea.zaclys.com/bvaudour/gob/option"
)
// Buffer stocke lensemble des caractère saisis et la position du curseur.
type Buffer struct {
data []rune
cursor int
}
// Len retourne le nombre de caractères du buffer.
func (b *Buffer) Len() int {
return len(b.data)
}
// Insert ajoute les caractères donnés à la position du curseur.
func (b *Buffer) Insert(data ...rune) {
l1, l2, c := b.Len(), len(data), b.cursor
newData := make([]rune, l1+l2)
if c > 0 {
copy(newData[:c], b.data[:c])
}
copy(newData[c:c+l2], data)
if c < l1 {
copy(newData[c+l2:], b.data[c:])
}
b.data = newData
b.cursor += l2
}
// Replace remplace les caractères du buffer à partir de la position du curseur
// par les caractères donnés.
// Si le buffer nest pas assez grand, le buffer est agrandi.
func (b *Buffer) Replace(data ...rune) {
l1, l2, c := b.Len(), len(data), b.cursor
l := max(l1, c+l2)
newData := make([]rune, l)
if c > 0 {
copy(newData[:c], b.data[:c])
}
copy(newData[c:c+l2], data)
if c+l2 < l1 {
copy(newData[c+l2:], b.data[c+l2:])
}
b.data = newData
b.cursor += l2
}
// Transpose transpose le caractère sous le curseur avec le caractère précédent.
// Si la transposition na pu se faire, il retourne faux.
func (b *Buffer) Transpose() (ok bool) {
l := b.Len()
if ok = b.cursor > 0 && l >= 2; ok {
c := b.cursor
if c == l {
c--
}
b.data[c-1], b.data[c] = b.data[c], b.data[c-1]
}
return
}
// Move déplace le curseur avec lincrément donné.
// Si lincrément est négatif, le déplacement se fait vers la gauche.
// Sinon, il se fait vers la droite.
// Si la position du curseur sort du buffer, retourne faux.
func (b *Buffer) Move(inc int) (ok bool) {
c, l := b.cursor+inc, b.Len()
if ok = c >= 0 && c <= l; ok {
b.cursor = c
}
return
}
// Left déplace le curseur dun caractère vers la gauche.
func (b *Buffer) Left() (ok bool) {
return b.Move(-1)
}
// Right déplace le curseur dun caractère vers la droite.
func (b *Buffer) Right() (ok bool) {
return b.Move(1)
}
// Begin déplace le curseur au début du buffer.
func (b *Buffer) Begin() (ok bool) {
if ok = b.cursor > 0; ok {
b.cursor = 0
}
return
}
// End déplace le curseur à la fin du buffer.
func (b *Buffer) End() (ok bool) {
l := b.Len()
if ok = b.cursor < l; ok {
b.cursor = l
}
return
}
// PrevWord déplace le curseur au début du mot,
// ou au début du précédent mot si le curseur nest
// pas positionné sur un mot.
func (b *Buffer) PrevWord() (ok bool) {
l := b.Len()
if l == 0 || b.cursor == 0 {
return
}
i := b.cursor - 1
for i == l || IsSpace(b.data[i]) {
i--
}
for i > 0 && !IsSpace(b.data[i-1]) {
i--
}
return b.Move(i - b.cursor)
}
// NextWord déplace le curseur au début du prochain mot.
func (b *Buffer) NextWord() (ok bool) {
i, l := b.cursor, b.Len()
if i == l {
return
}
for i < l && !IsSpace(b.data[i]) {
i++
}
for i < l && IsSpace(b.data[i]) {
i++
}
return b.Move(i - b.cursor)
}
// Backn supprime n caractères à gauche du curseur.
// Si la suppression a bien été effectuée, il retourne
// la chaîne supprimée.
func (b *Buffer) Backn(n int) (shifted option.Option[string]) {
l, c := b.Len(), b.cursor
c2 := c - n
if n <= 0 || c2 < 0 {
return
}
shifted = option.Some(string(b.data[c2:c]))
newData := make([]rune, l-n)
copy(newData[:c2], b.data[:c2])
if c < l {
copy(newData[c2:], b.data[c:])
}
b.data = newData
b.cursor = c2
return
}
// Deln supprime n caractères à partir du curseur.
// Si la suppression a bien été effectuée, il retourne
// la chaîne supprimée.
func (b *Buffer) Deln(n int) (shifted option.Option[string]) {
l, c := b.Len(), b.cursor
c2 := c + n
if n <= 0 || c2 > l {
return
}
shifted = option.Some(string(b.data[c:c2]))
newData := make([]rune, l-n)
copy(newData[:c], b.data[:c])
if c2 < l {
copy(newData[c:], b.data[c2:])
}
b.data = newData
return
}
// Back supprime le caractère avant le curseur.
func (b *Buffer) Back() option.Option[string] {
return b.Backn(1)
}
// Del supprime le caractère sous le curseur.
func (b *Buffer) Del() option.Option[string] {
return b.Deln(1)
}
// BackWord supprime le mot avant le caractère.
func (b *Buffer) BackWord() (shifted option.Option[string]) {
l := b.Len()
if l == 0 || b.cursor == 0 {
return
}
i := b.cursor - 1
for i == l || IsSpace(b.data[i]) {
i--
}
for i > 0 && !IsSpace(b.data[i-1]) {
i--
}
return b.Backn(b.cursor - i)
}
// DelWord supprime le mot après le caractère.
func (b *Buffer) DelWord() (shifted option.Option[string]) {
i, l := b.cursor, b.Len()
if i == l {
return
}
for i < l || IsSpace(b.data[i]) {
i++
}
for i < l && !IsSpace(b.data[i]) {
i++
}
return b.Deln(i - b.cursor)
}
// RemoveBegin supprime les caractères avant le curseur.
func (b *Buffer) RemoveBegin() (shifted option.Option[string]) {
if b.cursor > 0 {
shifted = option.Some(string(b.data[:b.cursor]))
b.data = b.data[b.cursor:]
b.cursor = 0
}
return
}
// RemoveEnd supprime les caractères après le curseur, curseur compris.
func (b *Buffer) RemoveEnd() (shifted option.Option[string]) {
if b.cursor < b.Len() {
shifted = option.Some(string(b.data[b.cursor:]))
b.data = b.data[:b.cursor]
}
return
}
// Clear efface tout le buffer.
func (b *Buffer) Clear() (shifted option.Option[string]) {
if b.Len() > 0 {
shifted = option.Some(string(b.data))
b.data, b.cursor = b.data[:0], 0
}
return
}
// String retourne le contenu du buffer sous forme de chaîne de caractères.
func (b *Buffer) String() string {
return string(b.data)
}
// Cursor retourne la position du curseur.
func (b *Buffer) Cursor() int {
return b.cursor
}
// Clone effectue une copie du buffer.
func (b *Buffer) Clone() *Buffer {
l, c := len(b.data), cap(b.data)
cb := Buffer{
data: make([]rune, l, c),
cursor: b.cursor,
}
copy(cb.data, b.data)
return &cb
}
func (b *Buffer) SetCursor(cursor int) (ok bool) {
if ok = cursor > 0 && cursor <= b.Len() && cursor != b.cursor; ok {
b.cursor = cursor
}
return
}