ANSI Strings: your fun-loving, colourful pal (quick-fix colour library)
Posted by Mathew Abonyi Thu, 01 Feb 2007 08:54:08 GMT
You can make your command line interface look a little better than just a bungle of white on black (or however you or your users set your terminal). In my on-going development of a few little open source projects, all of them with CLIs, I abstracted this cute addition to the Ruby snippets junkyard and decided to air it.
With it, you can wrap any string with ANSI codes like this:
"I am going to be bold and cyan!".bold.cyan
"I am going to be underlined. YAY!".underline
"Oh, for the love of God... please STOP BLINKING!".blinkMake your command line interface glisten with choice and sparing use of ANSI codes not just with the above methods dynamically added to the String class, but also by using with_ansi(*ansi_aliases). It’s really very simple. Also, all application of ANSI is dependent on $ANSI being true, which, by default, is false (as you can at the bottom of this article, in the first line of code). Lastly, ANSI Strings checks the current platform and won’t be applied to strings on a win32 platform—I have no way of testing whether win32 can in certain instances show colour, so I just disable it.
You can download ANSI Strings from my googlecode repository like so:
svn co http://mabs29.googlecode.com/svn/trunk/other/ansi_stringsOr you can just look at it here, since it is so simple and small:
$ANSI ||= true
class String
ANSI_CODES = {
:normal => [0, "m"],
:bold => [1, "m"],
:underline => [4, "m"],
:blink => [5, "m"],
:reverse_video => [7, "m"],
:invisible => [8, "m"],
:black => [30, "m"],
:red => [31, "m"],
:green => [32, "m"],
:yellow => [33, "m"],
:blue => [34, "m"],
:magenta => [35, "m"],
:cyan => [36, "m"],
:white => [37, "m"],
:black_bg => [40, "m"],
:red_bg => [41, "m"],
:green_bg => [42, "m"],
:yellow_bg => [43, "m"],
:blue_bg => [44, "m"],
:magenta_bg => [45, "m"],
:cyan_bg => [46, "m"],
:white_bg => [47, "m"]
}
# Create shortcuts to wrap any string with ansi codes
ANSI_CODES.keys.each { |meth| define_method(meth) { with_ansi(meth) } }
# Wrap a string with an arbitrary ansi code and the ansi normal code
def with_ansi(*codes)
use_ansi? ? "#{sym_to_ansi(*codes)}#{self}#{sym_to_ansi(:normal)}" : self
end
private
def sym_to_ansi(*symbols)
symbols.inject("") do |string, symbol|
string << "\033[%s%s" % ANSI_CODES[symbol] if ANSI_CODES[symbol]; string
end
end
# determine whether we have ansi support or ANSI enabled
def use_ansi?
$ANSI && RUBY_PLATFORM !~ /win32/i
end
end
Cool idea, and a clean implementation…having to figure out ways to cleanly use the ANSI color codes has always been annoying to me. One suggestion though…couldn’t you drop the “m” in the hash value array since all ASCI display options use it by default?
I’m of two minds when it comes to the ‘m’. On the one hand, if it is just ansi colour we are talking about, yes, why not. On the other hand, if you want to broaden the scope of ANSI Strings to include the other codes, like line deletion, cursor saving/returning, and other effects, the ‘m’ will become important.
Just by adding a few hash entries, cursor functionality would be present without any change to the code. For example, you could have a StringIO that you print out initialised like this: StringIO.new(””.clear_line). Just put in: ANSI_CODES[:clear_line] = [””, “K”]. Want to clear the screen? ANSI_CODES[:clear_screen] = [2, “J”]. And so on. That’s the reasoning for having ‘m’ there, at least.
However, you could make a compelling argument to do away with the array structure altogether, make ANSI_CODES a class variable and add a class-level method to register new aliases. That would make outside customisation a breeze—and I very well may do that soon.