Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions spec/std/colorize_spec.cr
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "spec"
require "colorize"
require "../support/env"

private def colorize(obj, *args)
obj.colorize(*args).toggle(true)
Expand All @@ -17,7 +18,70 @@ private class ColorizeToS
end
end

private class ColorizeTTY < IO
def tty? : Bool
true
end

def read(slice : Bytes)
0
end

def write(slice : Bytes) : Nil
end
end

describe "colorize" do
it ".default_enabled?" do
io = IO::Memory.new
tty = ColorizeTTY.new

with_env("TERM": nil, "NO_COLOR": nil) do
Colorize.default_enabled?(io).should be_false
Colorize.default_enabled?(tty).should be_true
Colorize.default_enabled?(io, io).should be_false
Colorize.default_enabled?(io, tty).should be_false
Colorize.default_enabled?(tty, io).should be_false
Colorize.default_enabled?(tty, tty).should be_true
end

with_env("TERM": nil, "NO_COLOR": "") do
Colorize.default_enabled?(io).should be_false
Colorize.default_enabled?(tty).should be_true
Colorize.default_enabled?(io, io).should be_false
Colorize.default_enabled?(io, tty).should be_false
Colorize.default_enabled?(tty, io).should be_false
Colorize.default_enabled?(tty, tty).should be_true
end

with_env("TERM": nil, "NO_COLOR": "1") do
Colorize.default_enabled?(io).should be_false
Colorize.default_enabled?(tty).should be_false
Colorize.default_enabled?(io, io).should be_false
Colorize.default_enabled?(io, tty).should be_false
Colorize.default_enabled?(tty, io).should be_false
Colorize.default_enabled?(tty, tty).should be_false
end

with_env("TERM": "xterm", "NO_COLOR": nil) do
Colorize.default_enabled?(io).should be_false
Colorize.default_enabled?(tty).should be_true
Colorize.default_enabled?(io, io).should be_false
Colorize.default_enabled?(io, tty).should be_false
Colorize.default_enabled?(tty, io).should be_false
Colorize.default_enabled?(tty, tty).should be_true
end

with_env("TERM": "dumb", "NO_COLOR": nil) do
Colorize.default_enabled?(io).should be_false
Colorize.default_enabled?(tty).should be_false
Colorize.default_enabled?(io, io).should be_false
Colorize.default_enabled?(io, tty).should be_false
Colorize.default_enabled?(tty, io).should be_false
Colorize.default_enabled?(tty, tty).should be_false
end
end

it "colorizes without change" do
colorize("hello").to_s.should eq("hello")
end
Expand Down
35 changes: 21 additions & 14 deletions src/colorize.cr
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@
#
# See `Colorize::Mode` for available text decorations.
module Colorize
# Objects will only be colored if this is `true`.
# Objects will only be colored if this is `true`, unless overridden by
# `Colorize::Object#toggle`.
#
# ```
# require "colorize"
Expand All @@ -129,27 +130,33 @@ module Colorize
# "hello".colorize.red.to_s # => "hello"
# ```
#
# NOTE: This is by default enabled according to `.on_tty_only!`.
class_property? enabled : Bool do
STDOUT.tty? && STDERR.tty? && ENV["TERM"]? != "dumb" && !ENV["NO_COLOR"]?.try(&.empty?.!)
end
# NOTE: This is by default enabled if `.default_enabled?` is true for `STDOUT`
# and `STDERR`.
class_property? enabled : Bool { default_enabled?(STDOUT, STDERR) }

# Makes `Colorize.enabled` `true` if and only if both of `STDOUT.tty?`
# and `STDERR.tty?` are `true` and the tty is not considered a dumb terminal.
# This is determined by the environment variable called `TERM`.
# If `TERM=dumb`, color won't be enabled.
# If `NO_COLOR` contains any non-empty value, color won't be enabled
# conforming to https://no-color.org
#
# Returns the new value of `Colorize.enabled?`.
# Resets `Colorize.enabled?` to its initial default value, i.e. whether
# `.default_enabled?` is true for `STDOUT` and `STDERR`. Returns this new
# value.
#
# This can be used to revert `Colorize.enabled?` to its default value after
# This can be used to revert `Colorize.enabled?` to its initial state after
# colorization is explicitly enabled or disabled.
def self.on_tty_only! : Bool
@@enabled = nil
enabled?
end

# Returns whether colorization should be enabled by default on the given
# standard output and error streams.
#
# This is true if both streams are terminals (i.e. `IO#tty?` returns true),
# the `TERM` environment variable is not equal to `dumb`, and the
# [`NO_COLOR` environment variable](https://no-color.org) is not set to a
# non-empty string.
def self.default_enabled?(stdout : IO, stderr : IO = stdout) : Bool
stdout.tty? && (stderr == stdout || stderr.tty?) &&
ENV["TERM"]? != "dumb" && !ENV["NO_COLOR"]?.try(&.empty?.!)
end

# Resets the color and text decoration of the *io*.
#
# ```
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/crystal/command.cr
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class Crystal::Command
@compiler : Compiler?

def initialize(@options : Array(String))
@color = ENV["TERM"]? != "dumb" && !ENV["NO_COLOR"]?.try(&.empty?.!)
@color = Colorize.default_enabled?(STDOUT, STDERR)
@error_trace = false
@progress_tracker = ProgressTracker.new
end
Expand Down Expand Up @@ -762,7 +762,7 @@ class Crystal::Command

private def error(msg, exit_code = 1)
# This is for the case where the main command is wrong
@color = false if ARGV.includes?("--no-color") || ENV["TERM"]? == "dumb" || ENV["NO_COLOR"]?.try(&.empty?.!)
@color = false if ARGV.includes?("--no-color") || !Colorize.default_enabled?(STDOUT, STDERR)
Crystal.error msg, @color, exit_code: exit_code
end

Expand Down
Loading