Color/color.go
2025-06-06 13:51:43 -05:00

102 lines
2.0 KiB
Go

package color
import (
"os"
"strings"
"sync"
)
// ANSI color codes
const (
resetCode = "\033[0m"
redCode = "\033[31m"
greenCode = "\033[32m"
yellowCode = "\033[33m"
blueCode = "\033[34m"
purpleCode = "\033[35m"
cyanCode = "\033[36m"
whiteCode = "\033[37m"
grayCode = "\033[90m"
)
var (
useColors bool
colorMu sync.RWMutex
)
// Color function; makes a call to makeColorFunc with the associated color code
var (
Reset = makeColorFunc(resetCode)
Red = makeColorFunc(redCode)
Green = makeColorFunc(greenCode)
Yellow = makeColorFunc(yellowCode)
Blue = makeColorFunc(blueCode)
Purple = makeColorFunc(purpleCode)
Cyan = makeColorFunc(cyanCode)
White = makeColorFunc(whiteCode)
Gray = makeColorFunc(grayCode)
)
func init() {
useColors = DetectShellColors()
}
func makeColorFunc(code string) func(string) string {
return func(text string) string {
colorMu.RLock()
enabled := useColors
colorMu.RUnlock()
if enabled {
return code + text + resetCode
}
return text
}
}
// DetectShellColors checks if the current shell supports colors
func DetectShellColors() bool {
// Check NO_COLOR environment variable (standard)
if os.Getenv("NO_COLOR") != "" {
return false
}
// Check FORCE_COLOR environment variable
if os.Getenv("FORCE_COLOR") != "" {
return true
}
// Check if stdout is a terminal
if !isTerminal(os.Stdout) {
return false
}
// Check TERM environment variable
term := os.Getenv("TERM")
if term == "" || term == "dumb" {
return false
}
// Common color-supporting terminals
return strings.Contains(term, "color") ||
strings.Contains(term, "xterm") ||
strings.Contains(term, "screen") ||
strings.Contains(term, "tmux") ||
term == "linux" ||
isWindowsTerminal()
}
// SetColors enables or disables colors globally
func SetColors(enabled bool) {
colorMu.Lock()
useColors = enabled
colorMu.Unlock()
}
// ColorsEnabled returns current global color setting
func ColorsEnabled() bool {
colorMu.RLock()
defer colorMu.RUnlock()
return useColors
}