mike enriquez

Mac/iOS/Web Developer

Technical stuff

How to integrate Ultraviolet into Mephisto

I couldn’t find a theme I liked for CodeRay, and I wasn’t up for creating/modifying my own. I ended up finding a different syntax highlighter called Ultraviolet. It uses TextMate’s syntax files which is pretty cool. The code in this article is highlighted using the “lazy” theme.

Integrating Ultraviolet into Mephisto (or more specifically the filtered_column_code_macro plugin) can be done in 3 steps.

Step 1: Install Ultraviolet

I won’t get into too much detail here. You can follow the directions here: http://ultraviolet.rubyforge.org/.

Step 2: Hack filtered_column_code_macro

I consider this a dirty hack. Ideally, filtered_column_code_macro would be written to give the user options to use either CodeRay or Ultraviolet. Kinda like how filtered_column supports Markdown and Textile. This works for now…

Modify code_macro.rb to look like the following:

# plugins/filtered_column_code_macro/lib/code_macro.rb

require 'uv'

class CodeMacro < FilteredColumn::Macros::Base
  DEFAULT_OPTIONS = {:wrap => :div, :line_numbers => :table, :tab_width => 2, :bold_every => 5, :hint => false, :line_number_start => 1}
  def self.filter(attributes, inner_text = '', text = '')
    # It's a whole lot easier to just set your attributes statically
    # I think for most of us the only option we're gonna change is 'lang'
    # refer to http://rd.cycnus.de/coderay/doc/classes/CodeRay/Encoders/HTML.html for more info
    # use code_highlighter.css to change highlighting
    # don't change, formats code so code_highlighter.css can be used

    lang    = attributes[:lang].blank? ? nil : attributes[:lang].to_sym
    options = DEFAULT_OPTIONS.dup
    # type of line number to print options: :inline, :table, :list, nil [default = :table]
    # you can change the line_numbers default value but you will probably have to change the css too
    options[:line_numbers]      = attributes[:line_numbers].to_sym    unless attributes[:line_numbers].blank?
    # changes tab spacing [default = 2]
    options[:tab_width]         = attributes[:tab_width].to_i         unless attributes[:tab_width].blank?
    # bolds every 'X' line number
    options[:bold_every]        = attributes[:bold_every].to_i        unless attributes[:bold_every].blank?
    # use it if you want to can be :info, :info_long, :debug just debugging info in the tags
    options[:hint]              = attributes[:hint].to_sym            unless attributes[:hint].blank?
    # start with line number
    options[:line_number_start] = attributes[:line_number_start].to_i unless attributes[:line_number_start].blank?

    inner_text = inner_text.gsub(/\A\r?\n/, '').chomp
    html = ""

    begin
      html = Uv.parse(inner_text, "xhtml", lang.to_s, false, "lazy")
    rescue
      unless lang.blank?
        RAILS_DEFAULT_LOGGER.warn "UltraViolet Error: #{$!.message}"
        RAILS_DEFAULT_LOGGER.debug $!.backtrace.join("\n")
      end
      html = "<pre><code>#{CGI.escapeHTML(inner_text)}</code></pre>"
    end
    "<div class=\"ultraviolet\">\n#{html}</div>\n"
  end
end

Two things to note here:

  1. Don’t forget to require 'uv' at the top. We won’t be needing CodeRay here anymore.
  2. Pay attention to the parameters passed to Uv.parse towards the bottom.

If you want to turn on line numbers, set the 4th parameter for Uv.parse to true. If you want to use a different theme, place it’s name in the 5th parameter. Again, a dirty hack that should be configured by the user of this library, but this isn’t an article about best practices.

Step 3: Generate and include the CSS

Fire up irb and run the following:

>> require 'uv'
>> Uv.copy_files "xhtml", "PATH_TO_GENERATE_FILES"

This will create a css directory containing all of the TextMate themes. Copy the theme you want and include it in your layout.liquid:

<!-- layout.liquid -->
{{ 'lazy' | stylesheet : 'screen, projection' }}

That’s it! Using it works the same way as before by using the <macro:code lang="ruby"> tags. A list of supported languages can be found by running puts Uv.syntaxes.join( ", " ) in irb.