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) && r != '\n' { 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-- } }