package color import ( "fmt" "strings" ) const Reset = "\033[0m" var ansiFg = map[string]int{ "black": 30, "red": 31, "green": 32, "yellow": 33, "blue": 34, "magenta": 35, "cyan": 36, "white": 37, } var ansiBg = map[string]int{ "black": 40, "red": 41, "green": 42, "yellow": 43, "blue": 44, "magenta": 45, "cyan": 46, "white": 47, } var ansiBrightFg = map[string]int{ "bright_black": 90, "bright_red": 91, "bright_green": 92, "bright_yellow": 93, "bright_blue": 94, "bright_magenta": 95, "bright_cyan": 96, "bright_white": 97, } var ansiBrightBg = map[string]int{ "bright_black": 100, "bright_red": 101, "bright_green": 102, "bright_yellow": 103, "bright_blue": 104, "bright_magenta": 105, "bright_cyan": 106, "bright_white": 107, } var xtermFg = map[string]int{ "black": 0, "red": 1, "green": 2, "yellow": 3, "blue": 4, "magenta": 5, "cyan": 6, "white": 7, "bright_black": 8, "bright_red": 9, "bright_green": 10, "bright_yellow": 11, "bright_blue": 12, "bright_magenta": 13, "bright_cyan": 14, "bright_white": 15, } var ansiStyles = map[string]string{ "bold": "\033[1m", "dim": "\033[2m", "italic": "\033[3m", "underline": "\033[4m", } func Fg(mode, name string) string { if mode == "none" || mode == "" { return "" } if mode == "xterm256" { if c, ok := xtermFg[name]; ok { return fmt.Sprintf("\033[38;5;%dm", c) } return "" } if c, ok := ansiFg[name]; ok { return fmt.Sprintf("\033[%dm", c) } if c, ok := ansiBrightFg[name]; ok { return fmt.Sprintf("\033[%dm", c) } return "" } func Bg(mode, name string) string { if mode == "none" || mode == "" { return "" } if mode == "xterm256" { if c, ok := xtermFg[name]; ok { return fmt.Sprintf("\033[48;5;%dm", c) } return "" } if c, ok := ansiBg[name]; ok { return fmt.Sprintf("\033[%dm", c) } if c, ok := ansiBrightBg[name]; ok { return fmt.Sprintf("\033[%dm", c) } return "" } func Style(name string) string { if s, ok := ansiStyles[name]; ok { return s } return "" } func Wrap(mode, name, text string) string { if mode == "none" || mode == "" { return text } return Fg(mode, name) + text + Reset } var knownColors []string func init() { seen := make(map[string]bool) for name := range ansiFg { if !seen[name] { knownColors = append(knownColors, name) seen[name] = true } } for name := range ansiBrightFg { if !seen[name] { knownColors = append(knownColors, name) seen[name] = true } } for name := range ansiStyles { if !seen[name] { knownColors = append(knownColors, name) seen[name] = true } } } func KnownColors() []string { return knownColors } func Tag(mode, text string) string { if mode == "none" || mode == "" { return cleanTags(text) } result := text for _, name := range knownColors { if _, isStyle := ansiStyles[name]; isStyle { result = replaceTag(result, name, Style(name), Reset) } else { result = replaceTag(result, name, Fg(mode, name), Reset) } } return result } func replaceTag(s, name, open, close string) string { tag := "<" + name + ">" end := "" for { start := strings.Index(s, tag) if start < 0 { break } closeIdx := strings.Index(s[start+len(tag):], end) if closeIdx < 0 { break } closeIdx += start + len(tag) inner := s[start+len(tag) : closeIdx] replacement := open + inner + close s = s[:start] + replacement + s[closeIdx+len(end):] } return s } func cleanTags(text string) string { result := text for _, name := range knownColors { result = strings.ReplaceAll(result, "<"+name+">", "") result = strings.ReplaceAll(result, "", "") } return result }