diff --git a/internal/logger/logger.go b/internal/logger/logger.go index cde5f597..91e23110 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -25,43 +25,62 @@ type ( ) func Default() PrintFunc { - return color.New(envColor("TASK_COLOR_RESET", color.Reset)).FprintfFunc() + return color.New(envColor("TASK_COLOR_RESET", color.Reset)...).FprintfFunc() } func Blue() PrintFunc { - return color.New(envColor("TASK_COLOR_BLUE", color.FgBlue)).FprintfFunc() + return color.New(envColor("TASK_COLOR_BLUE", color.FgBlue)...).FprintfFunc() } func Green() PrintFunc { - return color.New(envColor("TASK_COLOR_GREEN", color.FgGreen)).FprintfFunc() + return color.New(envColor("TASK_COLOR_GREEN", color.FgGreen)...).FprintfFunc() } func Cyan() PrintFunc { - return color.New(envColor("TASK_COLOR_CYAN", color.FgCyan)).FprintfFunc() + return color.New(envColor("TASK_COLOR_CYAN", color.FgCyan)...).FprintfFunc() } func Yellow() PrintFunc { - return color.New(envColor("TASK_COLOR_YELLOW", color.FgYellow)).FprintfFunc() + return color.New(envColor("TASK_COLOR_YELLOW", color.FgYellow)...).FprintfFunc() } func Magenta() PrintFunc { - return color.New(envColor("TASK_COLOR_MAGENTA", color.FgMagenta)).FprintfFunc() + return color.New(envColor("TASK_COLOR_MAGENTA", color.FgMagenta)...).FprintfFunc() } func Red() PrintFunc { - return color.New(envColor("TASK_COLOR_RED", color.FgRed)).FprintfFunc() + return color.New(envColor("TASK_COLOR_RED", color.FgRed)...).FprintfFunc() } -func envColor(env string, defaultColor color.Attribute) color.Attribute { +func envColor(env string, defaultColor color.Attribute) []color.Attribute { if os.Getenv("FORCE_COLOR") != "" { color.NoColor = false } - override, err := strconv.Atoi(os.Getenv(env)) - if err == nil { - return color.Attribute(override) + // Fetch the environment variable + override := os.Getenv(env) + + // First, try splitting the string by commas (RGB shortcut syntax) and if it + // matches, then prepend the 256-color foreground escape sequence. + // Otherwise, split by semicolons (ANSI color codes) and use them as is. + attributeStrs := strings.Split(override, ",") + if len(attributeStrs) == 3 { + attributeStrs = append([]string{"38", "2"}, attributeStrs...) + } else { + attributeStrs = strings.Split(override, ";") } - return defaultColor + + // Loop over the attributes and convert them to integers + attributes := make([]color.Attribute, len(attributeStrs)) + for i, attributeStr := range attributeStrs { + attribute, err := strconv.Atoi(attributeStr) + if err != nil { + return []color.Attribute{defaultColor} + } + attributes[i] = color.Attribute(attribute) + } + + return attributes } // Logger is just a wrapper that prints stuff to STDOUT or STDERR, diff --git a/website/docs/api_reference.mdx b/website/docs/api_reference.mdx index 53b9f3ac..29812226 100644 --- a/website/docs/api_reference.mdx +++ b/website/docs/api_reference.mdx @@ -154,6 +154,17 @@ Some environment variables can be overridden to adjust Task behavior. | `TASK_COLOR_RED` | `31` | Color used for red. | | `FORCE_COLOR` | | Force color output usage. | +All color variables are [ANSI color codes][ansi]. You can specify multiple codes +separated by a semicolon. For example: `31;1` will make the text bold and red. +Task also supports 8-bit color (256 colors). You can specify these colors by +using the sequence `38;2;R:G:B` for foreground colors and `48;2;R:G:B` for +background colors where `R`, `G` and `B` should be replaced with values between +0 and 255. + +For convenience, we allow foreground colors to be specified using shorthand, +comma-separated syntax: `R,G,B`. For example, `255,0,0` is equivalent to +`38;2;255:0:0`. + ## Taskfile Schema | Attribute | Type | Default | Description | @@ -373,3 +384,7 @@ tasks: | Attribute | Type | Default | Description | | --------- | ---------- | ------- | -------------------------------------------------------------------------------------------------- | | `vars` | `[]string` | | List of variable or environment variable names that must be set if this task is to execute and run | + +{/* prettier-ignore-start */} +[ansi]: https://en.wikipedia.org/wiki/ANSI_escape_code +{/* prettier-ignore-end */}