diff --git a/lib/rscons/ansi.rb b/lib/rscons/ansi.rb index da91e41..44f8cf5 100644 --- a/lib/rscons/ansi.rb +++ b/lib/rscons/ansi.rb @@ -3,6 +3,8 @@ module Rscons module Ansi class << self + RESET = "\e[0m" + # Write a message to an IO with ANSI escape codes. # # @param io [IO] @@ -17,27 +19,57 @@ module Rscons do_color = do_ansi?(io) end out = "" - message.each do |m| - if m.is_a?(String) - out += m - elsif do_color - case m - when :red - out += "\e[0;31m" - when :green - out += "\e[0;32m" - when :yellow - out += "\e[0;33m" - when :blue - out += "\e[0;34m" - when :magenta - out += "\e[0;35m" - when :cyan - out += "\e[0;36m" - when :white - out += "\e[0;37m" - when :reset - out += "\e[0m" + if do_color + current_color = RESET + desired_color = RESET + message.each do |m| + if m.is_a?(String) + lines = m.split("\n", -1) + lines.each_with_index do |line, i| + if line != "" + if current_color != desired_color + out += desired_color + current_color = desired_color + end + out += line + end + if i < lines.size - 1 + # A newline follows + if current_color != RESET + out += RESET + current_color = RESET + end + out += "\n" + end + end + else + case m + when :red; desired_color = "\e[0;31m" + when :green; desired_color = "\e[0;32m" + when :yellow; desired_color = "\e[0;33m" + when :blue; desired_color = "\e[0;34m" + when :magenta; desired_color = "\e[0;35m" + when :cyan; desired_color = "\e[0;36m" + when :white; desired_color = "\e[0;37m" + when :boldred; desired_color = "\e[1;31m" + when :boldgreen; desired_color = "\e[1;32m" + when :boldyellow; desired_color = "\e[1;33m" + when :boldblue; desired_color = "\e[1;34m" + when :boldmagenta; desired_color = "\e[1;35m" + when :boldcyan; desired_color = "\e[1;36m" + when :boldwhite; desired_color = "\e[1;37m" + when :bold; desired_color = "\e[1m" + when :reset; desired_color = RESET + end + end + end + if current_color != RESET + out += RESET + end + else + message.each do |m| + if m.is_a?(String) + out += m end end end diff --git a/spec/rscons/ansi_spec.rb b/spec/rscons/ansi_spec.rb index 51d23c6..9ba7c1f 100644 --- a/spec/rscons/ansi_spec.rb +++ b/spec/rscons/ansi_spec.rb @@ -1,12 +1,79 @@ module Rscons describe Ansi do - describe ".do_ansi?" do + let(:io) {double} + describe ".write" do + context "when not doing color" do + before(:each) do + stub_const("RUBY_PLATFORM", "linux") + allow(io).to receive(:tty?).and_return(false) + end + + it "skips color attributes and just writes the String arguments" do + expect(io).to receive(:write).with("line 1\nline 2\n") + Ansi.write(io, :bold, "line ", :red, "1\nline ", :boldblue, "2", "\n") + end + end + + context "when doing color" do + before(:each) do + stub_const("RUBY_PLATFORM", "linux") + allow(io).to receive(:tty?).and_return(true) + end + + it "writes the expected ANSI escape codes" do + expect(io).to receive(:write).with("\e[1mline \e[0;31m1\e[0m\n\e[0;31mline \e[1;34m2\e[0m\n") + Ansi.write(io, :bold, "line ", :red, "1\nline ", :boldblue, "2", "\n") + end + + it "ends with a reset sequence" do + expect(io).to receive(:write).with("\e[1;33mbold yellow\e[0m") + Ansi.write(io, :boldyellow, "bold yellow") + end + + it "does not write a color sequence when it would be overwritten" do + expect(io).to receive(:write).with("\e[1;36mBOLD CYAN\e[0m\n") + Ansi.write(io, :boldyellow, :boldcyan, "BOLD CYAN\n") + end + + it "uses the correct escape sequences for all colors" do + expect(io).to receive(:write).with("\e[0;31mHI\e[0m") + Ansi.write(io, :red, "HI") + expect(io).to receive(:write).with("\e[0;32mHI\e[0m") + Ansi.write(io, :green, "HI") + expect(io).to receive(:write).with("\e[0;33mHI\e[0m") + Ansi.write(io, :yellow, "HI") + expect(io).to receive(:write).with("\e[0;34mHI\e[0m") + Ansi.write(io, :blue, "HI") + expect(io).to receive(:write).with("\e[0;35mHI\e[0m") + Ansi.write(io, :magenta, "HI") + expect(io).to receive(:write).with("\e[0;36mHI\e[0m") + Ansi.write(io, :cyan, "HI") + expect(io).to receive(:write).with("\e[0;37mHI\e[0m") + Ansi.write(io, :white, "HI") + expect(io).to receive(:write).with("\e[1;31mHI\e[0m") + Ansi.write(io, :boldred, "HI") + expect(io).to receive(:write).with("\e[1;32mHI\e[0m") + Ansi.write(io, :boldgreen, "HI") + expect(io).to receive(:write).with("\e[1;33mHI\e[0m") + Ansi.write(io, :boldyellow, "HI") + expect(io).to receive(:write).with("\e[1;34mHI\e[0m") + Ansi.write(io, :boldblue, "HI") + expect(io).to receive(:write).with("\e[1;35mHI\e[0m") + Ansi.write(io, :boldmagenta, "HI") + expect(io).to receive(:write).with("\e[1;36mHI\e[0m") + Ansi.write(io, :boldcyan, "HI") + expect(io).to receive(:write).with("\e[1;37mHI\e[0m") + Ansi.write(io, :boldwhite, "HI") + end + end + end + + describe ".do_ansi?" do context "on Windows" do it "returns true when IO is a fifo on an xterm" do stub_const("RUBY_PLATFORM", "mingw") expect(ENV).to receive(:[]).with("TERM").and_return("xterm") - io = double expect(io).to receive(:stat).and_return(Struct.new(:ftype).new("fifo")) expect(Ansi.__send__(:do_ansi?, io)).to be_truthy end @@ -14,7 +81,6 @@ module Rscons it "returns false when TERM is not set appropriately" do stub_const("RUBY_PLATFORM", "mingw") expect(ENV).to receive(:[]).with("TERM").and_return(nil) - io = double expect(Ansi.__send__(:do_ansi?, io)).to be_falsey end end @@ -22,12 +88,11 @@ module Rscons context "on POSIX" do it "returns true when IO is a TTY" do stub_const("RUBY_PLATFORM", "linux") - io = double expect(io).to receive(:tty?).and_return(true) expect(Ansi.__send__(:do_ansi?, io)).to be_truthy end end - end + end end