lib/simplecov/color.rb
# frozen_string_literal: true module SimpleCov # ANSI colorization for stderr diagnostics. Thresholds mirror the # HTML formatter (>= 90 green, >= 75 yellow, otherwise red) so a # team's mental model of "what's the cutoff" is the same whether # they're reading the terminal output or the HTML report. # # Color precedence, highest first: # # - `SimpleCov.color = true` / `false` (programmatic override, wins # over everything; default is `:auto` which falls through) # - `NO_COLOR` env var (any non-empty value) → off (see no-color.org) # - `FORCE_COLOR` env var (any non-empty value) → on # - `stream.tty?` fallback # # `NO_COLOR` wins over `FORCE_COLOR` if both env vars are set. module Color GREEN_THRESHOLD = 90 YELLOW_THRESHOLD = 75 ANSI = { red: "\e[31m", yellow: "\e[33m", green: "\e[32m", reset: "\e[0m" }.freeze module_function # `stream` is the IO that the colorized text is destined for. The # formatter writes to stderr, so that's the default. CLI subcommands # that print to stdout should pass `$stdout` so a redirected pipe # doesn't get ANSI sequences. See the module-level comment for # precedence. def enabled?(stream = $stderr) # `SimpleCov.color` only exists once the full library is loaded. # The standalone CLI (`exe/simplecov`) loads `simplecov/color` # without `simplecov` itself to stay lightweight, so treat a # missing config the same as its `:auto` default: fall through to # the env vars and tty check below. config = SimpleCov.color if SimpleCov.respond_to?(:color) return config if [true, false].include?(config) return false if env_set?("NO_COLOR") return true if env_set?("FORCE_COLOR") stream.tty? end def for_percent(percent) return :green if percent >= GREEN_THRESHOLD return :yellow if percent >= YELLOW_THRESHOLD :red end # Wrap `text` in the ANSI sequence for `color` (a key of ANSI). # Returns the bare text if color is disabled. The `enabled:` # keyword lets callers (e.g., CLI subcommands honoring `--no-color`) # override the auto-detection without touching env vars. def colorize(text, color, enabled: enabled?) return text unless enabled "#{ANSI.fetch(color)}#{text}#{ANSI.fetch(:reset)}" end # Render `percent` as a fixed "NN.NN%" string colored by which # threshold band it falls into. Callers that want a different # rendering of the number can pass the pre-rendered `text`. def colorize_percent(percent, text = nil, enabled: enabled?) colorize(text || format("%.2f%%", percent), for_percent(percent), enabled: enabled) end def env_set?(name) value = ENV.fetch(name, nil) value && !value.empty? end end end