Rack::Codehighlighter middleware

What?

The rack-codehighlighter gem provides a thin wrapper over a bunch of code highlighters to make their usage as generic possible:

  • ultraviolet
  • coderay
  • syntax
  • prettify
  • censor (a fake highlighter used in the example below)

How it works?

Rack::Codehighlighter looks for code blocks to be highlighted in the HTML produced by your application. For each block found it calls requested highlighter.

Installing and Usage

Install the gem with:

sudo gem install wbzyl-rack-codehighlighter -s http://gems.github.com

Using rack-codehighlighter with a Rails application

In order to use, include the following code in a Rails application config/environment.rb file:

require 'coderay'               # get one of supported highlighters 
require 'rack/codehighlighter'

Rails::Initializer.run do |config|  
  config.gem 'coderay'
  config.gem 'wbzyl-rack-codehighlighter'

  config.middleware.use Rack::Codehighlighter, :coderay, :element => "pre", :pattern => /\A:::(\w+)\s*\n/
end  

Using rack-codehighlighter with a Rack application

The rack-codehighlighter gem can be used with any Rack application, for example with a Sinatra application. If your application includes a rackup file or uses Rack::Builder to construct the application pipeline, simply require and use as follows:

gem 'coderay'       # get one of supported highlighters 
require 'coderay'   

gem 'wbzyl-rack-codehighlighter'
require 'rack/codehighlighter'

use Rack::Codehighlighter, :coderay, :element => "pre", :pattern => /\A:::(\w+)\s*\n/
run app

Rack::Codehighlighter by an example

Below we ask Rack::Codehighlighter to look for code blocks into all pre *element*s:

use Rack::Codehighlighter, :coderay, :element => "pre", :pattern => /\A:::(\w+)\s*\n/

and use the /\A:::(\w+)\s*\n/ pattern to learn what language the code block contains.

For example:

<pre>:::ruby
puts "hello world"
</pre>

Informal description of the pattern: if the element contents begins with three colons, word characters following these colons up to optional spaces followed by end of line, name the language.

Language names are taken from the ultraviolet gem, see below for the list.

The matched element is removed, and the code block is passed to coderay highlighter for processing.

The highlighted code returned by the coderay highlighter is wrapped with pre element with attributes appropriate for the codehighlighter used.

More examples

In examples displayed below, the default value of each option is used.

Coderay:

use Rack::Codehighlighter, :coderay,
  :element => "pre", :pattern => /\A:::(\w+)\s*\n/, :logging => false

Ultraviolet:

use Rack::Codehighlighter, :ultraviolet, :theme => "dawn", :lines => false,
  :element => "pre", :pattern => /\A:::(\w+)\s*\n/, :logging => false

Prettify:

use Rack::Codehighlighter, :prettify,
  :element => "pre", :pattern => /\A:::(\w+)\s*\n/, :logging => false

Syntax:

use Rack::Codehighlighter, :syntax,
  :element => "pre", :pattern => /\A:::(\w+)\s*\n/, :logging => false

Censor:

use Rack::Codehighlighter, :censor, :reason => "[[--  ugly code removed  --]]",
  :element => "pre", :pattern => /\A:::(\w+)\s*\n/, :logging => false

In Markdown, Maruku and RDiscount templates, after converting to HTML, code is wrapped with pre>code. To remove this extra one level of nesting the :markdown option should be used:

use Rack::Codehighlighter, :coderay, :markdown => true,
  :element => "pre>code", :pattern => /\A:::(\w+)\s*\n/, :logging => false

Check the examples directory for working examples.

Try it!

Simple Copy & Paste example. Implemented in Sinatra and uses inline template.

# example.rb

require 'rubygems'
gem 'sinatra', '>=0.9.0' 
require 'sinatra'
gem 'wbzyl-rack-codehighlighter', '>=0.2.0'
require 'rack/codehighlighter'

use Rack::Codehighlighter, :censor, :reason => '[[--difficult code removed--]]'

get "/" do
  erb :hello
end

__END__

Run the example with:

ruby example.rb

The results are accessible from http://localhost:4567.

Supported highlighters

These currently include: Syntax (fast), Coderay (very fast), Ultraviolet (slow, but highlights almost any language).

Syntax

Languages supported by Syntax:

  • xml
  • ruby

Coderay

Languages supported by Coderay:

  • C, CSS
  • Delphi, diff
  • HTML, RHTML (Rails), Nitro-XHTML
  • Java, JavaScript, JSON
  • Ruby
  • YAML

Google Code Prettify, pure Javascript

