316 lines
6.0 KiB
Go
316 lines
6.0 KiB
Go
package atom
|
||
|
||
import (
|
||
"gitea.zaclys.com/bvaudour/gob/option"
|
||
)
|
||
|
||
// Buffer stocke l’ensemble 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 n’est 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 n’a 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 l’incrément donné.
|
||
// Si l’incré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 d’un caractère vers la gauche.
|
||
func (b *Buffer) Left() (ok bool) {
|
||
return b.Move(-1)
|
||
}
|
||
|
||
// Right déplace le curseur d’un 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 n’est
|
||
// 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
|
||
}
|