196 lines
4.6 KiB
Go
196 lines
4.6 KiB
Go
|
package atom
|
|||
|
|
|||
|
import (
|
|||
|
"errors"
|
|||
|
"fmt"
|
|||
|
"strings"
|
|||
|
"unicode"
|
|||
|
"unicode/utf8"
|
|||
|
|
|||
|
"gitea.zaclys.com/bvaudour/gob/option"
|
|||
|
)
|
|||
|
|
|||
|
var (
|
|||
|
ErrInvalidUnicode = errors.New("Not unicode")
|
|||
|
)
|
|||
|
|
|||
|
// NextUnescape enlève le caractère d’échapement en début de chaîne.
|
|||
|
func NextUnescape(str string) (result option.Option[string]) {
|
|||
|
if !strings.HasPrefix(str, "\033[") {
|
|||
|
result = option.Some(str)
|
|||
|
} else if i := strings.Index(str, "m"); i >= 0 {
|
|||
|
result = option.Some(str[i+1:])
|
|||
|
}
|
|||
|
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// IsPrintable retourne vrai si le caractère donné est imprimable.
|
|||
|
func IsPrintable(r Char) bool {
|
|||
|
return r == '\n' || unicode.IsPrint(r)
|
|||
|
}
|
|||
|
|
|||
|
// IsSpace retourne vrai si le caractère donné est un caractère blanc.
|
|||
|
func IsSpace(r Char) bool {
|
|||
|
return unicode.IsSpace(r)
|
|||
|
}
|
|||
|
|
|||
|
// CheckUnicode vérifie si la chaîne de caractère
|
|||
|
// ne contient que des caractères imprimables.
|
|||
|
func CheckUnicode(str string) (err option.Option[error]) {
|
|||
|
runes := []rune(str)
|
|||
|
|
|||
|
for i, r := range runes {
|
|||
|
if r == Esc {
|
|||
|
if i == len(runes)-1 || runes[i+1] != '[' {
|
|||
|
return option.Some(ErrInvalidUnicode)
|
|||
|
}
|
|||
|
} else if unicode.Is(unicode.C, r) {
|
|||
|
return option.Some(ErrInvalidUnicode)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// VisibleWith indique le nombre de colonnes nécessaires
|
|||
|
// pour afficher d’une chaîne passée à echo.
|
|||
|
func VisibleWidth(str string) int {
|
|||
|
w := 0
|
|||
|
|
|||
|
for len(str) > 0 {
|
|||
|
cleaned := NextUnescape(str)
|
|||
|
if s, ok := cleaned.Get(); ok {
|
|||
|
str = s
|
|||
|
} else {
|
|||
|
break
|
|||
|
}
|
|||
|
|
|||
|
r, i := utf8.DecodeRuneInString(str)
|
|||
|
if IsPrintable(r) {
|
|||
|
w++
|
|||
|
}
|
|||
|
|
|||
|
str = str[i:]
|
|||
|
}
|
|||
|
|
|||
|
return w
|
|||
|
}
|
|||
|
|
|||
|
// CursorOffset calcule la position relative du curseur
|
|||
|
// par rapport au début de la chaîne, ainsi que le
|
|||
|
// le n° de la dernière ligne (en commençant de 0).
|
|||
|
func CursorOffset(str string, p, w int) (px, py, l int) {
|
|||
|
if len(str) == 0 {
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
var (
|
|||
|
n, c int
|
|||
|
)
|
|||
|
|
|||
|
for len(str) > 0 {
|
|||
|
cleaned := NextUnescape(str)
|
|||
|
if s, ok := cleaned.Get(); ok {
|
|||
|
str = s
|
|||
|
} else {
|
|||
|
break
|
|||
|
}
|
|||
|
|
|||
|
r, i := utf8.DecodeRuneInString(str)
|
|||
|
str = str[i:]
|
|||
|
|
|||
|
if IsPrintable(r) {
|
|||
|
if p == n {
|
|||
|
px, py = c, l
|
|||
|
}
|
|||
|
n++
|
|||
|
|
|||
|
if r == '\n' {
|
|||
|
c, l = 0, l+1
|
|||
|
} else {
|
|||
|
c++
|
|||
|
if c == w {
|
|||
|
c, l = 0, l+1
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if p >= n {
|
|||
|
px, py = c, l
|
|||
|
}
|
|||
|
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// ClearBeginOfLine efface le début de la ligne, curseur compris
|
|||
|
func ClearBeginOfLine() { fmt.Print("\033[1K") }
|
|||
|
|
|||
|
// ClearEndOfLine efface la fin de la ligne, curseur compris.
|
|||
|
func ClearEndOfLine() { fmt.Print("\033[0K") }
|
|||
|
|
|||
|
// ClearLine efface toute la ligne.
|
|||
|
func ClearLine() { fmt.Print("\033[2K") }
|
|||
|
|
|||
|
// ClearBeginOfScreen efface l’écran jusqu’au curseur, curseur compris.
|
|||
|
func ClearBeginOfScreen() { fmt.Print("\033[1J") }
|
|||
|
|
|||
|
// ClearBeginOfScreen efface l’écran à partir du curseur, curseur compris.
|
|||
|
func ClearEndOfScreen() { fmt.Print("\033[0J") }
|
|||
|
|
|||
|
// ClearAllScreen efface tout l’écran.
|
|||
|
func ClearAllScreen() { fmt.Print("\033[2J") }
|
|||
|
|
|||
|
// ClearAllScreenAndHistory efface tout l’écran, ainsi que l’historique de l’affichage.
|
|||
|
func ClearAllScreenAndHistory() { fmt.Print("\033[3J") }
|
|||
|
|
|||
|
// MoveUp déplace le curseur de c lignes vers le haut.
|
|||
|
func MoveUp(c int) { fmt.Printf("\033[%dA", c) }
|
|||
|
|
|||
|
// MoveDown déplace le curseur de c lignes vers le bas.
|
|||
|
func MoveDown(c int) { fmt.Printf("\033[%dB", c) }
|
|||
|
|
|||
|
// MoveLeft déplace le curseur de c colonnes vers la gauche.
|
|||
|
func MoveLeft(c int) { fmt.Printf("\033[%dD", c) }
|
|||
|
|
|||
|
// MoveRight déplace le curseur de c colonnes vers la droite.
|
|||
|
func MoveRight(c int) { fmt.Printf("\033[%dC", c) }
|
|||
|
|
|||
|
// MoveLineUp se déplace de c lignes vers le haut et revient en début de ligne.
|
|||
|
func MoveLineUp(c int) { fmt.Printf("\033[%dF", c) }
|
|||
|
|
|||
|
// MoveLineDown se déplace de c lignes vers le bas et revient en début de ligne.
|
|||
|
func MoveLineDown(c int) { fmt.Printf("\033[%dE", c) }
|
|||
|
|
|||
|
// MoveToColumn se déplace à la colonne c. La colonne commence à 1.
|
|||
|
func MoveToColumn(c int) { fmt.Printf("\033[%dG", c) }
|
|||
|
|
|||
|
// MoveToScreen se déplace à la ligne l et la colonne c de l’écran.
|
|||
|
// l et c commencent à 1.
|
|||
|
func MoveToScreen(l, c int) { fmt.Print("\033[%d;%dH", l, c) }
|
|||
|
|
|||
|
// MoveTopLeft se déplace au tout début de l’écran
|
|||
|
func MoveTopLeft() { MoveToScreen(1, 1) }
|
|||
|
|
|||
|
// SaveCursorPosition enregistre la position du curseur
|
|||
|
// pour pouvoir y revenir plus tard.
|
|||
|
func SaveCursorPosition() { fmt.Print("\033[s") }
|
|||
|
|
|||
|
// RestoreCursorPosition revient à la position précédemment enregistrée.
|
|||
|
func RestoreCursorPosition() { fmt.Print("\033[u") }
|
|||
|
|
|||
|
// Beep émet un beep.
|
|||
|
func Beep() { fmt.Print("\a") }
|
|||
|
|
|||
|
// CarriageReturn revient en début de ligne.
|
|||
|
func CarriageReturn() { fmt.Print("\r") }
|
|||
|
|
|||
|
// NewLines rajoute c lignes.
|
|||
|
func NewLines(c int) {
|
|||
|
for c > 0 {
|
|||
|
fmt.Print("\n")
|
|||
|
c--
|
|||
|
}
|
|||
|
}
|