Skip to content

Commit

Permalink
Refactor Colorize#surround
Browse files Browse the repository at this point in the history
This is one of the separations of crystal-lang#3925.

Remove `surround` and rename `push` to `surround`, now `push` is
derecated.
(This reason is dscribed in crystal-lang#3925 (comment))
  • Loading branch information
makenowjust committed Mar 26, 2017
1 parent 917971d commit 67e3e4b
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 42 deletions.
46 changes: 33 additions & 13 deletions spec/std/colorize_spec.cr
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
require "spec"
require "colorize"

private class ColorizeToS
def to_s(io)
io << "hello"
io << "world".colorize.blue
io << "bye"
end
end

describe "colorize" do
it "colorizes without change" do
"hello".colorize.to_s.should eq("hello")
Expand Down Expand Up @@ -97,44 +105,56 @@ describe "colorize" do
"hello".colorize(:red).inspect.should eq("\e[31m\"hello\"\e[0m")
end

it "colorizes io with method" do
it "colorizes with surround" do
io = IO::Memory.new
with_color.red.surround(io) do
io << "hello"
with_color.green.surround(io) do
io << "world"
end
io << "bye"
end
io.to_s.should eq("\e[31mhello\e[0m")
io.to_s.should eq("\e[31mhello\e[0;32mworld\e[0;31mbye\e[0m")
end

it "colorizes io with symbol" do
it "colorizes with surround and reset" do
io = IO::Memory.new
with_color(:red).surround(io) do
with_color.red.surround(io) do
io << "hello"
with_color.green.bold.surround(io) do
io << "world"
end
io << "bye"
end
io.to_s.should eq("\e[31mhello\e[0m")
io.to_s.should eq("\e[31mhello\e[0;32;1mworld\e[0;31mbye\e[0m")
end

it "colorizes with push and pop" do
it "colorizes with surround and no reset" do
io = IO::Memory.new
with_color.red.push(io) do
with_color.red.surround(io) do
io << "hello"
with_color.green.push(io) do
with_color.red.surround(io) do
io << "world"
end
io << "bye"
end
io.to_s.should eq("\e[31mhello\e[0;32mworld\e[0;31mbye\e[0m")
io.to_s.should eq("\e[31mhelloworldbye\e[0m")
end

it "colorizes with push and pop resets" do
it "colorizes with surround and default" do
io = IO::Memory.new
with_color.red.push(io) do
with_color.red.surround(io) do
io << "hello"
with_color.green.bold.push(io) do
with_color.surround(io) do
io << "world"
end
io << "bye"
end
io.to_s.should eq("\e[31mhello\e[0;32;1mworld\e[0;31mbye\e[0m")
io.to_s.should eq("\e[31mhello\e[0mworld\e[31mbye\e[0m")
end

it "colorizes with to_s" do
ColorizeToS.new.colorize.red.to_s.should eq("\e[31mhello\e[0;34mworld\e[0;31mbye\e[0m")
end

it "toggles off" do
Expand Down
78 changes: 49 additions & 29 deletions src/colorize.cr
Original file line number Diff line number Diff line change
Expand Up @@ -239,64 +239,88 @@ struct Colorize::Object(T)
end

def surround(io = STDOUT)
must_append_end = append_start(io)
yield io
append_end(io) if must_append_end
end
return yield io unless @on

STACK = [] of Colorize::Object(String)
Object.surround(io, to_named_tuple) do |io|
yield io
end
end

# DEPRECATED: Use `#surround`.
def push(io = STDOUT)
last_color = STACK.last?

append_start(io, !!last_color)
{{ puts "Warning: `Colorize::Object#push` is deprecated and will be removed, use `Colorize::Object#surround` instead".id }}
surround(io) { |io| yield io }
end

STACK.push self
yield io
STACK.pop
private def to_named_tuple
{
fore: @fore,
back: @back,
mode: @mode,
}
end

if last_color
last_color.append_start(io, true)
else
append_end(io)
@@last_color = {
fore: FORE_DEFAULT,
back: BACK_DEFAULT,
mode: 0,
}

protected def self.surround(io, color)
last_color = @@last_color
must_append_end = append_start(io, color)
@@last_color = color

begin
yield io
ensure
append_start(io, last_color) if must_append_end
@@last_color = last_color
end
end

protected def append_start(io, reset = false)
return false unless @on
private def self.append_start(io, color)
last_color_is_default =
@@last_color[:fore] == FORE_DEFAULT &&
@@last_color[:back] == BACK_DEFAULT &&
@@last_color[:mode] == 0

fore = color[:fore]
back = color[:back]
mode = color[:mode]

fore_is_default = @fore == FORE_DEFAULT
back_is_default = @back == BACK_DEFAULT
mode_is_default = @mode == 0
fore_is_default = fore == FORE_DEFAULT
back_is_default = back == BACK_DEFAULT
mode_is_default = mode == 0

if fore_is_default && back_is_default && mode_is_default && !reset
if fore_is_default && back_is_default && mode_is_default && last_color_is_default || @@last_color == color
false
else
io << "\e["

printed = false

if reset
unless last_color_is_default
io << MODE_DEFAULT
printed = true
end

unless fore_is_default
io << ";" if printed
io << @fore
io << fore
printed = true
end

unless back_is_default
io << ";" if printed
io << @back
io << back
printed = true
end

unless mode_is_default
# Can't reuse MODES constant because it has bold/bright duplicated
{% for name in %w(bold dim underline blink reverse hidden) %}
if (@mode & MODE_{{name.upcase.id}}_FLAG) != 0
if (mode & MODE_{{name.upcase.id}}_FLAG) != 0
io << ";" if printed
io << MODE_{{name.upcase.id}}
printed = true
Expand All @@ -309,8 +333,4 @@ struct Colorize::Object(T)
true
end
end

protected def append_end(io)
io << "\e[0m"
end
end

0 comments on commit 67e3e4b

Please sign in to comment.