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!".blink

Make 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_strings

Or 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

Posted in ,  | 2 comments | no trackbacks

Comments

  1. Aaron Schaefer said 1 day later:

    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?

  2. Mathew Abonyi said 1 day later:

    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.

Trackbacks

Use the following link to trackback from your own site:
http://www.mathewabonyi.com/articles/trackback/45

Comments are disabled