From 3f86c31c9fa986a3ec808ede1b9366be318a759f Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Sat, 31 May 2025 09:11:21 -0500 Subject: [PATCH] rewrite color utils --- http/server.go | 2 +- main.go | 42 +++++++------- runner/env.go | 6 +- runner/fs.go | 6 +- runner/runner.go | 2 +- runner/sqlite.go | 2 +- utils/color/color.go | 127 ++++++++++++++++++++++++++----------------- watchers/api.go | 4 +- 8 files changed, 108 insertions(+), 83 deletions(-) diff --git a/http/server.go b/http/server.go index 96ba000..1cd006b 100644 --- a/http/server.go +++ b/http/server.go @@ -131,7 +131,7 @@ func New(luaRouter *router.LuaRouter, staticDir string, } func (s *Server) ListenAndServe(addr string) error { - logger.Info("Catch the swell at %s", color.Apply("http://localhost"+addr, color.Cyan)) + logger.Info("Catch the swell at %s", color.Cyan("http://localhost"+addr)) return s.fasthttpServer.ListenAndServe(addr) } diff --git a/main.go b/main.go index 7a356f0..eda8e5c 100644 --- a/main.go +++ b/main.go @@ -40,22 +40,13 @@ func main() { flag.Parse() scriptMode := *scriptPath != "" - banner() - mode := "" - if scriptMode { - mode = "[Script Mode]" - } else { - mode = "[Server Mode]" - } - fmt.Printf("%s %s\n\n", color.Apply(mode, color.Gray), color.Apply("v"+metadata.Version, color.Blue)) - - // Initialize logger - logger.InitGlobalLogger(true, false) + color.SetColors(color.DetectShellColors()) + banner(scriptMode) // Load config cfg, err := config.Load(*configPath) if err != nil { - logger.Warning("Config load failed: %v, using defaults", color.Apply(err.Error(), color.Red)) + logger.Warning("Config load failed: %v, using defaults", color.Red(err.Error())) cfg = config.New() } @@ -158,9 +149,9 @@ func initServerMode(cfg *config.Config, debug bool) (*Moonshark, error) { staticDir := "" if dirExists(cfg.Dirs.Static) { staticDir = cfg.Dirs.Static - logger.Info("Static files enabled: %s", color.Apply(staticDir, color.Yellow)) + logger.Info("Static files enabled: %s", color.Yellow(staticDir)) } else { - logger.Warning("Static directory not found: %s", color.Apply(cfg.Dirs.Static, color.Yellow)) + logger.Warning("Static directory not found: %s", color.Yellow(cfg.Dirs.Static)) } moonshark.HTTPServer = http.New( @@ -216,7 +207,7 @@ func (s *Moonshark) Start() error { return errors.New("cannot start server in script mode") } - logger.Info("Surf's up on port %s!", color.Apply(strconv.Itoa(s.Config.Server.Port), color.Cyan)) + logger.Info("Surf's up on port %s!", color.Cyan(strconv.Itoa(s.Config.Server.Port))) go func() { if err := s.HTTPServer.ListenAndServe(fmt.Sprintf(":%d", s.Config.Server.Port)); err != nil { @@ -289,20 +280,20 @@ func initLuaRouter(s *Moonshark) error { return fmt.Errorf("lua router init failed: %v", err) } } - logger.Info("LuaRouter is g2g! %s", color.Set(s.Config.Dirs.Routes, color.Yellow)) + logger.Info("LuaRouter is g2g! %s", color.Yellow(s.Config.Dirs.Routes)) return nil } func initRunner(s *Moonshark) error { if !dirExists(s.Config.Dirs.Override) { - logger.Warning("Override directory not found... %s", color.Apply(s.Config.Dirs.Override, color.Yellow)) + logger.Warning("Override directory not found... %s", color.Yellow(s.Config.Dirs.Override)) s.Config.Dirs.Override = "" } for _, dir := range s.Config.Dirs.Libs { if !dirExists(dir) { - logger.Warning("Lib directory not found... %s", color.Apply(dir, color.Yellow)) + logger.Warning("Lib directory not found... %s", color.Yellow(dir)) } } @@ -339,7 +330,7 @@ func initRunner(s *Moonshark) error { return fmt.Errorf("lua runner init failed: %v", err) } - logger.Info("LuaRunner is g2g with %s states!", color.Apply(strconv.Itoa(poolSize), color.Yellow)) + logger.Info("LuaRunner is g2g with %s states!", color.Yellow(strconv.Itoa(poolSize))) return nil } @@ -374,20 +365,25 @@ func setupWatchers(s *Moonshark) error { } else { plural = "directories" } - logger.Info("Watching %s module %s.", color.Apply(strconv.Itoa(len(moduleWatchers)), color.Yellow), plural) + logger.Info("Watching %s module %s.", color.Yellow(strconv.Itoa(len(moduleWatchers))), plural) } return nil } -func banner() { +func banner(scriptMode bool) { + if scriptMode { + fmt.Println(color.Blue(fmt.Sprintf("Moonshark %s << Script Mode >>", metadata.Version))) + return + } + banner := ` _____ _________.__ __ / \ ____ ____ ____ / _____/| |__ _____ _______| | __ / \ / \ / _ \ / _ \ / \ \_____ \ | | \\__ \\_ __ \ |/ / / Y ( <_> | <_> ) | \/ \| Y \/ __ \| | \/ < -\____|__ /\____/ \____/|___| /_______ /|___| (____ /__| |__|_ \ +\____|__ /\____/ \____/|___| /_______ /|___| (____ /__| |__|_ \ %s \/ \/ \/ \/ \/ \/ ` - fmt.Println(color.Apply(banner, color.Blue)) + fmt.Println(color.Blue(fmt.Sprintf(banner, metadata.Version))) } diff --git a/runner/env.go b/runner/env.go index 03c1432..b2ded8f 100644 --- a/runner/env.go +++ b/runner/env.go @@ -51,10 +51,10 @@ func InitEnv(dataDir string) error { count := len(globalEnvManager.vars) if count > 0 { logger.Info("Environment loaded: %s vars from %s", - color.Apply(fmt.Sprintf("%d", count), color.Yellow), - color.Apply(envPath, color.Yellow)) + color.Yellow(fmt.Sprintf("%d", count)), + color.Yellow(envPath)) } else { - logger.Info("Environment initialized: %s", color.Apply(envPath, color.Yellow)) + logger.Info("Environment initialized: %s", color.Yellow(envPath)) } return nil diff --git a/runner/fs.go b/runner/fs.go index 43dc4d4..c191bc8 100644 --- a/runner/fs.go +++ b/runner/fs.go @@ -52,7 +52,7 @@ func InitFS(basePath string) error { // Initialize file cache with 2000 entries (reasonable for most use cases) fileCache = lru.NewLRUCache(2000) - logger.Info("Filesystem is g2g! %s", color.Apply(fsBasePath, color.Yellow)) + logger.Info("Filesystem is g2g! %s", color.Yellow(fsBasePath)) return nil } @@ -62,8 +62,8 @@ func CleanupFS() { fileCache.Clear() logger.Info( "File cache cleared - %s hits, %s misses", - color.Apply(fmt.Sprintf("%d", stats.hits), color.Yellow), - color.Apply(fmt.Sprintf("%d", stats.misses), color.Red), + color.Yellow(fmt.Sprintf("%d", stats.hits)), + color.Red(fmt.Sprintf("%d", stats.misses)), ) } } diff --git a/runner/runner.go b/runner/runner.go index 02759ee..712645c 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -134,7 +134,7 @@ func NewRunner(options ...RunnerOption) (*Runner, error) { // initializeStates creates and initializes all states in the pool func (r *Runner) initializeStates() error { - logger.Info("[LuaRunner] Creating %s states...", color.Apply(strconv.Itoa(r.poolSize), color.Yellow)) + logger.Info("[LuaRunner] Creating %s states...", color.Yellow(strconv.Itoa(r.poolSize))) for i := range r.poolSize { state, err := r.createState(i) diff --git a/runner/sqlite.go b/runner/sqlite.go index c6cb82b..3d31e17 100644 --- a/runner/sqlite.go +++ b/runner/sqlite.go @@ -28,7 +28,7 @@ var ( // InitSQLite initializes the SQLite subsystem func InitSQLite(dir string) { dataDir = dir - logger.Info("SQLite is g2g! %s", color.Apply(dir, color.Yellow)) + logger.Info("SQLite is g2g! %s", color.Yellow(dir)) } // SetSQLitePoolSize sets the pool size to match the runner pool size diff --git a/utils/color/color.go b/utils/color/color.go index 1fe1b41..beacbe4 100644 --- a/utils/color/color.go +++ b/utils/color/color.go @@ -1,20 +1,83 @@ package color +import ( + "os" + "strings" + "syscall" + "unsafe" +) + +const ioctlReadTermios = 0x5401 + // ANSI color codes const ( - Reset = "\033[0m" - Red = "\033[31m" - Green = "\033[32m" - Yellow = "\033[33m" - Blue = "\033[34m" - Purple = "\033[35m" - Cyan = "\033[36m" - White = "\033[37m" - Gray = "\033[90m" + 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 = true +// 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 makeColorFunc(code string) func(string) string { + return func(text string) string { + if useColors { + 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" +} + // SetColors enables or disables colors globally func SetColors(enabled bool) { useColors = enabled @@ -25,44 +88,10 @@ func ColorsEnabled() bool { return useColors } -// Apply adds color to text using global color setting -func Apply(text, color string) string { - if useColors { - return color + text + Reset - } - return text -} - -// ApplyIf adds color to text if useColors is true (for backward compatibility) -func ApplyIf(text, color string, enabled bool) string { - if enabled { - return color + text + Reset - } - return text -} - -// Set adds color to text (always applies color, ignores global setting) -func Set(text, color string) string { - return color + text + Reset -} - -// Strip removes ANSI color codes from a string -func Strip(s string) string { - result := "" - inEscape := false - - for _, c := range s { - if inEscape { - if c == 'm' { - inEscape = false - } - continue - } - if c == '\033' { - inEscape = true - continue - } - result += string(c) - } - return result +// isTerminal checks if the file is a terminal +func isTerminal(f *os.File) bool { + fd := f.Fd() + var termios syscall.Termios + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) + return err == 0 } diff --git a/watchers/api.go b/watchers/api.go index d745a7f..e795bd9 100644 --- a/watchers/api.go +++ b/watchers/api.go @@ -48,7 +48,7 @@ func WatchLuaRouter(router *router.LuaRouter, runner *runner.Runner, routesDir s return nil, fmt.Errorf("failed to watch directory: %w", err) } - logger.Info("Started watching Lua routes! %s", color.Apply(routesDir, color.Yellow)) + logger.Info("Started watching Lua routes! %s", color.Yellow(routesDir)) return watcher, nil } @@ -77,7 +77,7 @@ func WatchLuaModules(luaRunner *runner.Runner, libDirs []string) ([]*DirectoryWa } watchers = append(watchers, watcher) - logger.Info("Started watching Lua modules! %s", color.Apply(dir, color.Yellow)) + logger.Info("Started watching Lua modules! %s", color.Yellow(dir)) } return watchers, nil