From b49ed16de611796974910096e8e2c5e6f10bb790 Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 11:41:57 +0200 Subject: [PATCH 01/28] Improve Colorize --- src/colorize.cr | 154 ++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 76 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index 9685c663f115..17672b54b3ce 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -1,4 +1,4 @@ -# With Colorize you can change the fore- and background colors and text decorations when rendering text +# With `Colorize` you can change the fore- and background colors and text decorations when rendering text # on terminals supporting ANSI escape codes. It adds the `colorize` method to `Object` and thus all classes # as its main interface, which calls `to_s` and surrounds it with the necessary escape codes # when it comes to obtaining a string representation of the object. @@ -33,6 +33,10 @@ # ``` # require "colorize" # +# "foo".colorize(0, 255, 255) # => "foo" in aqua +# +# # This is the same as: +# # "foo".colorize(Colorize::ColorRGB.new(0, 255, 255)) # => "foo" in aqua # ``` # @@ -68,8 +72,8 @@ # "foo".colorize(:red).toggle(false).toggle(true) # => "foo" in red # ``` # -# The color `:default` will just leave the object as it is (but it's an `Colorize::Object(String)` then). -# That's handy in for example conditions: +# The color `:default` leaves the object's representation as it is but the object is a `Colorize::Object` then +# which is handy in conditions such as: # ``` # require "colorize" # @@ -117,18 +121,16 @@ module Colorize # require "colorize" # # Colorize.enabled = true - # "hello".colorize.red.to_s # => "\e[31mhello\e[0m" + # "hello".colorize.red.to_s # => "hello" in red # # Colorize.enabled = false # "hello".colorize.red.to_s # => "hello" # ``` - class_property? enabled : Bool = true - - # Makes `Colorize.enabled` `true` if and only if both of `STDOUT.tty?` and `STDERR.tty?` are `true`. - def self.on_tty_only! - self.enabled = STDOUT.tty? && STDERR.tty? - end + # + # NOTE: This is by default disabled on non-TTY devices as they most likely do not support ANSI escape codes. + class_property? enabled : Bool = STDOUT.tty? && STDERR.tty? + # Resets the color of the object to the default. def self.reset(io = STDOUT) io << "\e[0m" if enabled? end @@ -138,18 +140,25 @@ def with_color "".colorize end -def with_color(color : Symbol) +def with_color(color : Colorize::Color) "".colorize(color) end module Colorize::ObjectExtensions + # Turns `self` into a `Colorize::Object`. def colorize Colorize::Object.new(self) end - def colorize(fore) + # Turns `self` into a `Colorize::Object` and colors it. + def colorize(fore : Color) Colorize::Object.new(self).fore(fore) end + + # Turns `self` into a `Colorize::Object` and colors it with an RGB color. + def colorize(r : UInt8, g : UInt8, b : UInt8) + Colorize::Object.new(self).fore(Colorize::ColorRGB.new(r, g, b)) + end end class Object @@ -159,6 +168,7 @@ end module Colorize alias Color = ColorANSI | Color256 | ColorRGB + # One color of a fixed set of colors. enum ColorANSI Default = 39 Black = 30 @@ -187,6 +197,7 @@ module Colorize end end + # An 8-bit color. record Color256, value : UInt8 do def fore(io : IO) : Nil @@ -200,6 +211,7 @@ module Colorize end end + # An RGB color. record ColorRGB, red : UInt8, green : UInt8, @@ -214,10 +226,33 @@ module Colorize {red, green, blue}.join(';', io, &.to_s io) end end + + # A text decoration. + # + # Note that not all decorations are supported in all terminals. + # When a decoration is not supported, the text won't have any decoration. + @[Flags] + enum Mode + # Makes the text bold. + Bold = 1 + # Makes the text color bright. + Bright = 1 + # Dims the text color, the opposite of `Bold` and `Bright`. + Dim + # Underlines the text. + Underline + # Makes the text blink slowly. + Blink + # Swaps the foreground and background colors. + Reverse + # Makes the text invisible. + Hidden + end end +# A colorize object colors and decorations can be applied to. struct Colorize::Object(T) - private MODE_DEFAULT = '0' + private MODE_NONE = '0' private MODE_BOLD = '1' private MODE_BRIGHT = '1' private MODE_DIM = '2' @@ -226,24 +261,13 @@ struct Colorize::Object(T) private MODE_REVERSE = '7' private MODE_HIDDEN = '8' - private MODE_BOLD_FLAG = 1 - private MODE_BRIGHT_FLAG = 1 - private MODE_DIM_FLAG = 2 - private MODE_UNDERLINE_FLAG = 4 - private MODE_BLINK_FLAG = 8 - private MODE_REVERSE_FLAG = 16 - private MODE_HIDDEN_FLAG = 32 - private COLORS = %w(default black red green yellow blue magenta cyan light_gray dark_gray light_red light_green light_yellow light_blue light_magenta light_cyan white) private MODES = %w(bold bright dim underline blink reverse hidden) - @fore : Color - @back : Color - def initialize(@object : T) @fore = ColorANSI::Default @back = ColorANSI::Default - @mode = 0 + @mode = Mode::None @enabled = Colorize.enabled? end @@ -261,74 +285,52 @@ struct Colorize::Object(T) {% for name in MODES %} def {{name.id}} - @mode |= MODE_{{name.upcase.id}}_FLAG + @mode |= Mode::{{name.capitalize.id}} self end {% end %} - def fore(color : Symbol) - {% for name in COLORS %} - if color == :{{name.id}} - @fore = ColorANSI::{{name.camelcase.id}} - return self - end - {% end %} - - raise ArgumentError.new "Unknown color: #{color}" - end - - def fore(@fore : Color) + # Sets the foreground color of the object to *color*. + def fore(color : Color) + @fore = color self end - def back(color : Symbol) - {% for name in COLORS %} - if color == :{{name.id}} - @back = ColorANSI::{{name.camelcase.id}} - return self - end - {% end %} - - raise ArgumentError.new "Unknown color: #{color}" - end - - def back(@back : Color) + # Sets the background color of the object to *color*. + def back(color : Color) + @back = color self end - def mode(mode : Symbol) - {% for name in MODES %} - if mode == :{{name.id}} - @mode |= MODE_{{name.upcase.id}}_FLAG - return self - end - {% end %} - - raise ArgumentError.new "Unknown mode: #{mode}" - end - - def on(color : Symbol) - back color + # Sets the text decoration of the object to *mode*. + def mode(mode : Mode) + @mode |= mode + self end + # Enables or disables colors and text decoration on this object. def toggle(flag) @enabled = !!flag self end + # Appends this object colored and with text decoration to *io*. def to_s(io : IO) : Nil surround(io) do io << @object end end + # Inspects this object and makes the ANSI escape codes visible. def inspect(io : IO) : Nil - surround(io) do - @object.inspect(io) - end + internal_io = IO::Memory.new + (surround(internal_io) do + @object.to_s internal_io + end) + io << internal_io.to_s.inspect end - def surround(io = STDOUT) + private def surround(io = STDOUT) return yield io unless @enabled Object.surround(io, to_named_tuple) do |io| @@ -347,7 +349,7 @@ struct Colorize::Object(T) @@last_color = { fore: ColorANSI::Default.as(Color), back: ColorANSI::Default.as(Color), - mode: 0, + mode: Mode::None, } protected def self.surround(io, color) @@ -367,7 +369,7 @@ struct Colorize::Object(T) last_color_is_default = @@last_color[:fore] == ColorANSI::Default && @@last_color[:back] == ColorANSI::Default && - @@last_color[:mode] == 0 + @@last_color[:mode] == Mode::None fore = color[:fore] back = color[:back] @@ -375,9 +377,9 @@ struct Colorize::Object(T) fore_is_default = fore == ColorANSI::Default back_is_default = back == ColorANSI::Default - mode_is_default = mode == 0 + no_mode = mode == Mode::None - if fore_is_default && back_is_default && mode_is_default && last_color_is_default || @@last_color == color + if @@last_color == color || fore_is_default && back_is_default && no_mode && last_color_is_default false else io << "\e[" @@ -385,7 +387,7 @@ struct Colorize::Object(T) printed = false unless last_color_is_default - io << MODE_DEFAULT + io << MODE_NONE printed = true end @@ -401,12 +403,12 @@ struct Colorize::Object(T) printed = true end - unless mode_is_default + unless no_mode # Can't reuse MODES constant because it has bold/bright duplicated - {% for name in %w(bold dim underline blink reverse hidden) %} - if mode.bits_set? MODE_{{name.upcase.id}}_FLAG + {% for mode in %w(bold dim underline blink reverse hidden) %} + if mode.includes? Mode::{{mode.capitalize.id}} io << ';' if printed - io << MODE_{{name.upcase.id}} + io << MODE_{{mode.upcase.id}} printed = true end {% end %} From 2e6cdf4696e90b2498b23885e3416e3b8844c106 Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 11:46:07 +0200 Subject: [PATCH 02/28] Improve mode documentation --- src/colorize.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/colorize.cr b/src/colorize.cr index 17672b54b3ce..4ecf1ab38eda 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -302,7 +302,7 @@ struct Colorize::Object(T) self end - # Sets the text decoration of the object to *mode*. + # Adds *mode* to the text's decorations. def mode(mode : Mode) @mode |= mode self From 69d64e8ec9c1bc821a6c053b131225d58bb6f917 Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 12:19:07 +0200 Subject: [PATCH 03/28] Make Colorize::Object#surround public again --- src/colorize.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/colorize.cr b/src/colorize.cr index 4ecf1ab38eda..14d6d33b7378 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -330,7 +330,7 @@ struct Colorize::Object(T) io << internal_io.to_s.inspect end - private def surround(io = STDOUT) + def surround(io = STDOUT) return yield io unless @enabled Object.surround(io, to_named_tuple) do |io| From 702c6f2ff825513a474a8f815459c357e8288df0 Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 12:25:22 +0200 Subject: [PATCH 04/28] Change Spec::COLORS to use the enum members --- src/spec/dsl.cr | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/spec/dsl.cr b/src/spec/dsl.cr index f1c468313a36..35562c334363 100644 --- a/src/spec/dsl.cr +++ b/src/spec/dsl.cr @@ -3,11 +3,11 @@ require "option_parser" module Spec private COLORS = { - success: :green, - fail: :red, - error: :red, - pending: :yellow, - comment: :cyan, + success: Colorize::ColorANSI::Green, + fail: Colorize::ColorANSI::Red, + error: Colorize::ColorANSI::Red, + pending: Colorize::ColorANSI::Yellow, + comment: Colorize::ColorANSI::Cyan, } private LETTERS = { From 64cf180870e4b42e4a06c343bd30ad29140605cb Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 13:15:48 +0200 Subject: [PATCH 05/28] Rename some variables and add documentation to surround --- src/colorize.cr | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index 14d6d33b7378..6e064f13551d 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -271,21 +271,21 @@ struct Colorize::Object(T) @enabled = Colorize.enabled? end - {% for name in COLORS %} - def {{name.id}} - @fore = ColorANSI::{{name.camelcase.id}} + {% for color in COLORS %} + def {{color.id}} + @fore = ColorANSI::{{color.camelcase.id}} self end - def on_{{name.id}} - @back = ColorANSI::{{name.camelcase.id}} + def on_{{color.id}} + @back = ColorANSI::{{color.camelcase.id}} self end {% end %} - {% for name in MODES %} - def {{name.id}} - @mode |= Mode::{{name.capitalize.id}} + {% for mode in MODES %} + def {{mode.id}} + @mode |= Mode::{{mode.capitalize.id}} self end {% end %} @@ -325,11 +325,30 @@ struct Colorize::Object(T) def inspect(io : IO) : Nil internal_io = IO::Memory.new (surround(internal_io) do - @object.to_s internal_io + @object.to_s(internal_io) end) io << internal_io.to_s.inspect end + # Surrounds *io* by the ANSI escape codes and let's you build colored strings: + # + # ``` + # io = IO::Memory.new + # + # with_color.red.surround(io) do + # io << "colorful" + # with_color.green.bold.surround(io) do + # io << " hello " + # end + # with_color.blue.surround(io) do + # io << "world" + # end + # io << " string" + # end + # + # io.to_s # => "colorful hello world string" + # # Where "colorful" is red, "hello" green, "world" blue and " string" red again + # ``` def surround(io = STDOUT) return yield io unless @enabled From c22e57c2091e851a211a6defaa6f5264ad7dcf31 Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 13:18:16 +0200 Subject: [PATCH 06/28] Fix specs --- spec/std/colorize_spec.cr | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/spec/std/colorize_spec.cr b/spec/std/colorize_spec.cr index 0d3989f0f473..fa57bbcb6260 100644 --- a/spec/std/colorize_spec.cr +++ b/spec/std/colorize_spec.cr @@ -1,7 +1,7 @@ require "spec" require "colorize" -private def colorize(obj, *args) +private def colorize(obj, *args : Colorize::ColorANSI) obj.colorize(*args).toggle(true) end @@ -107,26 +107,8 @@ describe "colorize" do colorize("hello").mode(:bold).to_s.should eq("\e[1mhello\e[0m") end - it "raises on unknown foreground color" do - expect_raises ArgumentError, "Unknown color: brown" do - colorize("hello", :brown) - end - end - - it "raises on unknown background color" do - expect_raises ArgumentError, "Unknown color: brown" do - colorize("hello").back(:brown) - end - end - - it "raises on unknown mode" do - expect_raises ArgumentError, "Unknown mode: bad" do - colorize("hello").mode(:bad) - end - end - it "inspects" do - colorize("hello", :red).inspect.should eq("\e[31m\"hello\"\e[0m") + colorize("hello", :red).inspect.should eq("\"\\e[31mhello\\e[0m\"") end it "colorizes with surround" do From 9432a4cfe0bf3aaaf07c009c2ff06a39b58bc045 Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 13:24:09 +0200 Subject: [PATCH 07/28] Fix specs --- spec/std/colorize_spec.cr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/std/colorize_spec.cr b/spec/std/colorize_spec.cr index fa57bbcb6260..da71e072dc11 100644 --- a/spec/std/colorize_spec.cr +++ b/spec/std/colorize_spec.cr @@ -1,7 +1,7 @@ require "spec" require "colorize" -private def colorize(obj, *args : Colorize::ColorANSI) +private def colorize(obj) obj.colorize(*args).toggle(true) end @@ -99,7 +99,7 @@ describe "colorize" do end it "colorizes foreground with symbol" do - colorize("hello", :red).to_s.should eq("\e[31mhello\e[0m") + colorize("hello").fore(:red).to_s.should eq("\e[31mhello\e[0m") colorize("hello").fore(:red).to_s.should eq("\e[31mhello\e[0m") end @@ -108,7 +108,7 @@ describe "colorize" do end it "inspects" do - colorize("hello", :red).inspect.should eq("\"\\e[31mhello\\e[0m\"") + colorize("hello").fore(:red).inspect.should eq("\"\\e[31mhello\\e[0m\"") end it "colorizes with surround" do From 38ef63460d91edb23382c971c535e1430d375f4a Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 13:28:51 +0200 Subject: [PATCH 08/28] Remove (*args) --- spec/std/colorize_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/std/colorize_spec.cr b/spec/std/colorize_spec.cr index da71e072dc11..2b6fe1c9eedf 100644 --- a/spec/std/colorize_spec.cr +++ b/spec/std/colorize_spec.cr @@ -2,7 +2,7 @@ require "spec" require "colorize" private def colorize(obj) - obj.colorize(*args).toggle(true) + obj.colorize.toggle(true) end private def with_color_wrap(*args) From f0cac302035f168d79a48a57c4e3ed6970822f8f Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 14:17:38 +0200 Subject: [PATCH 09/28] Make RGB colorize's arguments more explicit --- src/colorize.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index 6e064f13551d..f9ab71336936 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -156,8 +156,8 @@ module Colorize::ObjectExtensions end # Turns `self` into a `Colorize::Object` and colors it with an RGB color. - def colorize(r : UInt8, g : UInt8, b : UInt8) - Colorize::Object.new(self).fore(Colorize::ColorRGB.new(r, g, b)) + def colorize(red : UInt8, green : UInt8, blue : UInt8) + Colorize::Object.new(self).fore(Colorize::ColorRGB.new(red, green, blue)) end end From 80180f0342bb9e9d40985d099cbdcae7bca4f4f9 Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 14:23:47 +0200 Subject: [PATCH 10/28] Use color enum members in 2048.cr --- samples/2048.cr | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/samples/2048.cr b/samples/2048.cr index 090db3b46f2d..5eeee9acacc0 100644 --- a/samples/2048.cr +++ b/samples/2048.cr @@ -3,24 +3,26 @@ require "colorize" module Screen + alias Color = Colorize::ColorANSI + TILES = { - 0 => {:white, nil}, - 2 => {:black, :white}, - 4 => {:blue, :white}, - 8 => {:black, :yellow}, - 16 => {:white, :red}, - 32 => {:black, :red}, - 64 => {:white, :magenta}, - 128 => {:red, :yellow}, - 256 => {:magenta, :yellow}, - 512 => {:white, :yellow}, - 1024 => {:white, :yellow}, - 2048 => {:white, :yellow}, - 4096 => {:white, :black}, - 8192 => {:white, :black}, - 16384 => {:white, :black}, - 32768 => {:white, :black}, - 65536 => {:white, :black}, + 0 => {Color::White, nil}, + 2 => {Color::Black, Color::White}, + 4 => {Color::Blue, Color::White}, + 8 => {Color::Black, Color::Yellow}, + 16 => {Color::White, Color::Red}, + 32 => {Color::Black, Color::Red}, + 64 => {Color::White, Color::Magenta}, + 128 => {Color::Red, Color::Yellow}, + 256 => {Color::Magenta, Color::Yellow}, + 512 => {Color::White, Color::Yellow}, + 1024 => {Color::White, Color::Yellow}, + 2048 => {Color::White, Color::Yellow}, + 4096 => {Color::White, Color::Black}, + 8192 => {Color::White, Color::Black}, + 16384 => {Color::White, Color::Black}, + 32768 => {Color::White, Color::Black}, + 65536 => {Color::White, Color::Black}, } def self.colorize_for(tile) From e3b84b632e7012d079fe57a1b5df4b7f50593b71 Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 15:39:51 +0200 Subject: [PATCH 11/28] Add back Colorize::Object(T)#on and remove some documentation --- src/colorize.cr | 36 +++++------------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index f9ab71336936..36a3ccde6089 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -80,37 +80,7 @@ # "foo".colorize(some_bool ? :green : :default) # ``` # -# Available colors are: -# ``` -# :default -# :black -# :red -# :green -# :yellow -# :blue -# :magenta -# :cyan -# :light_gray -# :dark_gray -# :light_red -# :light_green -# :light_yellow -# :light_blue -# :light_magenta -# :light_cyan -# :white -# ``` -# -# Available text decorations are: -# ``` -# :bold -# :bright -# :dim -# :underline -# :blink -# :reverse -# :hidden -# ``` +# See `Colorize::ColorANSI` and `Colorize::Mode` for the available colors and text decorations. module Colorize # If this value is `true`, `Colorize::Object` is enabled by default. # But if this value is `false`, `Colorize::Object` is disabled. @@ -283,6 +253,10 @@ struct Colorize::Object(T) end {% end %} + def on(color : Color) + back color + end + {% for mode in MODES %} def {{mode.id}} @mode |= Mode::{{mode.capitalize.id}} From ef1900188322a2866d108c00a3f068768fc2f956 Mon Sep 17 00:00:00 2001 From: r00ster Date: Thu, 18 Apr 2019 23:29:09 +0200 Subject: [PATCH 12/28] Remove unneeded parentheses and avoid capitalize in macro --- src/colorize.cr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index 36a3ccde6089..5f7493af64bf 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -298,9 +298,9 @@ struct Colorize::Object(T) # Inspects this object and makes the ANSI escape codes visible. def inspect(io : IO) : Nil internal_io = IO::Memory.new - (surround(internal_io) do + surround(internal_io) do @object.to_s(internal_io) - end) + end io << internal_io.to_s.inspect end @@ -398,8 +398,8 @@ struct Colorize::Object(T) unless no_mode # Can't reuse MODES constant because it has bold/bright duplicated - {% for mode in %w(bold dim underline blink reverse hidden) %} - if mode.includes? Mode::{{mode.capitalize.id}} + {% for mode in %w(Bold Dim Underline Blink Reverse Hidden) %} + if mode.includes? Mode::{{mode.id}} io << ';' if printed io << MODE_{{mode.upcase.id}} printed = true From 743a6a908648464a83fc6d9234e9b136ee3a1ea2 Mon Sep 17 00:00:00 2001 From: r00ster Date: Fri, 19 Apr 2019 15:17:34 +0200 Subject: [PATCH 13/28] Improve documentation --- src/colorize.cr | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index 5f7493af64bf..c851a0571b75 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -4,6 +4,7 @@ # when it comes to obtaining a string representation of the object. # # Its first argument changes the foreground color: +# # ``` # require "colorize" # @@ -13,6 +14,7 @@ # ``` # # There are alternative ways to change the foreground color: +# # ``` # require "colorize" # @@ -21,6 +23,7 @@ # ``` # # To change the background color, the following methods are available: +# # ``` # require "colorize" # @@ -30,6 +33,7 @@ # ``` # # You can also pass an RGB color to `colorize`: +# # ``` # require "colorize" # @@ -41,6 +45,7 @@ # ``` # # Or an 8-bit color: +# # ``` # require "colorize" # @@ -48,6 +53,7 @@ # ``` # # It's also possible to change the text decoration: +# # ``` # require "colorize" # @@ -57,6 +63,7 @@ # # The `colorize` method returns a `Colorize::Object` instance, # which allows chaining methods together: +# # ``` # require "colorize" # @@ -65,6 +72,7 @@ # # With the `toggle` method you can temporarily disable adding the escape codes. # Settings of the instance are preserved however and can be turned back on later: +# # ``` # require "colorize" # @@ -74,18 +82,16 @@ # # The color `:default` leaves the object's representation as it is but the object is a `Colorize::Object` then # which is handy in conditions such as: +# # ``` # require "colorize" # # "foo".colorize(some_bool ? :green : :default) # ``` # -# See `Colorize::ColorANSI` and `Colorize::Mode` for the available colors and text decorations. +# See `Colorize::ColorANSI` and `Colorize::Mode` for all available colors and text decorations. module Colorize - # If this value is `true`, `Colorize::Object` is enabled by default. - # But if this value is `false`, `Colorize::Object` is disabled. - # - # The default value is `true`. + # Objects will only be colored if this is `true`. # # ``` # require "colorize" @@ -100,7 +106,15 @@ module Colorize # NOTE: This is by default disabled on non-TTY devices as they most likely do not support ANSI escape codes. class_property? enabled : Bool = STDOUT.tty? && STDERR.tty? - # Resets the color of the object to the default. + # Resets the color and text decoration of the *io*. + # + # ``` + # with_color.green.surround(io) do + # io << "green" + # Colorize.reset + # io << " default" + # end + # ``` def self.reset(io = STDOUT) io << "\e[0m" if enabled? end @@ -199,15 +213,15 @@ module Colorize # A text decoration. # - # Note that not all decorations are supported in all terminals. - # When a decoration is not supported, the text won't have any decoration. + # Note that not all text decorations are supported in all terminals. + # When a text decoration is not supported, it will not be visible. @[Flags] enum Mode # Makes the text bold. - Bold = 1 + Bold # Makes the text color bright. - Bright = 1 - # Dims the text color, the opposite of `Bold` and `Bright`. + Bright + # Dims the text color. Dim # Underlines the text. Underline @@ -220,7 +234,7 @@ module Colorize end end -# A colorize object colors and decorations can be applied to. +# A colorized object. Colors and text decorations can be modified. struct Colorize::Object(T) private MODE_NONE = '0' private MODE_BOLD = '1' @@ -307,6 +321,8 @@ struct Colorize::Object(T) # Surrounds *io* by the ANSI escape codes and let's you build colored strings: # # ``` + # require "colorize" + # # io = IO::Memory.new # # with_color.red.surround(io) do From 6425ad986c26c78d8daee097f35804585c741a3f Mon Sep 17 00:00:00 2001 From: r00ster Date: Fri, 19 Apr 2019 15:22:56 +0200 Subject: [PATCH 14/28] Fix Mode enum --- src/colorize.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index c851a0571b75..8acc02c8fb69 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -218,9 +218,9 @@ module Colorize @[Flags] enum Mode # Makes the text bold. - Bold + Bold = 1 # Makes the text color bright. - Bright + Bright = 1 # Dims the text color. Dim # Underlines the text. From c0f44058229c13522202b36038994d7875a6f14a Mon Sep 17 00:00:00 2001 From: r00ster Date: Wed, 24 Apr 2019 22:24:06 +0200 Subject: [PATCH 15/28] Remove COLORS and MODES and use ColorANSI and Mode --- src/colorize.cr | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index 8acc02c8fb69..a2b13bbd2698 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -245,9 +245,6 @@ struct Colorize::Object(T) private MODE_REVERSE = '7' private MODE_HIDDEN = '8' - private COLORS = %w(default black red green yellow blue magenta cyan light_gray dark_gray light_red light_green light_yellow light_blue light_magenta light_cyan white) - private MODES = %w(bold bright dim underline blink reverse hidden) - def initialize(@object : T) @fore = ColorANSI::Default @back = ColorANSI::Default @@ -255,7 +252,7 @@ struct Colorize::Object(T) @enabled = Colorize.enabled? end - {% for color in COLORS %} + {% for color in ColorANSI.constants %} def {{color.id}} @fore = ColorANSI::{{color.camelcase.id}} self @@ -271,7 +268,7 @@ struct Colorize::Object(T) back color end - {% for mode in MODES %} + {% for mode in Mode.constants %} def {{mode.id}} @mode |= Mode::{{mode.capitalize.id}} self @@ -318,7 +315,7 @@ struct Colorize::Object(T) io << internal_io.to_s.inspect end - # Surrounds *io* by the ANSI escape codes and let's you build colored strings: + # Surrounds *io* by the ANSI escape codes and lets you build colored strings: # # ``` # require "colorize" From 8042ed458e6fc0dda8940c6bb7f3b6639937442e Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Mon, 13 May 2019 18:22:12 +0200 Subject: [PATCH 16/28] More documentation --- src/colorize.cr | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/colorize.cr b/src/colorize.cr index a2b13bbd2698..b473a24a5a53 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -120,10 +120,16 @@ module Colorize end end +# Returns an empty colorized string. +# +# This is useful for building colored strings. See `Colorize#surround`. def with_color "".colorize end +# Returns an empty string colorized with *color*. +# +# This is useful for building colored strings. See `Colorize#surround`. def with_color(color : Colorize::Color) "".colorize(color) end From 0f84ac8371fac9c3f6e3362b1eb66b12f1b5c1a2 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Mon, 13 May 2019 18:29:07 +0200 Subject: [PATCH 17/28] Fix specs --- src/colorize.cr | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index b473a24a5a53..c2dfa53cc03b 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -259,13 +259,13 @@ struct Colorize::Object(T) end {% for color in ColorANSI.constants %} - def {{color.id}} - @fore = ColorANSI::{{color.camelcase.id}} + def {{color.underscore.id}} + @fore = ColorANSI::{{color.id}} self end - def on_{{color.id}} - @back = ColorANSI::{{color.camelcase.id}} + def on_{{color.underscore.id}} + @back = ColorANSI::{{color.id}} self end {% end %} @@ -275,8 +275,8 @@ struct Colorize::Object(T) end {% for mode in Mode.constants %} - def {{mode.id}} - @mode |= Mode::{{mode.capitalize.id}} + def {{mode.underscore.id}} + @mode |= Mode::{{mode.id}} self end {% end %} From ba565086cd335deb1c41fd6d7552e06881c76e15 Mon Sep 17 00:00:00 2001 From: r00ster Date: Mon, 13 May 2019 22:09:05 +0200 Subject: [PATCH 18/28] Remove the private attribute from two methods I would like to use these two methods in my code because I want to append only the start ANSI escape sequence, not the end. I want to append the end at a different time. This would allow me to implement something efficiently. --- src/colorize.cr | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index c2dfa53cc03b..988555e4a33c 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -350,7 +350,8 @@ struct Colorize::Object(T) end end - private def to_named_tuple + # :nodoc: + def to_named_tuple { fore: @fore, back: @back, @@ -377,7 +378,8 @@ struct Colorize::Object(T) end end - private def self.append_start(io, color) + # :nodoc: + def self.append_start(io, color) last_color_is_default = @@last_color[:fore] == ColorANSI::Default && @@last_color[:back] == ColorANSI::Default && From d8ce0cef4dcf8345a2dbe1f21b44db81cb7bdac5 Mon Sep 17 00:00:00 2001 From: r00ster Date: Mon, 13 May 2019 22:11:29 +0200 Subject: [PATCH 19/28] Minor documentation improvements --- src/colorize.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index 988555e4a33c..44a81ed9e7c5 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -220,7 +220,7 @@ module Colorize # A text decoration. # # Note that not all text decorations are supported in all terminals. - # When a text decoration is not supported, it will not be visible. + # When a text decoration is not supported, it will leave the text unaffected. @[Flags] enum Mode # Makes the text bold. @@ -233,7 +233,7 @@ module Colorize Underline # Makes the text blink slowly. Blink - # Swaps the foreground and background colors. + # Swaps the foreground and background colors of the text. Reverse # Makes the text invisible. Hidden From a9fda369e7dc0ae535b305883f882c96d7f46eed Mon Sep 17 00:00:00 2001 From: r00ster Date: Sat, 28 Sep 2019 07:24:39 +0200 Subject: [PATCH 20/28] Add a missing comma --- src/spec/dsl.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spec/dsl.cr b/src/spec/dsl.cr index e9fca37b4294..87b75c43f9cf 100644 --- a/src/spec/dsl.cr +++ b/src/spec/dsl.cr @@ -4,7 +4,7 @@ require "option_parser" module Spec private COLORS = { success: Colorize::ColorANSI::Green, - fail: Colorize::ColorANSI::Red + fail: Colorize::ColorANSI::Red, error: Colorize::ColorANSI::Red, pending: Colorize::ColorANSI::Yellow, comment: Colorize::ColorANSI::Cyan, From 95569e2a9347199fcd1d4c10b1e27c6ac3111321 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sat, 28 Sep 2019 15:02:06 +0200 Subject: [PATCH 21/28] Remove bright --- spec/std/colorize_spec.cr | 1 - src/colorize.cr | 6 +----- src/compiler/crystal/tools/print_hierarchy.cr | 2 +- src/compiler/crystal/util.cr | 2 +- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/spec/std/colorize_spec.cr b/spec/std/colorize_spec.cr index 2b6fe1c9eedf..3a1f07c89004 100644 --- a/spec/std/colorize_spec.cr +++ b/spec/std/colorize_spec.cr @@ -78,7 +78,6 @@ describe "colorize" do it "colorizes mode" do colorize("hello").bold.to_s.should eq("\e[1mhello\e[0m") - colorize("hello").bright.to_s.should eq("\e[1mhello\e[0m") colorize("hello").dim.to_s.should eq("\e[2mhello\e[0m") colorize("hello").underline.to_s.should eq("\e[4mhello\e[0m") colorize("hello").blink.to_s.should eq("\e[5mhello\e[0m") diff --git a/src/colorize.cr b/src/colorize.cr index 44a81ed9e7c5..cff7dbcc64d0 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -225,8 +225,6 @@ module Colorize enum Mode # Makes the text bold. Bold = 1 - # Makes the text color bright. - Bright = 1 # Dims the text color. Dim # Underlines the text. @@ -244,7 +242,6 @@ end struct Colorize::Object(T) private MODE_NONE = '0' private MODE_BOLD = '1' - private MODE_BRIGHT = '1' private MODE_DIM = '2' private MODE_UNDERLINE = '4' private MODE_BLINK = '5' @@ -418,8 +415,7 @@ struct Colorize::Object(T) end unless no_mode - # Can't reuse MODES constant because it has bold/bright duplicated - {% for mode in %w(Bold Dim Underline Blink Reverse Hidden) %} + {% for mode in Mode.constants %} if mode.includes? Mode::{{mode.id}} io << ';' if printed io << MODE_{{mode.upcase.id}} diff --git a/src/compiler/crystal/tools/print_hierarchy.cr b/src/compiler/crystal/tools/print_hierarchy.cr index f789335ca966..b00e9475e535 100644 --- a/src/compiler/crystal/tools/print_hierarchy.cr +++ b/src/compiler/crystal/tools/print_hierarchy.cr @@ -223,7 +223,7 @@ module Crystal print " bytes)" end else - print "MISSING".colorize.red.bright + print "MISSING".colorize.red.bold end end puts diff --git a/src/compiler/crystal/util.cr b/src/compiler/crystal/util.cr index 9e5ef4a69bfa..6246086ecba7 100644 --- a/src/compiler/crystal/util.cr +++ b/src/compiler/crystal/util.cr @@ -15,7 +15,7 @@ module Crystal def self.error(msg, color, exit_code = 1, stderr = STDERR, leading_error = true) stderr.print "Error: ".colorize.toggle(color).red.bold if leading_error - stderr.puts msg.colorize.toggle(color).bright + stderr.puts msg.colorize.toggle(color).bold exit(exit_code) if exit_code end From c81322c73dbdf0a5d39db04366e1c469bd6d8c42 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sat, 28 Sep 2019 15:13:31 +0200 Subject: [PATCH 22/28] Don't generate all and none methods --- src/colorize.cr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index cff7dbcc64d0..8c0b5bf1d92e 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -224,7 +224,7 @@ module Colorize @[Flags] enum Mode # Makes the text bold. - Bold = 1 + Bold # Dims the text color. Dim # Underlines the text. @@ -255,7 +255,7 @@ struct Colorize::Object(T) @enabled = Colorize.enabled? end - {% for color in ColorANSI.constants %} + {% for color in ColorANSI.constants.reject { |constant| constant == "All" || constant == "None" } %} def {{color.underscore.id}} @fore = ColorANSI::{{color.id}} self @@ -271,7 +271,7 @@ struct Colorize::Object(T) back color end - {% for mode in Mode.constants %} + {% for mode in Mode.constants.reject { |constant| constant == "All" || constant == "None" } %} def {{mode.underscore.id}} @mode |= Mode::{{mode.id}} self @@ -415,7 +415,7 @@ struct Colorize::Object(T) end unless no_mode - {% for mode in Mode.constants %} + {% for mode in Mode.constants.reject { |constant| constant == "All" || constant == "None" } %} if mode.includes? Mode::{{mode.id}} io << ';' if printed io << MODE_{{mode.upcase.id}} From 94fc12eb83976700f9e39776ede183393756cfd9 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sat, 28 Sep 2019 16:05:06 +0200 Subject: [PATCH 23/28] Add bright back but deprecate it --- src/colorize.cr | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/colorize.cr b/src/colorize.cr index 8c0b5bf1d92e..0c7360a3d488 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -224,7 +224,10 @@ module Colorize @[Flags] enum Mode # Makes the text bold. - Bold + Bold = 1 + # Makes the text color bright. + @[Deprecated("Please use `bold` instead.")] + Bright = 1 # Dims the text color. Dim # Underlines the text. @@ -242,6 +245,7 @@ end struct Colorize::Object(T) private MODE_NONE = '0' private MODE_BOLD = '1' + private MODE_BRIGHT = '1' # TODO: Remove this in the future private MODE_DIM = '2' private MODE_UNDERLINE = '4' private MODE_BLINK = '5' @@ -278,6 +282,12 @@ struct Colorize::Object(T) end {% end %} + @[Deprecated("Please use `bold` instead.")] + def bright + @mode |= Mode::Bold + self + end + # Sets the foreground color of the object to *color*. def fore(color : Color) @fore = color @@ -290,8 +300,16 @@ struct Colorize::Object(T) self end + @@warning_printed = false + # Adds *mode* to the text's decorations. def mode(mode : Mode) + # TODO: Remove this in the future + if mode == Mode::Bright + puts "Warning: The color `bright` is deprecated. Please use `bold` instead.".colorize(:light_yellow) unless @@warning_printed + @@warning_printed = true + end + @mode |= mode self end @@ -415,7 +433,10 @@ struct Colorize::Object(T) end unless no_mode - {% for mode in Mode.constants.reject { |constant| constant == "All" || constant == "None" } %} + # TODO: replace this by + # {% for mode in Mode.constants.reject { |constant| constant == "All" || constant == "None" } %} + # when bright gets removed + {% for mode in %w(Bold Dim Underline Blink Reverse Hidden) %} if mode.includes? Mode::{{mode.id}} io << ';' if printed io << MODE_{{mode.upcase.id}} From fedd9d3878315d21a89d8c1ffc1ee0c9025e7caa Mon Sep 17 00:00:00 2001 From: r00ster Date: Sun, 29 Sep 2019 07:54:22 +0200 Subject: [PATCH 24/28] Commit Sija's suggestion Co-Authored-By: Sijawusz Pur Rahnama --- src/colorize.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/colorize.cr b/src/colorize.cr index 0c7360a3d488..c189fe5f8c4d 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -147,7 +147,7 @@ module Colorize::ObjectExtensions # Turns `self` into a `Colorize::Object` and colors it with an RGB color. def colorize(red : UInt8, green : UInt8, blue : UInt8) - Colorize::Object.new(self).fore(Colorize::ColorRGB.new(red, green, blue)) + colorize(Colorize::ColorRGB.new(red, green, blue)) end end From 073e2d779608bbef0c7d7eee90d1810be1b1b763 Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Sun, 29 Sep 2019 08:12:53 +0200 Subject: [PATCH 25/28] Remove useless `u8`s, fix the warning message and improve docs --- spec/std/colorize_spec.cr | 8 ++++---- src/colorize.cr | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/std/colorize_spec.cr b/spec/std/colorize_spec.cr index 3a1f07c89004..e1ac1a686561 100644 --- a/spec/std/colorize_spec.cr +++ b/spec/std/colorize_spec.cr @@ -42,11 +42,11 @@ describe "colorize" do end it "colorizes foreground with 8-bit color" do - colorize("hello").fore(Colorize::Color256.new(123u8)).to_s.should eq("\e[38;5;123mhello\e[0m") + colorize("hello").fore(Colorize::Color256.new(123)).to_s.should eq("\e[38;5;123mhello\e[0m") end it "colorizes foreground with true color" do - colorize("hello").fore(Colorize::ColorRGB.new(12u8, 34u8, 56u8)).to_s.should eq("\e[38;2;12;34;56mhello\e[0m") + colorize("hello").fore(Colorize::ColorRGB.new(12, 34, 56)).to_s.should eq("\e[38;2;12;34;56mhello\e[0m") end it "colorizes background" do @@ -69,11 +69,11 @@ describe "colorize" do end it "colorizes background with 8-bit color" do - colorize("hello").back(Colorize::Color256.new(123u8)).to_s.should eq("\e[48;5;123mhello\e[0m") + colorize("hello").back(Colorize::Color256.new(123)).to_s.should eq("\e[48;5;123mhello\e[0m") end it "colorizes background with true color" do - colorize("hello").back(Colorize::ColorRGB.new(12u8, 34u8, 56u8)).to_s.should eq("\e[48;2;12;34;56mhello\e[0m") + colorize("hello").back(Colorize::ColorRGB.new(12, 34, 56)).to_s.should eq("\e[48;2;12;34;56mhello\e[0m") end it "colorizes mode" do diff --git a/src/colorize.cr b/src/colorize.cr index c189fe5f8c4d..f2a5fe240e34 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -140,7 +140,7 @@ module Colorize::ObjectExtensions Colorize::Object.new(self) end - # Turns `self` into a `Colorize::Object` and colors it. + # Turns `self` into a `Colorize::Object` and colors it with a color. def colorize(fore : Color) Colorize::Object.new(self).fore(fore) end @@ -306,7 +306,7 @@ struct Colorize::Object(T) def mode(mode : Mode) # TODO: Remove this in the future if mode == Mode::Bright - puts "Warning: The color `bright` is deprecated. Please use `bold` instead.".colorize(:light_yellow) unless @@warning_printed + puts "Warning: The text decoration `bright` is deprecated. Please use `bold` instead.".colorize(:light_yellow) unless @@warning_printed @@warning_printed = true end From 830d994fa9382cbd412356055bd590c866a3b864 Mon Sep 17 00:00:00 2001 From: r00ster Date: Sun, 22 Dec 2019 15:28:02 +0100 Subject: [PATCH 26/28] Improve an example --- src/colorize.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/colorize.cr b/src/colorize.cr index f2a5fe240e34..16e355a9d3fb 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -86,7 +86,7 @@ # ``` # require "colorize" # -# "foo".colorize(some_bool ? :green : :default) +# "foo".colorize(Random::DEFAULT.next_bool ? :green : :default) # ``` # # See `Colorize::ColorANSI` and `Colorize::Mode` for all available colors and text decorations. From 3c453bf7f097f98a8fc35addec4681512e2ccb95 Mon Sep 17 00:00:00 2001 From: r00ster Date: Sun, 22 Dec 2019 15:45:45 +0100 Subject: [PATCH 27/28] Remove on_tty_only! --- src/spec.cr | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/spec.cr b/src/spec.cr index d81304fa5577..5c3b8a06852e 100644 --- a/src/spec.cr +++ b/src/spec.cr @@ -93,8 +93,6 @@ require "./spec/dsl" module Spec end -Colorize.on_tty_only! - OptionParser.parse do |opts| opts.banner = "crystal spec runner" opts.on("-e ", "--example STRING", "run examples whose full nested names include STRING") do |pattern| From 9f19d98a7c80895bae09c2605a3abf9136c8b19c Mon Sep 17 00:00:00 2001 From: r00ster91 Date: Fri, 23 Apr 2021 12:54:27 +0200 Subject: [PATCH 28/28] Re-add on_tty_only! --- src/colorize.cr | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/colorize.cr b/src/colorize.cr index 52b4b9dc4e3c..659a460ca685 100644 --- a/src/colorize.cr +++ b/src/colorize.cr @@ -107,6 +107,15 @@ module Colorize # This is also be disabled if the environment variable `TERM` is "dumb". class_property? enabled : Bool = STDOUT.tty? && STDERR.tty? && ENV["TERM"]? != "dumb" + # 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. + @[Deprecated("This is now done by default and there is no longer any need to use this manually.")] + def self.on_tty_only! + self.enabled = STDOUT.tty? && STDERR.tty? && ENV["TERM"]? != "dumb" + end + # Resets the color and text decoration of the *io*. # # ```