Languages supported by Prettify:

  • css, lisp, hs, lua, sql, vb, wiki,
  • bsh, c, cc, cpp, cs, csh, cyc, cv, htm, html,
  • java, js, m, mxml, perl, pl, pm, py, rb, sh,
  • xhtml, xml, xsl

Ultraviolet

The ultraviolet gem needs oniguruma regexp library.

On Fedora install the library with:

sudo yum install oniguruma oniguruma-devel

For installation instruction of the oniguruma library from sources, see Carbonica

Now, install the gem:

sudo gem install ultraviolet

See also Ultraviolet themes gallery

Ultraviolet supports almost any language:

  • actionscript, active4d, active4d_html, active4d_ini, active4d_library, ada, antlr, apache, applescript, asp, asp_vb.net
  • bibtex, blog_html, blog_markdown, blog_text, blog_textile, build, bulletin_board
  • c, c++, cake, camlp4, cm, coldusion, context_free, cs, css, css_experimental, csv
  • d, diff, dokuwiki, dot, doxygen, dylan
  • eiffel, erlang, f-script, fortran, fxscript
  • greasemonkey, gri, groovy, gtd, gtdalt
  • haml, haskell, html, html-asp, html_django, html_for_asp.net, html_mason, html_rails, html_tcl
  • icalendar, inform, ini, installer_distribution_script, io
  • java, javaproperties, javascript, javascript_+_prototype, javascript_+_prototype_bracketed, jquery_javascript, json
  • languagedefinition, latex, latex_beamer, latex_log, latex_memoir, lexflex, lighttpd, lilypond, lisp, literate_haskell, logo, logtalk, lua
  • m, macports_portfile, mail, makefile, man, markdown, mediawiki, mel, mips, mod_perl, modula-3, moinmoin, mootools, movable_type, multimarkdown
  • objective-c, objective-c++, ocaml, ocamllex, ocamlyacc, opengl
  • pascal, perl, php, plain_text, pmwiki, postscript, processing, prolog, property_list, python, python_django
  • qmake_project, qt_c++, quake3_config
  • r, r_console, ragel, rd_r_documentation, regexp, regular_expressions_oniguruma, regular_expressions_python, release_notes remind, restructuredtext, rez, ruby, ruby_experimental, ruby_on_rails
  • s5, scheme, scilab, setext, shell-unix-generic, slate, smarty, sql, sql_rails, ssh-config, standard_ml, strings_file, subversion_commit_message, sweave, swig
  • tcl, template_toolkit, tex, tex_math, textile, tsv, twiki, txt2tags
  • vectorscript
  • xhtml_1.0, xml, xml_strict, xsl
  • yaml, yui_javascript

Why? TODO

Currently, almost everything what I write is rendered by Ruby on Rails and Sinatra applications. To improve the readability of the text, I want the code fragments to be colored. So extending Rails and Sinatra frameworks with syntax highlighting is a must.

The problem is to how syntax highlighting features are hooked into these frameworks.

Look at the current practice. To this end, analyze the top three tools listed on The Ruby Toolbox in category Syntax Highlighting.

tm_syntax_highlighting (plugin):

code(some_ruby_code, :theme => "twilight", :lang => "ruby", :line_numbers => true)

harsh (plugin):

<% harsh :theme => :dawn do %>    |    <% harsh %Q{ some_ruby_code }, :theme => :dawn %>
  some_ruby_code                  |
<% end %>                         |

Redcloth with CodeRay (gem):

<source:ruby> some_ruby_code </source> 

Overdone: highlighting engine/library/framework. Different solutions for each one framework are needed. Different output: include.

With Rack based application we can simplifiy thingst by adding to the application pipeline..

Ruby tips from me, your idol: I can not tell you how much time I’ve wasted trying to add in some cool feature into rails. I would dig into the rails internals, override methods, do all kinds of tricky stuff. I thought I was awesome. A month later rails comes out with some cool new feature, I update rails and everything explodes.

Conclusion: highlighting via plugins is doomed to explode sooner or later.

Rack::Codehighlighter provides a thin wrapper over a bunch of code highlighters to make their usage as generic possible.

Uniform/define own..

In each piece of code inserted into html we must change: < to &lt;. This is annoying thing. Each(? prettify, dp-) pure javascript highlighter has this defect.

In pre-Rack applications era possible approaches were:

  • gems; connection to methods responsible for code highlighting is obtrusive, i.e. via plugin + additional markup

Analyze packages mentioned at the The Ruby Toolbox page: Syntax Highlighting