Geshi Helper for CakePHP
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
- echo $geshi->highlight($textToBeHighLighted);
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:
- //configure cross language things
- $geshi->set_header_type(GESHI_HEADER_NONE);
- $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 2);
- $geshi->enable_classes();
- $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.
- $$('div.code');.each(function(block){
- var switchButton = block.getPrevious()
- var blockText = block.get('text');
- var htmlText = block.get('html');
- block.store('plainText', blockText);
- block.store('htmlText', htmlText);
- block.store('state', 'code');
- switchButton.addEvent('click', function(e) {
- e = new Event(e).stop();
- var state = block.retrieve('state');
- if (state == 'code') {
- this.set('text', 'Show Highlighted Code');
- block.empty().set('text', block.retrieve('plainText'));
- block.setStyle('white-space', 'pre');
- block.store('state', 'text');
- } else {
- this.set('text', 'Show Plain Text');
- block.empty().set('html', block.retrieve('htmlText'));
- block.setStyle('white-space', 'normal');
- block.store('state', 'code');
- }
- });
- });
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)
- Twilight
- Classic MacOS
- Dawn
- Vibrant Ink
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.
Update You can find the newest version of my Geshii helper in my GitHub
Awesome =D. I could use these stylesheets in my source code viewer: http://61924.wepwnyou.net/p-sourcebrowz0r.html
anonymous user on 6/11/08
Thank you. This helper rocks.
anonymous user on 6/23/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!
anonymous user on 8/9/08
primeminister: No I’m using the helper, I’ve just done some extra work on it. I’ll post an updated file soon.
mark story on 8/13/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…
anonymous user on 8/25/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.
mark story on 8/31/08
Thanks for the helper, its a vast improvement on my initial hack at a helper.
I have converted your mootools plain text to code toggle to JQuery, if anyone wants it the code is here, http://wiltonsoftware.com/posts/view/geshi-code-to-plain-text-jquery-javascript-toggle.
As a side one thing I noticed with your mootools version was that under IE6-7 if you click on the toggle a number of times the plain text version disappears.
anonymous user on 12/3/08
Awesome work! I used twilight in the programming section of my blog. Had to tweak a few small things but mostly was what I wanted. keep it up :)
Jono on 12/19/08
Hi, Mark.
Thanks for the helper! (I stole your JavaSript, too.) I noticed one minor bug. At line 116 of your Sept. 12 2008 version, the first $code argument should be wrapped in trim(), as it is in the call to set_source() in the following else block.
asciimo on 1/15/09
asciimo: You’re right, I’ve fixed it on github now.
Brett Wilson: This is quite possible. My IE readership is so low I don’t really waste much time testing on it. I’ll look into that when I get a chance though. Thanks for pointing it out :)
mark story on 1/16/09
This article seems to be well designed but I am having trouble trouble getting the helper to work, it’s just spitting out whatever I throw in highlight(), nothing else.
Brian H. on 8/16/09
Brian H: Do your pre blocks have language attributes? Without those the helper doesn’t know what language the code is in, and cannot tell geshi how to highlight it.
mark story on 8/17/09
Thanks! I looked through the tests and it explained that, I think I have a bad Geshi version though. now its giving me errors in the geshi helper itself:
Fatal error: Class ‘GeSHI’ not found in /foo/bar/app/views/helpers/geshi.php on line 116
Brian H, on 8/18/09
thanks a bunch for creating this! the only thing that threw me off was mistakenly downloading geshi 1.1 rather than 1.0.8 (and the methods are quite different with 1.1 of course..!).
josh on 11/23/09
Thank you so much for the Geshi Helper!!
It took a while until I figured out that you have to include the
xemle on 2/2/10
Thank you so much for the Geshi Helper!!
It took a while until I figured out that you have to include the
block within the text to be highlighted. My favorite (corrected and escaped) example would be:echo $geshi->highlight(’<pre lang=“php”><?php echo “Geshi Helper Rocks!”; ?></pre>’);
Anyway – thanks!
xemle on 2/2/10
Thanks for the helper Mark, it does the job nicely.
One issue I ran into however (and this is not a fault of the helper) is that the helper would return null when highlighting a long string in PHP >= 5.2.
I found that the cause of this issue is a new setting introduced in 5.2 called pcre.backtrack_limit. This limits the amount of characters PHP will check when using things like non-greedy patterns (as this helper does). The default limit is only 100000 bytes (~97KB) so anything longer than this cannot be highlighted.
I fixed this by adding a constructor to the helper and raising the limit to 1MB there (though obviously this can be done in php.ini etc too).
Obviously earlier verions of PHP are not affected, the internal limit (if there was one) must have been much higher.
Hope this helps someone.
Karl on 2/11/10
Very nice, but this guide is not as clear as it could be. Step by step guides are better so people who doesn’t know that much English can understand them as well.
ram4nd on 7/25/10
Hey Mark!
Great helper. Just one thing. It generates invalid html code.
<— see?! :D
johann on 12/6/10
lol ^^
it generates
<\div class="code">
instead of</div>
johann on 12/6/10