How To: Anatomy of the Code Highlighter Plugin

Posted by Sean Cribbs on Thursday, September 28, 2006 | |

Previously, we discussed how to create your own global tags in a plugin. In this miniature How To, I’ll discuss the process of how the Code Highlighter Plugin works.

Origins

The Code Highlighter Plugin was recently checked in to the plugins trunk by John. It comes from work originally by Tom de Grunt. Both the original and John’s new version employ Jamis Buck’s syntax gem for code highlighting.

The syntax gem, for those CS geeks out there, is a slightly enhanced lexical analyzer. It is flexible enough to generate all different kinds of marked-up code, but currently only generates HTML out of the box. It includes parsers (lexers, really) for Ruby, HTML, and YAML.

Installing

Just like any Rails plugin, we need to install it. Start by running these lines in a shell inside the root of your Radiant installation.

  # ruby script/plugin source http://dev.radiantcms.org/svn/radiant/trunk/plugins
  # ruby script/plugin install code_highlighter

Code munchin’

Now let’s take a look inside the plugin.

  # cd vendor/plugins/code_highlighter 

Open init.rb with your favorite editor and you’ll see this, which is pretty typical of Rails plugins.

  require 'code_highlighter_plugin'

Because the init.rb file is executed everytime your Rails server starts up, the file will automatically load and execute lib/code_highlighter_plugin.rb.

Go ahead and open lib/code_highlighter_plugin.rb with your favorite editor.

If you look at the top of the file, you’ll see that John was nice enough to include comments on how to use the plugin. Basically, you put your code between r:code tags, using the “lang” attribute if you need a language other than Ruby. Now let’s look at the next two lines. There’s those requires again!

  require 'behavior'
  require 'syntax/convertors/html'

These lines make sure that Behavior::Base is loaded so we can hook into it, and that the Syntax gem HTML convertor is loaded.

If you look at the next lines, you’ll see that John has taken the first method from my first How To, using Behavior::Base.define_tags to create the global tag. There’s no problem in doing it this way, just make sure this plugin is loaded before any other plugins that have custom Behaviors.

Now we come to the meat of the plugin, the tag definition. We’ll start with the first two lines.

   tag 'code' do |tag|
    lang = tag.attr['lang'] || "ruby"

So, we’ve defined the tag called code, which will appear in Radiant as r:code. Then we grab the tag’s context and put it in the variable tag.

Since we have the option to choose the language, we set the local lang variable to the value of the attribute through the tag.attr Hash. If that value is null (i.e. the attribute wasn’t set), we set the value to “ruby” (our default!).

Now let’s convert the tag’s contents. First we’ll create the convertor, then we’ll process the tag’s contents and convert them.

  convertor = Syntax::Convertors::HTML.for_syntax(lang)
  code = convertor.convert(tag.expand.to_s.strip, false)

Notice the arguments to convertor.convert in the second line. tag.expand.to_s.strip first expands the contents of the current tag, then converts it to a string (just in case), and strips whitespace from the ends. The second argument tells the convertor not to add the pre tag, we’ll do it ourselves.

  %{<pre class="code #{lang}-code"><code>#{code}</code></pre>}

If you are confused by that %{} thing, just know that’s an easy way to delimit strings in Ruby when you have double-quote marks inside the string. Also, if you’re unfamiliar with #{}, it’s an easy way to put the value of a variable inside a double-quoted string; so lang and code are inserted into the resulting string in their respective places. Since the last line of any function or block is the return value, our tag definition returns this string of HTML code. Notice how it automatically puts two classes on your pre block, code and ruby-code (if you Ruby is the contained language).

Now we close off those blocks with two end lines and we’re done. Syntax highlighting in less than 10 lines!

Show me the money

Let’s see the tag in action. Take this simple example from one of the blog posts on seancribbs.com.

Here’s how it renders (with the properly included styles, of course):

Now that I've shown you the money, show me the code! ;)