Added prompt colors

This commit is contained in:
Alexandre 2025-02-03 14:43:28 +01:00
parent 73b0429195
commit 84b59ca9be

89
main.go
View File

@ -23,6 +23,19 @@ type Config struct {
var config Config var config Config
// Map des couleurs ANSI
var colors = map[string]string{
"black": "\033[30m",
"red": "\033[31m",
"green": "\033[32m",
"yellow": "\033[33m",
"blue": "\033[34m",
"purple": "\033[35m",
"cyan": "\033[36m",
"white": "\033[37m",
"reset": "\033[0m",
}
func main() { func main() {
// Chargement de la configuration // Chargement de la configuration
loadConfig() loadConfig()
@ -33,10 +46,10 @@ func main() {
// Configuration du shell interactif // Configuration du shell interactif
rl, err := readline.NewEx(&readline.Config{ rl, err := readline.NewEx(&readline.Config{
Prompt: getPrompt(), // Utilise une fonction pour générer le prompt dynamiquement Prompt: getPrompt(),
HistoryFile: historyFile, // Permet de sauvegarder et charger l'historique HistoryFile: historyFile,
HistoryLimit: config.HistorySize, HistoryLimit: config.HistorySize,
AutoComplete: nil, // Peut être amélioré avec l'autocomplétion AutoComplete: nil,
}) })
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, "Erreur readline:", err) fmt.Fprintln(os.Stderr, "Erreur readline:", err)
@ -45,22 +58,18 @@ func main() {
defer rl.Close() defer rl.Close()
for { for {
// Mettre à jour le prompt avec le répertoire courant
rl.SetPrompt(getPrompt()) rl.SetPrompt(getPrompt())
// Lecture de l'entrée utilisateur avec édition et historique
input, err := rl.Readline() input, err := rl.Readline()
if err != nil { // EOF ou Ctrl+D if err != nil {
break break
} }
// Suppression des espaces inutiles
input = strings.TrimSpace(input) input = strings.TrimSpace(input)
if input == "" { if input == "" {
continue continue
} }
// Exécute la commande
if err := execInput(input); err != nil { if err := execInput(input); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
} }
@ -75,7 +84,6 @@ func loadConfig() {
viper.AddConfigPath(configPath) viper.AddConfigPath(configPath)
viper.SetConfigName("gosh_config") viper.SetConfigName("gosh_config")
viper.SetConfigType("toml") viper.SetConfigType("toml")
// viper.SetConfigFile(configPath)
// Valeurs par défaut // Valeurs par défaut
viper.SetDefault("prompt", "[{dir}] > ") viper.SetDefault("prompt", "[{dir}] > ")
@ -85,7 +93,6 @@ func loadConfig() {
// Lire le fichier de configuration // Lire le fichier de configuration
if err := viper.ReadInConfig(); err != nil { if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok { if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// Si le fichier n'existe pas, le créer avec les valeurs par défaut
fmt.Println("Création du fichier de configuration avec les valeurs par défaut...") fmt.Println("Création du fichier de configuration avec les valeurs par défaut...")
if err := viper.WriteConfigAs(configPath + "gosh_config"); err != nil { if err := viper.WriteConfigAs(configPath + "gosh_config"); err != nil {
fmt.Fprintln(os.Stderr, "Erreur lors de la création du fichier de configuration:", err) fmt.Fprintln(os.Stderr, "Erreur lors de la création du fichier de configuration:", err)
@ -93,19 +100,16 @@ func loadConfig() {
fmt.Println("Fichier de configuration créé avec succès:", configPath) fmt.Println("Fichier de configuration créé avec succès:", configPath)
} }
} else { } else {
// Autre erreur de lecture du fichier
fmt.Fprintln(os.Stderr, "Erreur de configuration:", err) fmt.Fprintln(os.Stderr, "Erreur de configuration:", err)
fmt.Println("Utilisation des valeurs par défaut.") fmt.Println("Utilisation des valeurs par défaut.")
} }
} }
// Charger la configuration dans la structure Config
if err := viper.Unmarshal(&config); err != nil { if err := viper.Unmarshal(&config); err != nil {
fmt.Fprintln(os.Stderr, "Erreur de chargement de la configuration:", err) fmt.Fprintln(os.Stderr, "Erreur de chargement de la configuration:", err)
fmt.Println("Utilisation des valeurs par défaut.") fmt.Println("Utilisation des valeurs par défaut.")
} }
// Validation des valeurs
if config.HistorySize <= 0 { if config.HistorySize <= 0 {
fmt.Fprintln(os.Stderr, "Taille de l'historique invalide. Utilisation de la valeur par défaut (1000).") fmt.Fprintln(os.Stderr, "Taille de l'historique invalide. Utilisation de la valeur par défaut (1000).")
config.HistorySize = 1000 config.HistorySize = 1000
@ -119,34 +123,38 @@ func getPrompt() string {
wd = "?" wd = "?"
} }
// Remplacer le chemin du home par "~"
homeDir, _ := os.UserHomeDir() homeDir, _ := os.UserHomeDir()
if homeDir != "" && strings.HasPrefix(wd, homeDir) { if homeDir != "" && strings.HasPrefix(wd, homeDir) {
wd = "~" + strings.TrimPrefix(wd, homeDir) wd = "~" + strings.TrimPrefix(wd, homeDir)
} }
// Utiliser le prompt défini dans la configuration
prompt := strings.Replace(config.Prompt, "{dir}", wd, -1) prompt := strings.Replace(config.Prompt, "{dir}", wd, -1)
// Ajouter de la couleur si configuré // Appliquer la couleur si elle existe dans la map
if config.Color == "blue" { if colorCode, exists := colors[config.Color]; exists {
blue := "\033[34m" prompt = colorCode + prompt + colors["reset"]
reset := "\033[0m"
prompt = blue + prompt + reset
} }
if config.Color == "green" {
green := "\033[32m"
reset := "\033[0m"
prompt = green + prompt + reset
}
return prompt return prompt
} }
// Fonction pour déterminer si une commande est interactive
func isInteractiveCommand(cmd string) bool {
interactiveCommands := map[string]bool{
"vim": true,
"nano": true,
"ssh": true,
"top": true,
"htop": true,
"less": true,
"more": true,
}
return interactiveCommands[cmd]
}
func execInput(input string) error { func execInput(input string) error {
input = strings.TrimSuffix(input, "\n") input = strings.TrimSuffix(input, "\n")
// Diviser la commande en arguments en respectant les guillemets
args, err := shlex.Split(input) args, err := shlex.Split(input)
if err != nil { if err != nil {
return fmt.Errorf("Erreur lors de la division des arguments: %v", err) return fmt.Errorf("Erreur lors de la division des arguments: %v", err)
@ -156,7 +164,6 @@ func execInput(input string) error {
return nil return nil
} }
// Gérer les commandes intégrées
switch args[0] { switch args[0] {
case "cd": case "cd":
if len(args) < 2 || args[1] == "" { if len(args) < 2 || args[1] == "" {
@ -179,21 +186,25 @@ func execInput(input string) error {
return setConfig(args[1], strings.Join(args[2:], " ")) return setConfig(args[1], strings.Join(args[2:], " "))
} }
// Exécuter la commande système dans un PTY
cmd := exec.Command(args[0], args[1:]...) cmd := exec.Command(args[0], args[1:]...)
if isInteractiveCommand(args[0]) {
ptmx, err := pty.Start(cmd) ptmx, err := pty.Start(cmd)
if err != nil { if err != nil {
return fmt.Errorf("Erreur lors du démarrage du PTY: %v", err) return fmt.Errorf("Erreur lors du démarrage du PTY: %v", err)
} }
defer ptmx.Close() defer ptmx.Close()
// Rediriger les entrées/sorties entre le terminal parent et le PTY
go func() { go func() {
io.Copy(ptmx, os.Stdin) // Rediriger stdin vers le PTY io.Copy(ptmx, os.Stdin)
}() }()
io.Copy(os.Stdout, ptmx) // Rediriger stdout du PTY vers le terminal io.Copy(os.Stdout, ptmx)
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
}
// Attendre la fin de la commande
if err := cmd.Wait(); err != nil { if err := cmd.Wait(); err != nil {
return fmt.Errorf("Erreur lors de l'exécution de la commande: %v", err) return fmt.Errorf("Erreur lors de l'exécution de la commande: %v", err)
} }
@ -207,6 +218,9 @@ func setConfig(key, value string) error {
case "prompt": case "prompt":
viper.Set("prompt", value) viper.Set("prompt", value)
case "color": case "color":
if _, exists := colors[value]; !exists {
return fmt.Errorf("Couleur inconnue: %s. Couleurs disponibles: %v", value, getAvailableColors())
}
viper.Set("color", value) viper.Set("color", value)
case "history_size": case "history_size":
intValue, err := strconv.Atoi(value) intValue, err := strconv.Atoi(value)
@ -218,12 +232,10 @@ func setConfig(key, value string) error {
return fmt.Errorf("Clé de configuration inconnue: %s", key) return fmt.Errorf("Clé de configuration inconnue: %s", key)
} }
// Sauvegarder la configuration dans le fichier
if err := viper.WriteConfig(); err != nil { if err := viper.WriteConfig(); err != nil {
return fmt.Errorf("Erreur lors de la sauvegarde de la configuration: %v", err) return fmt.Errorf("Erreur lors de la sauvegarde de la configuration: %v", err)
} }
// Recharger la configuration
if err := viper.Unmarshal(&config); err != nil { if err := viper.Unmarshal(&config); err != nil {
return fmt.Errorf("Erreur lors du rechargement de la configuration: %v", err) return fmt.Errorf("Erreur lors du rechargement de la configuration: %v", err)
} }
@ -231,3 +243,12 @@ func setConfig(key, value string) error {
fmt.Printf("Configuration mise à jour: %s = %s\n", key, value) fmt.Printf("Configuration mise à jour: %s = %s\n", key, value)
return nil return nil
} }
// Retourne la liste des couleurs disponibles
func getAvailableColors() []string {
keys := make([]string, 0, len(colors))
for k := range colors {
keys = append(keys, k)
}
return keys
}