Geshi Helper for CakePHP

Apr 05 2008

GeSHi or the Generic Syntax Highlighter is a simple yet powerful syntax highlighter for many languages. Implemented in many CMS. When I wanted syntax highlighting for my postings I decided to implement GeSHi. I wanted to share my implementation of GeSHi as a CakePHP helper. So here it is GeshiHelper . Included in the package is the helper, my config file and a unit test. You will need my config file for the unit test to pass.

I originally based my helper of off Gingus’ GeshiHelper however, I felt uncomfortable with his implementation. Using DOMDocument to parse HTML fragments required hacky string manipulation to get the highlighted code to validate. So I’ve gutted his helper entirely and replaced the DOMDocument and related functions with what I believe to be a more elegant Regular Expression.

Using GeSHi Helper

Before using the GeshiHelper you will need to obtain a copy of GeSHi and place it in one of your vendors directorys. Using GeshiHelper is quite straight forward. First include it in your $helpers array for the controllers you wish to have highlighting in. In your views simply

Show Plain Text
  1. echo $geshi->highlight($textToBeHighLighted);
  2.  

Depending on how you configure your GeshiHelper it will grab all the block tags and as long as they have a valid language attribute of your choice they will be highlighted. There is also a default language option. If you only want to display one language or want to have a catch all language this is the ticket.

While this works great in many cases, I wanted a bit more control in my syntax highlighting. The helper will also check to see if you have a app/config/geshi.php and use it to modify the internal highlighter instance. This allows you to control how your Geshi Helper behaves. For instance mine looks like:

Show Plain Text
  1. //configure cross language things
  2. $geshi->set_header_type(GESHI_HEADER_NONE);
  3. $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 2);
  4. $geshi->enable_classes();
  5. $geshi->set_tab_width(4);

As you can see you have access to all of GeSHi’s methods in your config file and are able to manipulate it in any way you would be able to modify a standalone GeSHi instance. You can read more about all the GeSHi methods. I’ve chosen to use classes and enable fancy line numbers. Creating much cleaner and compact code output. However this output is unstyled, and not very pretty at first.

Validation mode

The geshi helper by default will swap any <pre> blocks that have highlighted with <div class="code"> in addition to any other attributes that may already exist on the pre tags. This helps keep your html files valid. It can be disabled by setting $geshi->containerMap = array() before highlighting text.

Showing plain code

By default the GeshiHelper will create a link above highlighted code that looks a bit like <a href="#null" class="geshi-plain-text">Show Plain Text</a>. This link can be used with javascript to enable plain text swapping of the highlighted code, as hightlighted code is difficult to copy and paste. This behavior can be disabled by setting $geshi->showPlainTextButton = false; before highlighting code.

Sample javascript for plain text switching.

This site is using mootools as a javascript library but this code should easily port to the javascript library of your choice.

Show Plain Text
  1. $$('div.code');.each(function(block){
  2.     var switchButton = block.getPrevious()
  3.     var blockText = block.get('text');
  4.     var htmlText = block.get('html');
  5.     block.store('plainText', blockText);
  6.     block.store('htmlText', htmlText);
  7.     block.store('state', 'code');
  8.  
  9.     switchButton.addEvent('click', function(e) {
  10.         e = new Event(e).stop();
  11.         var state = block.retrieve('state');                   
  12.         if (state == 'code') {
  13.             this.set('text', 'Show Highlighted Code');
  14.             block.empty().set('text', block.retrieve('plainText'));
  15.  
  16.             block.setStyle('white-space', 'pre');                          
  17.             block.store('state', 'text');
  18.         } else {
  19.             this.set('text', 'Show Plain Text');
  20.             block.empty().set('html', block.retrieve('htmlText'));
  21.             block.setStyle('white-space', 'normal');
  22.             block.store('state', 'code');
  23.         }
  24.     });
  25. });

Styling GeSHi output

Being a standards purist I wanted all the styling to be in external CSS. Now the GeSHi documentation didn’t really specify what classes would be used and where they would be applied but after a short while of poking at the highlighted code in firebug I came up with something I was happy with.

Bonus Style Sheets!

Overambitious as always I’ve gone and created GeSHI CSS files for a number of Textmate themes. So if you love Textmate, or are a fan of pretty highlighted syntax check these out. I’ve tried to reproduce the look of the themes in as many languages as I had time for. (PHP, Python, Javascript, CSS)

Download the full CSS pack

The above CSS files and GeshiHelper are licensed under the MIT license so feel free to use it in commerical or non-commercial projects.

If there are others you’d like to see done let me know, I’ll see what I can do.


Comments

Frank on 11/6/08

Awesome =D. I could use these stylesheets in my source code viewer: http://61924.wepwnyou.net/p-sourcebrowz0r.html

Adam on 23/6/08

Thank you. This helper rocks.

primeminister on 9/8/08

He Mark, Nice helper! But you use something else? Because of the neat links ‘Show Plain text’? Or is it you own implementation?
thnx again!

mark story on 13/8/08

primeminister: No I’m using the helper, I’ve just done some extra work on it. I’ll post an updated file soon.

kuba on 25/8/08

Great helper but maybe using a behavior would be a better approach? You can pass all config data during behavior init (it will enable more settings for different models)

Then, if the code was found in the record you can automatically include specific stylesheet/js in the page header…

mark story on 31/8/08

kuba: I disagree with this being a behavior. Behaviors should be handling reusable blocks of model logic code. I don’t think that making code samples in HTML output really falls into that domain. However, I like the idea of being able to set a config array and reuse it.


Have Something to say?

*
* (Never published, I promise)
* You can use Textile markup, but be reasonable

Recent Artwork

  • CakePHP Test suite icons
  • Shuriken 2
  • Clumsy Penguins
  • Balloon Animals
  • Metal in the air! (vertical)
  • Cleavers