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 }