WordPress csh GeSHi Plugin
From ByteWiki
The WordPress csh_geshi plugin was written by Clinton so that we could have the same code syntax highlighting features that we were also enjoying on [1] and here on our byteclub wiki. Both this WordPress plugin project and the mediaWiki extension project use the fantastic GeSHI engine - it does all the real work and it's awesome. The GeSHi project is by Nigel McNie, and released under a GNU GPL.
You are free to use this code as you wish. I hope others find it useful. If you have bugs/suggestion you can contact me cwoodward@byteclub.net and let me know.
Contents |
The Plugin Code
This WordPress plugin is a single, fairly simple, php file. WordPress plugins use some special header comments to identify themselves to a wordpress installation. If all is done right, the header details automagically appear in the wordpress admin panel, making "activation" and "deactivation" of plugins easy (nice!). You can see the code details below... :)
I save all this in a file called csh_geshi.php (Code Syntax Highlighting using GeSHi... if that was totally obvious.) For those that have looked at the mediawiki extension you will surely realise this looks very similar... of course it is. Cut-n-paste code-reuse at its best! (eish... :)
<?php /* Plugin Name: csh_geshi Plugin URI: http://www.byteclub.net/wiki/wordpress_csh_geshi_plugin Description: Code Syntax Highlighting using the GeSHi engine Version: 2.0 Author: Clinton Woodward Author URI: http://www.byteclub.net/blog/cwoodward/ */ // i'm writing "filter hooks" to change text-content. // see http://codex.wordpress.org/Writing_a_Plugin#Plugin_File_Structure add_filter('the_content','csh_geshi_the_content',1); add_action('wp_head','csh_geshi_head_style'); function csh_geshi_the_content($content) { // find each of the <code>[] blocks and do something... $result = preg_replace_callback('!<code>(.*?)</code>!ims','csh_geshi_snippet',$content); return $result; } function csh_geshi_head_style($unused) { echo '<style type="text/css"> #content pre { border: 1px dashed #2f6fab; padding: 0.5em; background-color: #fafafa; overflow:auto; } #content pre ol { margin: 0; padding: 0; margin-left: 2em; } #content pre ol li { margin: 0; padding: 0; height: 1em; } /* used for testing... #content pre ol li div { background-color: blue; line-height: 1em; border: 0; padding: 0; }*/ </style>'; } $csh_geshi_languages = array ( 'actionscript','ada','apache','asm','c','cpp','eiffel','ini','html','xhtml', 'java','css','js','javascript','vbnet','csharp','pascal','xml','php', 'delphi','bash','perl','lisp','matlab','mpasm','objc','vb','smarty','vhdl', 'ruby','sql','python','pseudocode','qbasic','scheme','oracle8','dos'); // See: http://qbnz.com/highlighter/geshi-doc.html define('CSH_GESHI_PATH','our/path/to/the/geshi/files'); // we use geshi for more than one project! include_once(CSH_GESHI_PATH.'geshi.php'); # The callback function for converting the input code text to pretty HTML output # <code>[lang,line_no,start_at,range()]...</code> # lang = req. line_no=Y/N (default N), start_at=1(default) # special lang='langlist' list all the available languages function csh_geshi_snippet($source) { //error_reporting(E_ALL); global $csh_geshi_languages; // $source from preg_replace_callback() is an array each matched piece, // [0] is the complete match, [1] is the first subpattern match $source = $source[1]; // extract the required args [...] $lines = explode("\n",trim($source)); $args = trim($lines[0]); // defaults $lang = 'csharp'; $line_no = 'N'; $line_start = 1; $line_range = ''; // Is there a [..] section? if (strpos($args,'[')===0 && strpos($args,']')!==false) { // extract the args values, trim off the [] characters. $eofargspos = strpos($args, ']'); $args = explode(',', substr($args, 1, $eofargspos - 1)); // Language? Is it one of the one we have enabled? if (in_array($args[0],$csh_geshi_languages)) { $lang = $args[0]; } // Is this the special "list the languages" command? elseif ($args[0] == 'langlist') { sort($csh_geshi_languages); // sort so its easy to read return 'Current enabled languages: <code>'.implode(', ',$csh_geshi_languages).'.</code>'; } else { $lang = ''; } // Show line numbers/start line no? if (isset($args[1])) { if(is_numeric($args[1])) { $line_no = 'Y'; // implied by the presence of a start line number $line_start = intval($args[1]); if(isset($args[2])) { // give the whole arg string $line_range = csh_get_geshi_mark_range(trim($lines[0])); } } else { $line_no = (strtoupper($args[1]) == 'Y') ? 'Y' : 'N'; // start line given? May be irrelevant if 'N' set :) if(isset($args[2]) && is_numeric($args[2])) { $line_start = intval($args[2]); } } } // Get rid of the now used first bit of [...] info, implode and trim $lines[0] = trim(substr($lines[0], $eofargspos + 1, strlen($lines[0]))); $source = trim(implode("\n",$lines)); // Remap any languages? if ($lang == 'pascal') { $lang = 'delphi'; } // looks better if ($lang == 'js') { $lang = 'javascript'; } // shortcut if ($lang == 'html') { $lang = 'html4strict'; } // shortcut if ($lang == 'xhtml') { $lang = 'html4strict'; } // remap // Create the GeSHi parser object, tell it what it needs... $geshi = new GeSHi($source, $lang, CSH_GESHI_PATH.'/geshi'); // turn line numbers on? if ($line_no == 'Y') { // add to remove the extra line height we didn't like to see in mediawiki $norm = 'border: 0px solid green; margin: -1px; padding: 0;'; $geshi->set_line_style($norm); $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS,0); if(is_array($line_range)) { $mark = 'background-color:#FFFFF0; color: red; font-weight: bold;'; $geshi->set_highlight_lines_extra_style($mark); $geshi->highlight_lines_extra($line_range); } } // start line numbering at ... $geshi->start_line_numbers_at($line_start); // parse the output, hand it back. $output = $geshi->parse_code(); return $output; } else { // If it's not for us, catch and return with the simple use of <code></code> tags return '<code>'.htmlentities($source).'</code>'; } } // end csh_geshi_snippet() function csh_get_geshi_mark_range($str) { // get the (...) substring as an array of int values $start = strpos($str,'(')+1; $end = strpos($str,')'); $str = substr($str,$start,$end-$start); // break into parts $tmp = explode(',',$str); foreach($tmp as $key=>$val) { // expand the 4-7 type ranges and replace in $tmp if(strpos($val,'-')) { list($start,$end) = explode('-',$val); for($i=$start; $i<=$end; $i++) $tmp[] = $i; unset($tmp[$key]); } } return $tmp; } ?>
Installation
You will need a working install of WordPress and the GeSHi software.
- Download and install the GeSHi software.
- Download and save the above script as csh_geshi.php (or similar) into your wordpress plugin directory.
- Update the defined CSH_GESHI_PATH information to match your GeSHi location. Be careful about the path to the all important geshi.php file and the actual geshi/ folder location where all your languages are kept. If geshi can't find the languages you as for, it fails silently (quite sensible really) but leaves you wondering what the heck has happened. I considered adding a file check, but decided I couldn't be bothered and it's a bit of a waste - just be aware of this issue and make sure you set it up right. :)
- Update the languages you want to use. I have used a "whitelist" mentality on the assumption this languages are added to the "ok" list on demand, thus limiting risk of availability to languages we don't use. If you don't want this - just remove the $csh_geshi_languages array and remove the code that uses it to check if the language is allowed.
- Login to your wordpress "site admin" area, go to the "Plugins" tab. You should see your newly available plugin listed and ready for "activation". If all goes well, it should be fine, and you can start using the plugin features in your post.
- Special Note for ByteClub bloggers - the plugin code is available for all, but each user must still activate the plugin for their particular settings (updates the database values).
Usage
The use is very simialar to the wiki extension syntax. Start your section of code with <code>[lang] ... </code> and you're done.
From the code comments you can see
# The callback function for converting the input code text to pretty HTML output # <code>[lang,line_no,start_at,range()]...</code> # lang = req. line_no=Y/N (default N), start_at=1(default) # special lang='langlist' list all the available languages
The examples for using the Wiki extension may be helpful.
Note:
- only the lang is required. It must be one of the configured languges.
- The default for line numbers of "N". (This is the opposite of the mediawiki extension).
WordPress Editor Issues
Unfortunately, the fancy builtin wordpress editor doesn't know about our lovely plugin syntax, and I haven't looked at what is required to make the editor okay with it yet. So, if you want to make a post with syntax-highlighted code, you will need to turn off the edit (for that one post at least). Let me know if you have a nice fix for this. :)
