Using Pygments in Redmine

The syntax highlighting engine in Redmine is CodeRay which is vastly underpowered to be used in software as useful as Redmine. The obvious choice (at least for me) for proper syntax highlighting is Pygments, a python library. To change the syntax highlighting engine a few modifications need to be made to both Redmine and to Pygments.

The method that is called for syntax highlighting is in the app/helpers/repositories_helper.rb file.

def syntax_highlight(name, content)
  type = CodeRay::FileType[name]
  type ? CodeRay.scan(content, type).html : h(content)
end

(Note: This is for version 0.7-stable as the location of this method has moved in the trunk of the repository to app/helpers/application_helper.rb)

I initially piped the contents to Pygments with the -g argument which tries to guess the language but the success rate was too low. While it can choose a lexer based on a filename it also attempts to read the code from the file which doesn’t physically exist on our filesystem. The only solution was to add the desired functionality to Pygments via a new command-line argument. The ticket along with patch can be viewed here. Hopefully the patch will be accepted and patching will no longer be necessary (see update at end of post). The new argument, -N, allows you to pass a filename to Pygments which will return the lexer which best represents the contents based solely on the filename.

After patching Pygments we need to call it in the syntax highlighting method instead of CodeRay.

  def syntax_highlight(name, content)
    f = IO.popen("pygmentize -N " + name, 'r')
    lexer = f.read[0...-1] # removes trailing newline
 
    f = IO.popen("pygmentize -f html -l " + lexer, 'w+')
    f.write(content)
    f.close_write
    return f.read[28...-13]# removes surrounding div and pre as well as trailing newline
  end

As this was my first endeavor with Ruby I doubt it’s as elegant as it could be but it gets the job done. For this to work as-is it is assumed that pygmentize is in your PATH variable. Since Pygments uses its own CSS styles we must generate them, embed them in the stylesheet, and change an HTML tag so the new CSS rules are being applied. To generate the CSS use the following command:

[redmine-0.7]$ pygmentize -f html -S colorful -a .Pygments >> public/stylesheets/scm.css

This will append the new rules onto the already existing ones in the scm.css file. We still need to add an additional custom rule though so open scm.css in your favorite text editor and append the following:

.Pygments pre { margin: 0px; }

Now all that needs to be done is editing the templates to change the table class from “CodeRay” to “Pygments” so our new CSS rules are used. Open app/views/repositories/entry.rhtml and change line 4 and app/views/repositories/diff.rhtml and change line 18 and 48 to read:

<table class="filecontent Pygments">

Also open app/views/repositories/annotate.rhtml and change line 6 to read:

<table class="filecontent annotate Pygments">

You should now be able to enjoy the Pygments highlighting engine in your Redmine repositories!

Update:
My patch to Pygments has been accepted and checked into the repository. The changeset can be viewed here. The 1.0 version, the first to include this feature, is slated to be released on December 1st, 2008.

Written on: 11-12-08 · 5 Comments » · Permalink

5 Responses to “Using Pygments in Redmine”

  1. blogsportgruppe* » Blog Archiv » Redmine and Python wrote:

    [...] lucky I found the blog post of Jake Wharton describing how to switch the syntax highlighting engine to Pygments in an older version of Redmine, [...]

    January 11th, 2009 at 1:52 pm
  2. Sean wrote:

    Excellent post! I really like redmine but I was disappointed with its syntax highlighting. Pygments has much broader file type support. One suggestion though: trying to get the lexer from the file extension doesn’t work for executable scripts (shell scripts mostly), I made a quick mod so if there’s no extension it lets pygmentize guess from stdin:

    [code]
    def syntax_highlight(name, content)
    # Where's pygment live?
    pygmentize = ""

    # If we have a file extension, we can force a lexer
    if File.extname(name) != ""
    f = IO.popen(pygmentize + " -N " + name, 'r')
    lexer = f.read[0...-1] # Removes trailing newline

    f = IO.popen(pygmentize + " -f html -l " + lexer, 'w+')
    f.write(content)
    f.close_write
    else
    # otherwise we just have to let Pygments guess
    f = IO.popen(pygmentize + " -g -f html", 'w+')
    f.write(content)
    f.close_write
    end

    # Remove surrounding div and pre and newline and return
    return f.read[28...-13]
    end
    [/code]

    September 7th, 2009 at 3:55 pm
  3. Using Pygments in Redmine « 乌拉山羊 wrote:

    [...] http://jakewharton.com/2008/11/12/using-pygments-in-redmine/ [...]

    September 10th, 2009 at 1:03 am
  4. Приколист wrote:

    спасибо за статью… добавил в ридер

    April 5th, 2010 at 10:00 pm
  5. TranceParty wrote:

    Не спорю, для новичков статья

    April 27th, 2010 at 5:58 pm

Leave a Reply