====== Code Syntax PlugIn ====== ---- plugin ---- description: Syntax highlighting with optional line numbers author : Matthias Watermann email : support@mwat.de type : render, syntax lastupdate : 2008-07-22 compatible : Hrun depends : conflicts : similar : code tags : code, syntaxhighlight, odt downloadurl: http://dev.mwat.de/dw/syntax_plugin_code.zip ---- While writing several pages to document some source code I noticed that it would be helpful if there were //line numbers// added automatically when using the ''%%%%'' tag to highlight the respective code fragments. Investigating the underlying //[[http://qbnz.com/highlighter/|GeSHi]]// system((which is used by DokuWiki internally for syntax highlighting)) showed that its line numbering feature was not available for use in DokuWiki((And for a good reason, I'd say: It produces a bunch of markup that does not even validate.)). --- Hence I wrote this plugin, which //replaces// and enhances [[wiki:syntax|DokuWiki's builtin]] "''%%%%''" feature. While I was at it I integrated the algorithms of my older [[diff|Diff plugin]] as well((that one as well was written initially to solve problems with the in some respects rather limited //GeSHi//)) -- this way obsoleting the latter((although both plugins will work side by side without disturbing each other)). --- And after some enlightening((by private mail, directing me to think about CSS styling ...)) I implemented the option to specify an optional //header/footer// for a code section. --- Some time later the [[#Console]] mode, the inclusion of [[#External Files|external]] resources and [[#Open Document Format|ODT]] support followed. So the abstract markup looks like this: {code to highlight} * "''lang''" (if present) specifies the given code's (programming-) language; * "''123''" (if present) specifies the first number to use when numbering the given code's lines; * the first "''%%|%%''" (if present) delimits the first argument(s) from the remaining ones which are used to produce a //header// or //footer// above/below the highlighted code; * a "''h''" (for //"header"//)((or "''t''" for //"top"//)) following //immediately// the pipe character causes the remaining "''text''" to get placed //above// the code, a "''f''" (for //"footer"//)((or "''b''" for //"bottom"//)) places the "''text''" below the code (note that this flag is //optional//; if omitted it defaults to "''f''"); * the "''text''" is used as-is with the additional benefit of getting wrapped by an anchor tag which allows for addressing the code from other pages. * the second "''%%|%%''" (if present) delimits the header/footer text; * a "''s''"((for //"show"//, the default)) or "''h''"((for //"hide"//)) character (if present) controls the code block's initial folding state; for more details see the [[#folding_state|Behaviour]] section below. * the "''{code to highlight}''" is either the plain text to process or a pointer to an [[#external_files|external source]]. ===== Compatibility ===== This plugin is compatible up to DokuWiki version 2014-09-29d "Hrun". :!: Installing it on newer versions will break your DokuWiki installation. This means you will see a blank page after installation. To get your wiki running again you will have to manually delete the folder **lib/plugins/code2**. :!: ===== Usage ===== Backward compatible with DokuWiki's builtin "''%%%%''" [[#Notes|markup]] this plugin provides additional (optional) arguments which can be used to both turning ON the new line numbering feature and specify the first number to use actually when numbering the lines. As for "''diff''" the optional second argument can be used to give a hint about the diff (//unified//, //context//, //RCS// or //simple//) format. To use syntax highlighting you'll use the "''%%%%''" tag as you're used to do. Without giving a new argument the output will be just the same as with DokuWiki's built-in((After all, the //GeSHi// engine is used very much the same way.)). * ''%%%%\\ some text\\ %%%%'' __//(basic)//__\\ just marks up "''some text''" as preformatted text (w/o any special highlighting). * ''%%%%\\ some text\\ %%%%'' __//(extended)//__\\ marks up "''some text''" as preformatted text (w/o any special highlighting) with line numbers starting at number ''7''. * ''%%%%\\ some text\\ %%%%'' __//(extended)//__\\ marks up "''some text''" as preformatted text (w/o any special highlighting) with a footer line of "''A description''". * ''%%%%\\ some text\\ %%%%'' __//(basic)//__\\ turns on HTML highlighting for "''some text''". * ''%%%%\\ some text\\ %%%%'' __//(extended)//__\\ turns on HTML highlighting for "''some text''" with added line numbers starting with number ''1''. * ''%%%%\\ some text\\ %%%%'' __//(extended)//__\\ turns on HTML highlighting for "''some text''" and a header line of "''A description:''". * ''%%%%\\ some text\\ %%%%'' __//(extended)//__\\ turns on HTML highlighting for "''some text''" with added line numbers starting with number ''66'' and a footer line of "''A description''". * ''%%%%\\ some text\\ %%%%'' __//(extended)//__\\ turns on ''diff'' highlighting (autodetecting the patch format). * ''%%%%\\ some text\\ %%%%'' __//(extended)//__\\ turns on ''diff'' highlighting (assuming an ''unified'' patch format). * ''%%%%\\ some text\\ %%%%'' __//(extended)//__\\ turns on ''diff'' highlighting (assuming a ''context'' patch format) and a footer line of "''A description''". * ''%%%%\\ some console commands capture\\ %%%%'' __//(extended)//__\\ turns on [[#Console|console]] mode with a footer line of "''The shell output''". * ''%%%%\\ extern> %%http://some.where.else.tld/page.html%%\\ %%%%'' __//(extended)//__\\ turns on HTML highlighting for the [[#External Files|external]] "''%%http://some.where.else.tld/page.html%%''" page's source with added line numbers starting with number ''1'' and a header line of "''The example page:''". The entries marked __//(basic)//__ are available with DokuWiki's builtin ''%%%%'' markup as well, while those marked __//(extended)//__ are features added by this plugin. So if you neither need nor want any of the latter you won't have to install this plugin but instead get yourself a cup of tea and watch the sunset. And just for the records: The optional line number argument specifies the first //number// to use when numbering the lines but //not// the first code line to show. ==== Examples ==== Please note that you will //not// see any highlighting //here//: This section just shows some use cases without actually triggering the activation of the plugin -- even if it was installed ... some text and more This just renders the given preformatted text without any special highlighting. However, some text and more will add line numbers in front of each line starting (in this case) with ''1'' (one). var de = function() { return (typeof(window.de) == 'object') ? window.de : {}; }(); This markup will turn on both the JavaScript syntax highlighting and the line numbering starting in this case with number ''12'' and place the text "''Listing 2''" below the code block -- producing HTML like:
  12:  var de = function() {
  13:      return (typeof(window.de) == 'object') ? window.de : {};
  14:  }();
  

Listing 2

If you'd rather the text //above// the code you'd just insert a "''h''" right after the pipe character i.e. "''%%%%''". Of course, instead of ''JavaScript'' you may use any other highlighting mode supported by //GeSHi// like "''html''" for example or "''php''". In case a language is given which is //not// supported by //GeSHi// there won't be any syntax highlighting, of course, but the line numbers (if requested) and header/footer lines (if requested) will appear nevertheless. As mentioned above besides the line numbering feature this plugin provides an improved highlighting mode for "''diff''" files (aka "''patches''"). For "''diff''" the second argument can be either __''u''__ (for "//unified//"), __''c''__ (for "//context//"), __''n''__ (or __''r''__ for //RCS//) or __''s''__ (for "//simple//") format((Meaning, that line numbering is //not// available for "''diff''". It wouldn't make any sense anyway. Just think about it.)). Although appreciated this argument is optional as well. Omitting it will cause the plugin to perform some additional tests to figure out the "''diff''" format actually used. For more information see the [[diff#examples|Examples]] section of the [[diff|Diff]] plugin's doc. To summarize: * Without the new arguments nothing changes from what you're used to know (and get) -- but see the [[#Notes]] section below. * Giving a numeric argument //only// results in preformatted text without highlighting but line numbering turned on. * Giving just a language argument turns on syntax highlighting w/o line numbers. * Giving a language //and// a numeric argument turns on syntax highlighting //and// line numbering starting with the given number. * Adding a "''|''", optionally followed by "''h''" or "''f''" and some text will result in a header or footer line attached to the code block (see [[#Behaviour]] below). ===== Installation ===== Search and install the plugin using the [[plugin:extension|Extension Manager]]. Alternatively, refer to [[:Plugins]] on how to install plugins manually. It's quite easy to integrate this plugin with your DokuWiki: - Download the [[http://dev.mwat.de/dw/syntax_plugin_code.zip|source archive]] (~25KB) and unpack it in your DokuWiki plugin directory ''{dokuwiki}/lib/plugins'' (make sure, included subdirectories are unpacked correctly); this will create the directory ''{dokuwiki}/lib/plugins/code''. - Make sure both the new directory and the files therein are readable by the web-server e.g. $> chown apache:apache dokuwiki/lib/plugins/* -Rc ===== Plugin Source ===== Here comes the [[http://www.gnu.org/licenses/gpl.html|GPLed]] PHP source((The comments within the [[#Plugin Source|source]] file are suitable for the OSS [[http://www.stack.nl/~dimitri/doxygen/index.html|doxygen]] tool, a documentation system for C++, C, Java, Objective-C, Python, IDL and to some extent PHP, C#, and D. --- Since I'm working with different programming languages it's a great ease to have one tool that handles the docs for all of them.)) for those who'd like to scan before actually installing it: syntax_plugin_code.php - A PHP4 class that implements the * DokuWiki plugin for highlighting code fragments. * *

* Usage:
* <code [language startno |[fh] text |[hs]]>...</code> *

 *  Copyright (C) 2006, 2008  M.Watermann, D-10247 Berlin, FRG
 *      All rights reserved
 *    EMail : <support@mwat.de>
 * 
* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either * version 3 of the * License, or (at your option) any later version.
* This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. *
* @author Matthias Watermann * @version $Id: syntax_plugin_code.php,v 1.29 2008/07/22 09:22:47 matthias Exp $ * @since created 24-Dec-2006 */ class syntax_plugin_code extends DokuWiki_Syntax_Plugin { /** * @privatesection */ //@{ /** * Additional markup used with older DokuWiki installations. * * @private * @see _fixJS() */ var $_JSmarkup = FALSE; /** * Indention "text" used by _addLines(). * *

* Note that we're using raw UTF-8 NonBreakable Spaces here. *

* @private * @see _addLines() */ var $_lead = array('', ' ', '  ', '   ', '    ', '     ', '      ', '       '); /** * Section counter for ODT export * * @private * @see render() * @since created 08-Jun-2008 */ var $_odtSect = 0; /** * Prepare the markup to render the DIFF text. * * @param $aText String The DIFF text to markup. * @param $aFormat String The DIFF format used ("u", "c", "n|r", "s"). * @param $aDoc String Reference to the current renderer's * doc property. * @return Boolean TRUE. * @private * @see render() */ function _addDiff(&$aText, &$aFormat, &$aDoc) { // Since we're inside a PRE block we need the leading LFs: $ADD = "\n" . ''; $DEL = "\n" . ''; $HEAD = "\n" . ''; $CLOSE = ''; // Common headers for all formats; // the RegEx needs at least ")#" appended! $DiffHead = '#\n((?:diff\s[^\n]*)|(?:Index:\s[^\n]*)|(?:={60,})' . '|(?:RCS file:\s[^\n]*)|(?:retrieving revision [0-9][^\n]*)'; switch ($aFormat) { case 'u': // unified output $aDoc .= preg_replace( array($DiffHead . '|(?:@@[^\n]*))#', '|\n(\+[^\n]*)|', '|\n(\-[^\n]*)|'), array($HEAD . '\1' . $CLOSE, $ADD . '\1' . $CLOSE, $DEL . '\1' . $CLOSE), $aText); return TRUE; case 'c': // context output $sections = preg_split('|(\n\*{5,})|', preg_replace($DiffHead . ')#', $HEAD . '\1' . $CLOSE, $aText), -1, PREG_SPLIT_DELIM_CAPTURE); $sections[0] = preg_replace( array('|\n(\-{3}[^\n]*)|', '|\n(\*{3}[^\n]*)|'), array($ADD . '\1' . $CLOSE, $DEL . '\1' . $CLOSE), $sections[0]); $c = count($sections); for ($i = 1; $c > $i; ++$i) { $hits = array(); if (preg_match('|^\n(\*{5,})|', $sections[$i], $hits)) { unset($hits[0]); $sections[$i] = $HEAD . $hits[1] . $CLOSE; } else if (preg_match('|^\n(\x2A{3}\s[^\n]*)(.*)|s', $sections[$i], $hits)) { unset($hits[0]); // free mem $parts = preg_split('|\n(\-{3}\s[^\n]*)|', $hits[2], -1, PREG_SPLIT_DELIM_CAPTURE); // $parts[0] == OLD code $parts[0] = preg_replace('|\n([!\-][^\n]*)|', $DEL . '\1' . $CLOSE, $parts[0]); // $parts[1] == head of NEW code $parts[1] = $ADD . $parts[1] . $CLOSE; // $parts[2] == NEW code $parts[2] = preg_replace( array('|\n([!\x2B][^\n]*)|', '|\n(\x2A{3}[^\n]*)|'), array($ADD . '\1' . $CLOSE, $DEL . '\1' . $CLOSE), $parts[2]); if (isset($parts[3])) { // TRUE when handling multi-file patches $parts[3] = preg_replace('|^(\x2D{3}[^\n]*)|', $ADD . '\1' . $CLOSE, $parts[3]); } // if $sections[$i] = $DEL . $hits[1] . $CLOSE . implode('', $parts); } // if // ELSE: leave $sections[$i] as is } // for $aDoc .= implode('', $sections); return TRUE; case 'n': // RCS output // Only added lines are there so we highlight just the // diff indicators while leaving the text alone. $aDoc .= preg_replace( array($DiffHead . ')#', '|\n(d[0-9]+\s+[0-9]+)|', '|\n(a[0-9]+\s+[0-9]+)|'), array($HEAD . '\1' . $CLOSE, $DEL . '\1' . $CLOSE, $ADD . '\1' . $CLOSE), $aText); return TRUE; case 's': // simple output $aDoc .= preg_replace( array($DiffHead . '|((?:[0-9a-z]+(?:,[0-9a-z]+)*)(?:[^\n]*)))#', '|\n(\x26#60;[^\n]*)|', '|\n(\x26#62;[^\n]*)|'), array($HEAD . '\1' . $CLOSE, $DEL . '\1' . $CLOSE, $ADD . '\1' . $CLOSE), $aText); return TRUE; default: // unknown diff format $aDoc .= $aText; // just append any unrecognized text return TRUE; } // switch } // _addDiff() /** * Add the lines of the given $aList to the specified * $aDoc beginning with the given $aStart linenumber. * * @param $aList Array [IN] the list of lines as prepared by * render(), [OUT] FALSE. * @param $aStart Integer The first linenumber to use. * @param $aDoc String Reference to the current renderer's * doc property. * @private * @see render() */ function _addLines(&$aList, $aStart, &$aDoc) { // Since we're dealing with monospaced fonts here the width of each // character (space, NBSP, digit) is the same. Hence the length of // a digits string gives us its width i.e. the number of digits. $i = $aStart + count($aList); // greatest line number $g = strlen("$i"); // width of greatest number while (list($i, $l) = each($aList)) { unset($aList[$i]); // free mem $aDoc .= '' . $this->_lead[$g - strlen("$aStart")] . "$aStart:" . ((($l) && (' ' != $l)) ? " $l\n" : "\n"); ++$aStart; // increment line number } // while $aList = FALSE; // release memory } // _addLines() /** * Internal convenience method to replace HTML special characters. * * @param $aString String [IN] The text to handle; * [OUT] the modified text (i.e. the method's result). * @return String The string with HTML special chars replaced. * @private * @since created 05-Feb-2007 */ function &_entities(&$aString) { $aString = str_replace(array('&', '<', '>'), array('&', '<', '>'), $aString); return $aString; } // _entities() /** * Try to fix some markup error of the GeSHi SHELL highlighting. * *

* The GeShi highlighting for type "sh" (i.e. "bash") is, well, * seriously flawed (at least up to version 1.0.7.20 i.e. 2007-07-01). * Especially handling of comments and embedded string as well as * keyword is plain wrong. *

* This internal helper method tries to solve some minor problems by * removing highlight markup embedded in comment markup. * This is, however, by no means a final resolution: GeSHi obviously * keeps a kind of internal state resulting in highlighting markup * spawing (i.e. repeated on) several lines. * Which - if that state is wrong - causes great demage not by * corrupting the data but by confusing the reader with wrong markup. * The easiest way to trigger such a line spawning confusion is to use * solitary doublequotes or singlequotes (apostrophe) in a comment * line ... *

* @param $aMarkup String [IN] The highlight markup as returned by GeSHi; * [OUT] FALSE. * @param $aDoc String Reference to the current renderer's * doc property. * @private * @since created 04-Aug-2007 * @see render() */ function _fixGeSHi_Bash(&$aMarkup, &$aDoc) { $hits = array(); if (defined('GESHI_VERSION') && preg_match('|(\d+)\.(\d+)\.(\d+)\.(\d+)|', GESHI_VERSION, $hits) && ($hits = sprintf('%02u%02u%02u%03u', $hits[1] * 1, $hits[2] * 1, $hits[3] * 1, $hits[4] * 1)) && ('010007020' < $hits)) { // GeSHi v1.0.7.21 has the comments bug fixed $aDoc .= $aMarkup; $aMarkup = FALSE; // release memory return; } // if $lines = explode("\n", $aMarkup); $aMarkup = FALSE; // release memory while (list($i, $l) = each($lines)) { $hits = array(); // GeSHi "bash" module marks up comments with CSS class "re3": if (preg_match('|^((.*))(.*)$|i', $l, $hits)) { if ('#!/bin/' == substr($hits[3], 0, 7)) { $lines[$i] = $hits[2] . strip_tags($hits[3]); } else { $lines[$i] = $hits[1] . strip_tags($hits[3]) . ''; } // if } else if (! preg_match('|^\s*_JSmarkup) { // Markup already added (or not needed) return; } // if $localdir = realpath(dirname(__FILE__)) . '/'; $webdir = DOKU_BASE . 'lib/plugins/code/'; $css = ''; if (file_exists($localdir . 'style.css')) { ob_start(); @include($localdir . 'style.css'); // Remove whitespace from CSS and expand IMG paths: if ($css = preg_replace( array('|\s*/\x2A.*?\x2A/\s*|s', '|\s*([:;\{\},+!])\s*|', '|(?:url\x28\s*)([^/])|', '|^\s*|', '|\s*$|'), array(' ', '\1', 'url(' . $webdir . '\1'), ob_get_contents())) { $css = ''; } // if ob_end_clean(); } // if $js = (file_exists($localdir . 'script.js')) ? '' : ''; if ($this->_JSmarkup = $css . $js) { $aRenderer->doc = $this->_JSmarkup . preg_replace('|\s*

\s*

\s*|', '', $aRenderer->doc); //ELSE: Neither CSS nor JS files found. } // if // Set member field to skip tests with next call: $this->_JSmarkup = TRUE; } // _fixJS() /** * RegEx callback to markup spaces in ODT mode. * * @param $aList Array A list of RegEx matches. * @private * @static * @since created 07-Jun-2008 * @see render() */ function _preserveSpaces($aList) { return ($len = strlen($aList[1])) ? '' : ' '; } // _preserveSpaces() /** * Add the lines of the given $aText to the specified * $aDoc beginning with the given $aStart linenumber. * * @param $aText String [IN] the text lines as prepared by * handle(), [OUT] FALSE. * @param $aStart Integer The first linenumber to use; * if 0 (zero) no linenumbers are used. * @param $aDoc String Reference to the current renderer's * doc property. * @param $aClass String The CSS class name for the PRE tag. * @param $addTags Boolean Used in "ODT" mode to suppress tagging * the line numbers. * @private * @since created 03-Feb-2007 * @see render() */ function _rawMarkup(&$aText, $aStart, &$aDoc, $aClass, $addTags = TRUE) { if ($addTags) { $aDoc .= '
' . "\n";
    } // if
    if ($aStart) {
      // Split the prepared data into a list of lines:
      $aText = explode("\n", $aText);
      // Add the numbered lines to the document:
      $this->_addLines($aText, $aStart, $aDoc);
    } else {
      $aDoc .= $aText;
    } // if
    if ($addTags) {
      $aDoc .= '
'; } // if $aText = FALSE; // release memory } // _rawMarkup() /** * RegEx callback to replace SPAN tags in ODT mode. * * @param $aList Array A list of RegEx matches. * @private * @static * @since created 07-Jun-2008 * @see render() */ function _replaceSpan($aList) { return ($aList[3]) ? '' : ''; } // _replaceSpan() //@} /** * @publicsection */ //@{ /** * Tell the parser whether the plugin accepts syntax mode * $aMode within its own markup. * * @param $aMode String The requested syntaxmode. * @return Boolean FALSE (no nested markup allowed). * @public * @see getAllowedTypes() */ function accepts($aMode) { return FALSE; } // accepts() /** * Connect lookup pattern to lexer. * * @param $aMode String The desired rendermode. * @public * @see render() */ function connectTo($aMode) { // look-ahead to minimize the chance of false matches: $this->Lexer->addEntryPattern( '\x3Ccode(?=[^>]*\x3E\r?\n.*\n\x3C\x2Fcode\x3E)', $aMode, 'plugin_code'); } // connectTo() /** * Get an array of mode types that may be nested within the * plugin's own markup. * * @return Array Allowed nested types (none). * @public * @see accepts() * @static */ function getAllowedTypes() { return array(); } // getAllowedTypes() /** * Get an associative array with plugin info. * *

* The returned array holds the following fields: *

*
author
Author of the plugin
*
email
Email address to contact the author
*
date
Last modified date of the plugin in * YYYY-MM-DD format
*
name
Name of the plugin
*
desc
Short description of the plugin (Text only)
*
url
Website with more information on the plugin * (eg. syntax description)
*
* @return Array Information about this plugin class. * @public * @static */ function getInfo() { $c = 'code'; // hack to hide "desc" field from GeShi return array( 'author' => 'Matthias Watermann', 'email' => 'support@mwat.de', 'date' => '2008-07-22', 'name' => 'Code Syntax Plugin', 'desc' => 'Syntax highlighting with line numbering <' . $c . ' lang 1 |[fh] text |[hs]> ... ', 'url' => 'http://wiki.splitbrain.org/plugin:code2'); } // getInfo() /** * Define how this plugin is handled regarding paragraphs. * *

* This method is important for correct XHTML nesting. * It returns one of the following values: *

*
normal
The plugin can be used inside paragraphs.
*
block
Open paragraphs need to be closed before * plugin output.
*
stack
Special case: Plugin wraps other paragraphs.
*
* @return String "block". * @public * @static */ function getPType() { return 'block'; } // getPType() /** * Where to sort in? * * @return Integer 194 (below "Doku_Parser_Mode_code"). * @public * @static */ function getSort() { // class "Doku_Parser_Mode_code" returns 200 return 194; } // getSort() /** * Get the type of syntax this plugin defines. * * @return String "protected". * @public * @static */ function getType() { return 'protected'; } // getType() /** * Handler to prepare matched data for the rendering process. * *

* The $aState parameter gives the type of pattern * which triggered the call to this method: *

*
DOKU_LEXER_UNMATCHED
*
ordinary text encountered within the plugin's syntax mode * which doesn't match any pattern.
*
* @param $aMatch String The text matched by the patterns. * @param $aState Integer The lexer state for the match. * @param $aPos Integer The character position of the matched text. * @param $aHandler Object Reference to the Doku_Handler object. * @return Array Index [0] holds the current $aState, * index [1] the embedded text to highlight, * index [2] the language/dialect (or FALSE), * index [3] the first line number (or 0), * index [4] the top title (or FALSE), * index [5] the bottom title (or FALSE), * index [6] hidding CSS flag (or ""). * @public * @see render() * @static */ function handle($aMatch, $aState, $aPos, &$aHandler) { if (DOKU_LEXER_UNMATCHED != $aState) { return array($aState); // nothing to do for "render()" } // if $aMatch = explode('>', $aMatch, 2); // $aMatch[0] : lang etc. // $aMatch[1] : text to highlight $n = explode('>', trim($aMatch[1])); $l = 'extern'; // external resource requested? // Check whether there's an external file to fetch: if ($l == $n[0]) { if ($n[1] = trim($n[1])) { if (is_array($n[0] = @parse_url($n[1])) && ($n[0] = $n[0]['scheme'])) { // Don't accept unsecure schemes like // "file", "javascript", "mailto" etc. switch ($n[0]) { case 'ftp': case 'http': case 'https': //XXX This might fail due to global PHP setup: if ($handle = @fopen($n[1], 'rb')) { $aMatch[1] = ''; while (! @feof($handle)) { //XXX This might fail due to // memory constraints: $aMatch[1] .= @fread($handle, 0x8000); } // while @fclose($handle); } else { $aMatch = array($l, 'Failed to retrieve: ' . $n[1]); } // if break; default: $aMatch = array($l, 'Unsupported URL scheme: ' . $n[0]); break; } // switch } else { $aMatch = array($l, 'Invalid URL: ' . $n[1]); } // if } else { $aMatch = array($l, 'Missing URL: ' . $aMatch[1]); } // if } // if // Strip leading/trailing/EoL whitespace, // replace TABs by four spaces, " " by NBSP: $aMatch[1] = preg_replace( array('#(?>\r\n)|\r#', '|^\n\n*|', '|[\t ]+\n|', '|\s*\n$|'), array("\n", '', "\n", ''), str_replace(' ', ' ', str_replace("\t", ' ', $aMatch[1]))); $css = ''; // default: no initial CSS content hidding $l = FALSE; // default: no language $n = 0; // default: no line numbers $ht = $ft = FALSE; // default: no (head/foot) title $hits = array(); // RegEx matches from the tag attributes /* The free form of the RegEx to parse the arguments here is: /^ # "eat" leading whitespace: \s* (?=\S) # Look ahead: do not match empty lines. This is # needed since all other expressions are optional. # Make sure, nothing is given away once it matched: (?> # We need a separate branch for "diff" because it may be # followed by a _letter_ (not digit) indicating the format. (?> (diff) # match 1 (?>\s+([cnrsu]?))? # match 2 ) | # Branch for standard language highlighting (?> # extract language: ([a-z][^\x7C\s]*) # match 3 (?> # extract starting line number: \s+(\d\d*) # match 4 )? ) | # Branch for line numbering only (\d\d*) # match 5 | \s* # dummy needed to match "title only" markup (below) ) # "eat" anything else up to the text delimiter: [^\x7C]* (?> \x7C # extract the position flag: ([bfht])?\s* # match 6 # extract the header,footer line: ([^\x7C]+) # match 7 (?> # see whether there is a class flag: \x7C\s* (h|s)?.* # match 8 )? )? # Anchored to make sure everything gets matched: $/xiu Since compiling and applying a free form RegEx slows down the overall matching process I've folded it all to a standard RegEx. Benchmarking during development gave me free form: 20480 loops, 552960 hits, 102400 fails, 12.994689 secs standard: 20480 loops, 552960 hits, 102400 fails, 8.357169 secs */ if (preg_match('/^\s*(?=\S)(?>(?>(diff)(?>\s+([cnrsu]?))?)|' . '(?>([a-z][^\x7C\s]*)(?>\s+(\d\d*))?)|(\d\d*)|\s*)[^\x7C]*' . '(?>\x7C([bfht])?\s*([^\x7C]+)(?>\x7C\s*(h|s)?.*)?)?$/iu', $aMatch[0], $hits)) { unset($hits[0]); // free mem // $hits[1] = "diff" // $hits[2] = type (of [1]) // $hits[3] = LANG // $hits[4] = NUM (of [3]) // $hits[5] = NUM (alone) // $hits[6] = Top/Bottom flag (of [7]) // $hits[7] = TITLE // $hits[8] = s/h CSS flag if (isset($hits[3]) && ($hits[3])) { $l = strtolower($hits[3]); if (isset($hits[4]) && ($hits[4])) { $n = (int)$hits[4]; } // if $hits[3] = $hits[4] = FALSE; } else if (isset($hits[1]) && ($hits[1])) { $l = strtolower($hits[1]); $hits[2] = (isset($hits[2])) ? strtolower($hits[2]) . '?' : '?'; $n = $hits[2]{0}; $hits[1] = $hits[2] = FALSE; } else if (isset($hits[5]) && ($hits[5])) { $n = (int)$hits[5]; } // if if (isset($hits[7]) && ($hits[7])) { $hits[6] = (isset($hits[6])) ? strtolower($hits[6]) . 'f' : 'f'; switch ($hits[6]{0}) { case 'h': case 't': $ht = trim($hits[7]); break; default: $ft = trim($hits[7]); break; } // switch if (isset($hits[8])) { $hits[8] = strtolower($hits[8]) . 's'; if ('h' == $hits[8]{0}) { // This class is handled by JavaScript (there // _must_not_ be any CSS rules for this): $css = ' HideOnInit'; } // if } // if $hits[6] = $hits[7] = $hits[8] = FALSE; } // if // ELSE: no arguments given to CODE tag } // if switch ($l) { case 'console': // nothing additional to setup here break; case 'diff': if ("\n" != $aMatch[1]{0}) { // A leading LF is needed to recognize and handle // the very first line with all the REs used. $aMatch[1] = "\n" . $aMatch[1]; } // if switch ($n) { case 'u': // DIFF cmdline switch for "unified" case 'c': // DIFF cmdline switch for "context" case 'n': // DIFF cmdline switch for "RCS" case 's': // We believe the format hint ... // (or should we be more suspicious?) break; case 'r': // Mnemonic for "RCS" $n = 'n'; break; default: // try to figure out the format actually used if (preg_match( '|\n(?:\x2A{5,}\n\x2A{3}\s[1-9]+.*?\x2A{4}\n.+?)+|s', $aMatch[1])) { $n = 'c'; } else if (preg_match( '|\n@@\s\-[0-9]+,[0-9]+[ \+,0-9]+?@@\n.+\n|s', $aMatch[1])) { $n = 'u'; } else if (preg_match( '|\n[ad][0-9]+\s+[0-9]+\r?\n|', $aMatch[1])) { // We've to check this _before_ "simple" since // the REs are quite similar (but this one is // slightly more specific). $n = 'n'; } else if (preg_match( '|\n(?:[0-9a-z]+(?:,[0-9a-z]+)*)(?:[^\n]*\n.*?)+|', $aMatch[1])) { $n = 's'; } else { $n = '?'; } // if break; } // switch break; case 'htm': // convenience shortcut case 'html': // dito $l = 'html4strict'; break; case 'js': // shortcut $l = 'javascript'; break; case 'sh': // shortcut $l = 'bash'; break; default: if (! $l) { // no language: simple PRE markup will get generated $l = FALSE; } // if break; } // switch return array(DOKU_LEXER_UNMATCHED, $aMatch[1], $l, $n, $ht, $ft, $css); } // handle() /** * Add exit pattern to lexer. * * @public */ function postConnect() { // look-before to minimize the chance of false matches: $this->Lexer->addExitPattern('(?<=\n)\x3C\x2Fcode\x3E', 'plugin_code'); } // postConnect() /** * Handle the actual output (markup) creation. * *

* The method checks the given $aFormat to decide how to * handle the specified $aData. * The standard case (i.e. "xhtml") is handled completely * by this implementation, preparing linenumbers and/or head/foot * lines are requested. * For the "odt" format all plugin features (incl. linenumbers * and header/footer lines) are supported by generating the appropriate * ODT/XML markup. * All other formats are passed back to the given $aRenderer * instance for further handling. *

* $aRenderer contains a reference to the renderer object * which is currently in charge of the rendering. * The contents of the given $aData is the return value * of the handle() method. *

* @param $aFormat String The output format to generate. * @param $aRenderer Object A reference to the renderer object. * @param $aData Array The data created/returned by the * handle() method. * @return Boolean TRUE. * @public * @see handle() */ function render($aFormat, &$aRenderer, &$aData) { if (DOKU_LEXER_UNMATCHED != $aData[0]) { return TRUE; } // if if ('xhtml' == $aFormat) { if ($tdiv = (($aData[4]) || ($aData[5]))) { $this->_fixJS($aRenderer); // check for old DokuWiki versions $aRenderer->doc .= '
'; if ($aData[4]) { //XXX Note that "_headerToLink()" is supposed to be a // _private_ method of the renderer class; so this code // will fail once DokuWiki is rewritten in PHP5 which // implements encapsulation of private methods and // properties: $aRenderer->doc .= '

' . $this->_entities($aData[4]) . '

'; $aData[4] = $aData[6] = FALSE; // free mem } // if } // if if ($aData[2]) { // lang was given if ('console' == $aData[2]) { $this->_rawMarkup($this->_entities($aData[1]), $aData[3], $aRenderer->doc, $aData[2]); } else if ('diff' == $aData[2]) { $this->_entities($aData[1]); $aRenderer->doc .= '
';
          $this->_addDiff($aData[1], $aData[3], $aRenderer->doc);
          $aRenderer->doc .= '
'; } else { $isSH = ('bash' == $aData[2]); $geshi = new GeSHi($aData[1], $aData[2], GESHI_LANG_ROOT); if ($geshi->error()) { // Language not supported by "GeSHi" $geshi = NULL; // release memory $this->_rawMarkup($this->_entities($aData[1]), $aData[3], $aRenderer->doc, 'code'); } else { $aData[1] = FALSE; // free mem $geshi->enable_classes(); $geshi->set_encoding('utf-8'); $geshi->set_header_type(GESHI_HEADER_PRE); $geshi->set_overall_class('code ' . $aData[2]); global $conf; if ($conf['target']['extern']) { $geshi->set_link_target($conf['target']['extern']); } // if if ($aData[3]) { // line numbers requested // Separate PRE tag from parsed data: $aData[1] = explode('>', $geshi->parse_code(), 2); // [1][0] = leading "doc .= $aData[1][0] . '>'; // Separate trailing PRE tag: $aData[1] = explode('', $aData[1][1], 2); // [1][0] = GeSHi markup // [1][1] = trailing "_fixGeSHi_Bash($aData[1][0], $aData[1][1]); } else { // Set reference to fixed markup to sync with // the "bash" execution path (above): $aData[1][1] =& $aData[1][0]; } // if // Split the parsed data into a list of lines: $aData[2] = explode("\n", $aData[1][1]); $aData[1] = FALSE; // free mem // Add the numbered lines to the document: $this->_addLines($aData[2], $aData[3], $aRenderer->doc); // Close the preformatted section markup: $aRenderer->doc .= ''; } else { // w/o line numbering if ($isSH) { // Separate trailing PRE tag which // sometimes is "forgotten" by GeSHi: $aData[2] = explode('', $geshi->parse_code(), 2); // [1][0] = GeSHi markup // [1][1] = trailing "_fixGeSHi_Bash($aData[2][0], $aRenderer->doc); $aRenderer->doc .= ''; } else { $aRenderer->doc .= $geshi->parse_code(); } // if $geshi = NULL; // release memory } // if } // if } // if } else { $this->_rawMarkup($this->_entities($aData[1]), $aData[3], $aRenderer->doc, 'code'); } // if if ($tdiv) { if ($aData[5]) { //XXX See "_headerToLink()" note above. $aRenderer->doc .= '

' . $this->_entities($aData[5]) . '

'; } // if $aRenderer->doc .= '
'; } // if } else if ('odt' == $aFormat) { $inLI = array(); if (preg_match('|^\s*\s*(.*)$|si', $aRenderer->doc, $inLI)) { // remove leading whitespace $aRenderer->doc = $inLI[1]; } // if // The "renderer_plugin_odt" doesn't clean (close) // its own tags before calling this plugin. // To work around that bug we have to check some // private properties of the renderer instance. $inLI = FALSE; if (is_a($aRenderer, 'renderer_plugin_odt')) { if ($inLI = ($aRenderer->in_list_item)) { // If we're in a list item, we've to close the paragraph: $aRenderer->doc .= ''; } // if if ($aRenderer->in_paragraph) { $aRenderer->doc .= ''; $aRenderer->in_paragraph = FALSE; } // if } // if // Init (open) our text section: $aRenderer->doc .= "\n" . ''; if ($tdiv = (($aData[4]) || ($aData[5]))) { // Check whether we need a top caption ("header"): if ($aData[4]) { $aRenderer->doc .= '' . "\n" . $aData[4] . "\n"; $aData[4] = $aData[6] = FALSE; // free mem } // if } // if // The following code resembles the "xhtml" processing // above except that we're not using "pre" tags here // but ODT/XML markup. $aData[0] = ''; // tmp. container of processed data if ($aData[2]) { // lang was given if ('console' == $aData[2]) { $this->_rawMarkup($this->_entities($aData[1]), $aData[3], $aData[0], $aData[2], FALSE); } else if ('diff' == $aData[2]) { $this->_addDiff($this->_entities($aData[1]), $aData[3], $aData[0]); } else { $isSH = ('bash' == $aData[2]); $geshi = new GeSHi($aData[1], $aData[2], GESHI_LANG_ROOT); if ($geshi->error()) { // Language not supported by "GeSHi" $geshi = NULL; // release memory $this->_rawMarkup($this->_entities($aData[1]), $aData[3], $aData[0], '', FALSE); } else { $aData[1] = FALSE; // free mem $geshi->enable_classes(); $geshi->set_encoding('utf-8'); $geshi->set_header_type(GESHI_HEADER_PRE); $geshi->set_overall_class('code ' . $aData[2]); global $conf; if ($conf['target']['extern']) { $geshi->set_link_target($conf['target']['extern']); } // if // Separate PRE tag from parsed data: $aData[1] = explode('>', $geshi->parse_code(), 2); // [1][0] = leading "', $aData[1][1], 2); // [1][0] = GeSHi markup // [1][1] = trailing "_fixGeSHi_Bash($aData[1], $aData[2]); } else { $aData[2] = $aData[1]; } // if $aData[1] = FALSE; // release memory if ($aData[3]) { // line numbers requested // Split the parsed data into a list of lines: $aData[1] = explode("\n", $aData[2]); $aData[2] = FALSE; // release memory // Add the numbered lines to the document: $this->_addLines($aData[1], $aData[3], $aData[0]); } else { // w/o line numbers $aData[0] = $aData[2]; $aData[2] = FALSE; // release memory } // if } // if } // if } else { $this->_rawMarkup($this->_entities($aData[1]), $aData[3], $aData[0], '', FALSE); } // if if ('console' == $aData[2]) { $aRenderer->doc .= ''; } else { $aRenderer->doc .= ''; } // if // Replace the HTML "span" tags (for highlighting) by // the appropriate ODT/XML markup. // For unknown reasons we need an additional space // in front of the very first line. $aData[0] = '' . preg_replace_callback('|(]*)?>)|', array('syntax_plugin_code', '_replaceSpan'), // OOo (v2.3) crashes on " " str_replace(' ', chr(194) . chr(160), str_replace('
', '', strip_tags($aData[0], '')))); // Now append our markup to the renderer's document; // TABs, LFs and SPACEs are replaced by their respective // ODT/XML equivalents: $aRenderer->doc .= preg_replace_callback('|( {2,})|', array('syntax_plugin_code', '_preserveSpaces'), str_replace("\n", "\n", $aData[0])); $aData[0] = FALSE; // release memory // Check whether we need a bottom caption ("footer"): if ($tdiv && ($aData[5])) { $aRenderer->doc .= '' . $aData[5]; } // if // Close all our open tags: $aRenderer->doc .= "\n"; if ($inLI) { // Workaround (see above): (re-)open a paragraph: $aRenderer->doc .= ''; } // if } else { // unsupported output format $aData[0] = $aData[4] = $aData[5] = FALSE; // avoid recursion // Pass anything else back to the renderer instance // (which will - hopefully - know how to handle it): $aRenderer->code($aData[1], $aData[2]); } // if $aData = array(FALSE); // don't process this text again return TRUE; } // render() //@} } // class syntax_plugin_code } // if //Setup VIM: ex: et ts=2 enc=utf-8 : ?>
==== Presentation ==== The accompanying CSS presentation rules (using web-safe colours only): div.dokuwiki pre.code,pre.code,div.dokuwiki pre.console,pre.console{border:thin dotted #ccc;margin:1ex 0;line-height:1.33;overflow:auto;text-indent:0;max-height:40em;max-width:99%;} div.dokuwiki pre.code,pre.code{background:#fcfdfe none;color:#000;padding:0.4ex;} .code .diff_addedline{background:#cfc none;color:#000;} .code .diff_blockheader{background:#ccf none;color:#000;} .code .diff_deletedline{background:#fcc none;color:#000;} .code .br0{background:inherit;color:#369;} .code .co1,.code .co2,.code .coMULTI,.code .kw2,.code .lno{font-style:italic;} .code .br0,.code .co2,.code .es0,.code .kw1,.code .kw2,.code .kw3,.code .lno{font-weight:600;} .code .co1,.code .co2,.code .coMULTI{background:inherit;color:#666;} .code .es0{background:inherit;color:#c09;} .code .imp{background:inherit;color:#909;} .code .kw1{background:inherit;color:#903;} .code .kw2{background:inherit;color:#036;} .code .kw3{background:inherit;color:#309;} .code .kw4{background:inherit;color:#933;} .code .kw5{background:inherit;color:#00f;} .code .lno{background:inherit;color:#999;font-size:smaller;} .code .me0{background:inherit;color:#060;} .code .nu0{background:inherit;color:#939;} .code .re0{background:inherit;color:#606;} .code .re1{background:inherit;color:#660;} .code .re2{background:inherit;color:#063;} .code .re3{background:inherit;color:#963;font-style:italic;font-weight:400;} .code .re4{background:inherit;color:#099;} .code .sc0{background:inherit;color:#069;} .code .sc1{background:inherit;color:#960;} .code .sc2{background:inherit;color:#090;} .code .st0{background:inherit;color:#900;} .code .sy0{background:inherit;color:#6c6;} pre.code a{border:none;} div.dokuwiki pre.console,pre.console{background:#333 none;color:#fff;font-weight:900;padding:0.4ex 0.3ex 0.6ex 0.6ex;} div.dokuwiki pre.console .lno,pre.console .lno{background:inherit;color:#cff;font-size:smaller;font-style:italic;} div.code{margin:0.4ex 0;padding:0.4ex 0;} div.code pre.code{margin:0;} div.code p.codehead,div.code p.codefoot{color:#030;background:inherit;line-height:1.33;text-align:left;padding:0 0 0 1ex;} div.code p.codehead{margin:0.6ex 0 0 0;text-decoration:underline;} div.code p.codefoot{margin:0 0 0.6ex 0;text-decoration:underline;} div.code p.codeHidden{background-image:url(img/plus-11x11.gif);} div.code p.codeShown{background-image:url(img/minus-11x11.gif);} div.code p.codeHidden,div.code p.codeShown{padding-left:13px;background-repeat:no-repeat;background-position:0 50%;cursor:pointer;} div.code p.codeHidden:hover,div.code p.codeShown:hover{background-color:#ddffdd;color:#030;text-decoration:none;} div.code pre.codeHidden{display:none;} div.code pre.codeShown{display:block;} Of course, you're free to modify these styles((The [[http://dev.mwat.de/dw/syntax_plugin_code.zip|source archive]] contains a commented and indented stylesheet for your information.)) to suit your personal needs or aesthetics((Just be careful when modifying a CSS file: both the order and the selector groupings are important for CSS to work as intended/expected.)). ==== Behaviour ==== Sometimes code blocks can be quite long. Long enough, anyway, to sort of overwhelm the other text of the respective page and forcing the user to scroll up/down a lot. The JavaScript file that comes with this plugin provides some additional functionality for the reader to address this issue: If you've added a [[#usage|header/footer]] line to the code block that very line becomes a //clickable area// allowing to fold (hide/show) the whole code block. The client side JavaScript accompanying this plugin uses a totaly unobtrusive approach. That means there's no mixing of markup (HTML), presentation (CSS) and behaviour (JavaScript). And if JavaScript is not available for one [[:quantserve|reason]] or another the only consequence is that there's no folding feature i.e. the very same situation as the one without this plugin installed. If, however, JavaScript //is// available (and activated) at the user's side((always assuming a browser implementing the DOM standard methods)) clicking on a code block's header/footer line will toggle the visibility of the respective code block. Of course -- since visibility, colour and alike do not belong to the behaviour domain of JavaScript but to presentation i.e. CSS -- the script just assigns CSS classes to the respective page elements. That allows to configure the actual changes which should follow a user's click without having to learn and modify JavaScript source code: Just adjust the CSS and you're done. If -- for example -- you do not want the code block to disappear completely "on click" you could change the two very last lines of the [[#presentation|CSS]] file to something like this: div.code pre.codeHidden { max-height: 3em; } div.code pre.codeShown { max-height: 50em; } With browsers implementing the CSS correctly((which cannot be presumed with all browsers since CSS is an open standard and only ten years old)) the styling rules above will "shrink" the code block to at most three lines (instead of hidding them altogether) on click and expand the code block on the next click to at most 50 lines. The script itself defines just one (meaningless) global variable((which would not be necessary if DokuWiki's JavaScript wouldn't be the mess it is but use a proper namespace concept instead)) while encapsulating all its functionality in private members and methods. So there's little chance of naming conflicts with other JavaScript. === Folding state === Without user intervention (i.e. clicking on header/footer lines) nothing changes from default. That means all code blocks are visible((unless you reverse the "meaning" of the two CSS selectors mentioned above by changing the settings therein)) as one would expect. After all, it would be quite unthoughtful to hide parts of a page by default thus //forcing// the reader to click somewhere before he/she sees the hidden contents. But. But there may be situations where the code block is not that important but more sort of illustration and the surrounding text should gain the user's focus first. In other words: //You// -- and not some hardcoded browser logic -- whish some block of highlighted code to be hidden initially (i.e. when the page is presented to the user) and become visible only on explicit user intervention (i.e. clicking). That's where the very last optional argument of the code tag comes into the play:\\ ''%%%%'' This adds some more use cases to the examples given [[#usage|above]]: * ''%%%%\\ some text\\ %%%%''\\ marks up "''some text''" as preformatted text (w/o any special highlighting) with a footer line of "''A description''" and the actual code block hidden initially. * ''%%%%\\ some text\\ %%%%''\\ turns on HTML highlighting for "''some text''" and a header line of "''A description:''" with the actual code block hidden initially. * ''%%%%\\ some text\\ %%%%''\\ turns on HTML highlighting for "''some text''" with added line numbers starting with number ''66'' and a footer line of "''A description''" with the actual code block hidden initially. You've noticed, probably, that I didn't use the "''s''" flag here. The simple reason: As mentioned above the default behaviour is to ''show'' the code block anyway. Hence the "''s''" flag doesn't really change anything; however, I've implemented it for symmetry reasons and to allow for a more explicit markup. When implementing this folding feature I've taken great care to not exclude users with JavaScript turned off. One consequence is that code blocks you marked up using the "''hide''" flag will be //not hidden// in browsers w/o JavaScript((otherwise those users would have to disable CSS completely in their browsers to see the hidden contents)). In case you neither like nor want this folding feature, simply remove (delete or rename) the ''script.js'' file in the ''{dokuwiki}/lib/plugins/code'' directory after [[#installation|installing]] this plugin. The non-/existence of the JavaScript file doesn't influence the [[#Plugin Source|PHP plugin]] in any way. ==== Changes ==== __2008-07-22__:\\ * CSS: added some selectors to work around DokuWiki's faulty default CSS;\\ * JS: minor internal changes (mostly adding comments);\\ * PHP: changed some misleading texts in 'handle()'; __2008-06-09__:\\ * updated CSS class names in '_addDiff()';\\ * modified '_addLines()' to use SPAN (instead of B) tags;\\ + implemented a version test in '_fixGeSHi_Bash()';\\ + implemented private '_preserveSpaces()' and '_replaceSpan()' methods for use by ODT export;\\ * added optional 'addTags' argument in '_rawMarkup()' to suppress PRE tags in ODT exports;\\ * changed whitespace handling in 'handle()' (for better OTD support);\\ + completely rewrote ODT generation in 'render()' to fully support highlighting and header/footer lines;\\ * CSS: renamed DIFF related classes (for consistency with ODT styles);\\ + added ODT stylesheet; __2008-06-02__:\\ + implemented basic support for ODT export;\\ * modified '_addLines()' to accept new 'addTags' argument;\\ # removed a superflous end tag in '_fixJS()';\\ * moved entityfication from 'handle()' to 'render()' method since the ODT plugin does this on its own;\\ * modified 'render()' method to pass unsupported data formats back to the active renderer instance; __2008-05-26__:\\ + extended 'handle()' method to retrieve external files; __2008-05-25__:\\ * CSS: added a text-indent rule (for code snippets in footnotes etc.); __2008-04-05__:\\ + implemented new ''"console"'' pseudo language keyword;\\ * replaced former private "_makeID()" method by calls of the renderer's "_headerToLink()" method; __2007-08-31__:\\ * modified "_fixGeSHi_Bash()" to omit LF if the very last line is marked up as comment; __2007-08-28__:\\ * corrected some doc typos; __2007-08-25__:\\ - removed TAB reduction in "handle()"; __2007-08-16__:\\ * minor change in "_fixGeSHi_Bash()" to omit trailing LF; __2007-08-15__:\\ * added GPL link and fixed some doc problems; __2007-08-05__:\\ + implemented another workaround in "render()" for GeSHi forgetting the trailing/closing "pre" tag in "bash" mode; __2007-08-04__:\\ + implemented private "_fixGeSHi_Bash()" method to try to fix some GeSHi errors and modified "render()" accordingly;\\ * decreased init timeout in JavaScript class;\\ + added '.re3' selector in CSS file; __2007-02-23__:\\ * several internal changes (mostly for better support of older browsers and DokuWiki releases); __2007-02-19__:\\ + implemented support for DokuWiki installations older 2006-03-05; __2007-02-11__:\\ * modified overall RegEx to require LF (to minimize false matches);\\ * improved TAB handling in "handle()";\\ + implemented optional flag/option to hide code blocks initially;\\ + added JavaScript to allow for folding code blocks; __2007-02-04__:\\ * improved handling of leading/trailing/EoL whitespace to save memory & bandwidth; __2007-02-03__:\\ * recrafted RegEx used in "handle()" to become more tolerant, faster and consuming less memory;\\ * implemented handling of GeSHi "error()"; __2007-02-02__:\\ * made some REs nonpossessive in "_addDiff()"; __2007-01-31__:\\ + implemented header/footer option;\\ * minor changes in "handle()" to make "diff" REs unpossessive; __2007-01-16__:\\ * rewrote private "_addLines()" to handle line numbers up to 999,999,999; __2007-01-12__:\\ + added 'r' option for "diff" highlighting; __2007-01-10__:\\ + implemented (recognition/highlighting of) "RCS" diff format; __2007-01-05__:\\ * moved entity replacements for blocks w/o language from "render()" to "handle()" and added some more comments; __2006-12-25__:\\ + initial release; //[[support@mwat.de|Matthias Watermann]] 2008-07-22// ===== Notes ===== Please note, that this plugin shares common problems with DokuWiki's builtin "''%%%%''" tag handler as far as the underlying "GeSHi" module is concerned. ==== Markup ==== If there's "''%%%%''" markup //within// the text to highlight the RegEx based parsers will stumble -- with unwanted results((That's the reason why I changed the markup in my source doc comments from ''%%%%'' to ''%%%%'' -- a practise you could follow if you experience such a problem.)). --- Now, considering that (a) the //strings// "''%%%%''" and "''%%%%''" occur almost exclusively in texts marked up by (X)HTML tags and that (b) the "''%%code%%''" //tag// is an //inline// tag, one might presume that in most cases((i.e. //not// always!)) those tags will be embedded within a line of text. Therefor this plugin uses slightly different patterns requiring a LF((LineFeed, char #10, "''\n''")) both immediately //after the opening tag// __and__ //before the closing tag// -- thus minimizing (but //not// removing) the chance of false matches. So DokuWiki markup such as bla bla bla or bla bla bla or bla bla bla will //not// be matched & handled by this plugin. However, that doesn't stop DokuWiki's //builtin// "''code''" handler from jumping in. And since those builtin DokuWiki parsers are not designed for extensibility you have to live with this problem. While this might sound annoyingly comp-li-cated(("Nac Mac Feegle! The Wee Free Men! Nae king! Nae quin! Nae laird! Nae master! //We willna be fooled again!//")), actually, the only point you should be aware of is to //just make sure the LFs mentioned above are in place//. ==== Console ==== The ''console'' mode is intended to reproduce text such as commandline commands and their respective output; for example to document the installation of an application or the invocation of a program etc. You'll have to capture the output of a console session by some means((like using the ''session'' program, or redirecting the I/O from/to a file, or using the ''tee'' tool, or ...)) to be able to insert the text in a wiki page. Using ''console'' does not really produce some kind of highlighting but just places the given content in a preformatted (X)HTML section with accompanying styling rules to sort of resemble a raw console screen's output. You may use the optional //linenumber// and //footer/header// arguments just as with all the other languages to markup/highlight. However, in ''console'' mode the tag's content (i.e. the text between the opening and closing ''code'' tags) is just printed out as-is without analyzing or tokenizing it: plain and dump. ==== Diff ==== The problems and solution regarding "''diff''" handling are already discussed in the [[#usage]] and [[#examples]] sections above. With this plugin installed everything works as expected. ==== External Files ==== Starting with the 2008-05-26 release this plugin allows for including highlighted data from external sources. The wiki markup would look like this: extern> http://www.dokuwiki.org/lib/exe/css.php So basically instead of inserting the complete text to highlight you just enter an URI with the "''extern>''" keyword prepended. You may use any highlighting type you wish in the "''code''" tag. e.g. extern> http://www.dokuwiki.org/ would produce a numbered and highlighted listing of the given page's markup and extern> http://www.dokuwiki.org/lib/exe/js.php gives you an impression of the script code delivered to DokuWiki readers ... This feature allows you to include a highlighted version of whatever a remote server happens to send back when serving your specified URI((This includes error pages such as "''404 - file not found''"!)). You should note, however, that there's no relation between the "filetype" of an URI (as indicated by a socalled filename extension) and the content-type of the data returned when serving the URI request. The first example above //appears// to request a PHP file, but the returned data are of type "''text/css''", actually. The second example requests //something// (by an URI w/o any indication about a possible data type) and gets a "''text/html''" data stream. The third example, finally, again seems to request a PHP file but, in fact, gets data of type "''text/javascript''". -- So please don't confuse the URI with the data it points to. And, ah, you should //know// about the content-type of a certain URI's data when deciding which highlighting language to use: Although the examples above all point (directly or indirectly) to a PHP file none of them return a PHP source code((unless, of course, the remote server is broken)). As noted above, when using this feature to include external resources you have to live with whatever the external server sends. Especially the formatting of the data may not suit your needs. For example, quite a few server send CSS, HTML, JavaScript etc. without any unnecessary whitespace((thus saving lots af bandwidth and transfer time)) i.e. there may be no linefeeds or indentions in the data stream you receive. While that is perfectly okay for browsers it doesn't //look// too well in a wiki page, even with syntax highlighting. So you should check the resource you'd like to include with a browser's //''Show Source''// view to decide whether it's suitable for inclusion in a wiki page. Please be aware that lots of people consider it abusive to include their respective contents in foreign pages. Note as well, that including((as opposed to //linking//)) external resources could raise copyright problems. If in doubt, just ask the respective site operator/owner for permission before using this feature. === Limitations on external files === For security reasons the supported URI-schemes are limited to "''ftp''", "''http''" and "''https''"((ignoring e.g. "''gopher''" because I think wiki operators neither know about, nor use it anyway)). Hence it's not possible to compromise your wiki by either e.g. malicious use of the "''javascript''" or "''mailto''" pseudo schemes or the exposure of local files by the "''file''" scheme. Please note as well that this feature depends on PHP's [[http://us2.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen|allow-url-fopen]] configuration setting. So it's //not// guaranteed to work on each and every system. Consult your PHP manual for more details about this subject. In case someone sees other security problems with this feature please drop me a [[support@mwat.de|note]]. ==== Open Document Format ==== There's an experimental [[plugin:odt|odt plugin]] aiming to support the [[http://en.wikipedia.org/wiki/OpenDocument|Open Document Format]]. Unfortunately that plugin doesn't use the well defined XHTML markup (produced by all builtin and plugin renderers) to prepare a page for export but instead requires that each and every syntax plugin explicitely supports the ODT as well. As of 2008-06-02 [[#Plugin Source|this plugin]] provides support for the ODT export. However, you should be aware that the overall result of an ODT export might not be what you expect. Not only whole parts of a wiki page may simply disappear((mostly due to other plugins not yet implementing ODT support)). But the exported ODT file can be corrupted as well since the odt plugin((at least up to its 2008-05-07 release)) doesn't support the native DokuWiki syntax completely((For example, code fragments in list items or (block)quotes sometimes just disappear although this plugin produces the appropriate ODT/XML markup which can be found in the exported ODT's ''content.xml'' file.)). So before making ODT exports publically available for your wiki pages you should carefully check whether it works for you or not. Please note, that this plugin handles //only// the code fragments marked up by "''''\\ ''some text to highlight''\\ ''''" but //not// the preformatted texts in your wiki page indented by spaces or TABs. The latter are handled by the [[plugin:odt|odt]] plugin which doesn't work properly in all constellations. If you're experiencing problems in this area try to add the ''code'' tags and see whether it helps. Anyway, as far as [[#Plugin Source|this plugin]] is concerned the text to highlight results in preformatted text, optionally with line numbers prepended (see [[#Examples]] above). The header/footer lines are placed in the ODT export as well but, of course, there's no show/hide feature in the ODT file. === ODT highlighting === To make the syntax highlighting actually work the ODT reader (e.g. [[http://why.openoffice.org/|OpenOffice.org]] writer) needs a fitting stylesheet -- just as the web-browser needs one -- to render the text. Starting with the 2008-06-09 release the plugin's [[#Installation|archive]] contains a file ''"syntax_plugin_code_styles.xml"'' which provides styling rules for all the classes used by this plugin. You'll have to copy it into the ''odt'' plugin's directory to let the latter use it. Because this task cannot automaticated you'll need to change to this plugin's directory and copy the stylesheet //manually//, e.g. $> cd /to/my/dokuwiki/lib/plugins/code $> cp syntax_plugin_code_styles.xml ../odt/styles.xml $> Once you've done that all your ODT exports will use a highlighting similar to the browser's [[#Presentation]]. If you miss this step the resulting ODT file should still be usable but all the code fragments will look like standard text -- probably not what you intend ... ==== Shell ==== The //GeSHi// module handling shell scripts ("''sh''"/"''bash''") is, well, seriously flawed (at least up to version 1.0.7.20 i.e. 2007-07-01). Especially the handling of comments with embedded strings as well as keywords is plain wrong. This plugin tries to solve some minor problems by removing highlight markup embedded in comment markup. This is, however, by no means a final solution: //GeSHi// obviously keeps a kind of internal state resulting in highlighting markup spanning (i.e. repeated on) several lines. Which -- if that state is wrong -- causes great demage: not by corrupting the data but by confusing the reader with wrong markup. The easiest way to trigger such a line spanning confusion is to use solitary doublequotes or singlequotes (apostrophe) in a comment line as demonstrated below. The following fragment will show up correctly with this plugin installed but wrong with DokuWiki's builtin ''code'' handler (using //GeSHi// up to v1.0.7.20): #!/bin/sh # This file is for test purposes only. # There is no other reason - # hence do not use it. echo "This test is right!" exit 0 #_EoF_ Although the words "''for''", "''test''" and "''do''" are just plain text in a comment line, //GeSHi// (up to v1.0.7.20) treats them as keywords. This cause of irritation gets removed by this plugin (as of 2007-08-04). The next fragment, however, will result in confusing highlighting: #!/bin/sh # This file is for test purposes only. # There's no other reason - # hence do not use it. echo "This test's not right!" exit 0 #_EoF_ The problem here starts with the apostrophe in line 3. Besides the wrong keyword handling mentioned above the recognition of //strings// is broken as well: The text 's no other reason - # hence do not use it. echo "This test' is handled as one string and the text " exit 0 #_EoF_ (i.e. up to the end of the fragment) is handled as a second string. Since this kind of errors may span several lines (and can very well mixup the whole highlighting as the following fragment shows) this plugin can't do anything about it((apart from replacing the whole "''bash''" handling to bypass //GeSHi// completely, that is)). The only 'workaround' is to manually replace the apostrophes (i.e. write //"''it is''"// instead of //"''it's''"//) and single doublequotes((which cause the same desaster but are less likely to occur)). #!/bin/sh # It's not gonna work ... echo "This test's not right as well for all I know." # What's going on? echo "I'd rather use something else ..." # It's disturbing, isn't it? #_EoF_ A general problem with the //GeSHi// "''bash''" module is that is treats a lot of words as keywords which in fact are **not** "''bash''" keywords. It seems that someone just dumped his ''/bin/'' directories saying: "All this tools and programs are shell keywords"((GeSHi's 1.0.7.20 version therefor should be avoided -- or at least clean the ''KEYWORDS[2]'' array completely (lines 64 - 118 inclusive of the "''bash.php''"" file) because none of its entries holds any ''bash'' specific keyword)). Which, of course, is plain wrong. Whoever is responsible for that module should just take a look at the ''bash'' man page to see which words and symbols are keywords actually. -- Anyway, the consequence of all the bugs mentioned is that the //GeSHi// "''bash''" module is not usable for serious work. It causes more confusion than help. **Update:** The [[http://qbnz.com/highlighter/|GeSHi]] version 1.0.7.21 (2008-03-23) seems to have at least the comment bug fixed. You might want to try installing that version over the one that came with your DokuWiki if that was an older version. ==== UTF-8 ==== Another problem is that the //GeSHi// engine has a bug which causes raw UTF-8 character sequences in the text to highlight to end up illegible in some cases((The obvious reason seems to be that //GeSHi// undiscriminatingly uses PHP's builtin ''htmlspecialchars()'' function which handles UTF-8 characters only in PHP 4.3 and greater thus making it unusable for e.g. widely used RH9 installations and hosted systems with PHP 4.2 installed.)). As a workaround you could manually "entityfy" those characters in your page's text. ===== Backwards compatibility ===== Prior to the 2007-02-19 release of this plugin the [[#behaviour]] didn't work with DokuWiki releases older than 2006-03-05((The reason being that older DokuWiki versions didn't support plugin related CSS and JavaScript files.)). But obviously there are still quite a few older versions in production use((As far as it was mentioned the reason to avoid updating is that newer DokuWiki releases are using PHP functions not available with the respective OS/PHP combination.)). To support such systems I enhanced the plugin to include the CSS and JavaScript in such cases. I've tested it successfully with a 2005-07-30 version of DokuWiki, but please let me know if there are issues with other releases. You should be aware that the resulting XHTML code with such DokuWiki releases does not validate((while working, actually, at least with Firefox, Opera and M$I€)) because the current design of DokuWiki doesn't provide a way to add markup to a page's ''head'' section((This is not a problem specific to this plugin but a general one with DokuWiki. Unfortunately its software architecture is poorly designed and a rampant mix of global variables, functions and objects -- a characteristic it shares with a lot of other CMSs out there in the wild.)). Hence the only way to transport the plugin's CSS to the user's browser is by embedding it in the page which, alas, renders the page formally invalid. The only workaround is to (a) append the plugin's CSS file to DokuWiki's main CSS file and (b) remove (delete) the plugin's CSS file e.g.: $> cat lib/plugins/code/style.css >> lib/tpl/default/design.css $> rm -f lib/plugins/code/style.css $> _ ===== See also ===== There was another code plugin with different aims. Check its [[plugin:code|docs]] to see whether it will fit your needs. ==== Plugins by the same author ==== * [[bomfix|BOMfix Plugin]] -- ignore Byte-Order-Mark characters in your pages * [[code2|Code Syntax Plugin]] -- use syntax highlighting of code fragments in your pages * [[deflist|Definition List Syntax Plugin]] -- use the only complete definition lists in your pages * [[diff|Diff Syntax Plugin]] -- use highlighting of diff files (aka "patches") in your pages((obsoleted by incorporating its ability into the [[code2|Code]] plugin)) * [[hr|HR Syntax Plugin]] -- use horizontal rules in nested block elements of your pages * [[lang|LANGuage Syntax Plugin]] -- markup different languages in your pages * [[lists|Lists Syntax Plugin]] -- use the only complete un-/ordered lists in your pages * [[nbsp|NBSP Syntax Plugin]] -- use Non-Breakable-Spaces in your pages * [[nstoc|NsToC Syntax Plugin]] -- use automatically generated namespace indices * [[shy|Shy Syntax Plugin]] -- use soft hyphens in your pages * [[tip|Tip Syntax Plugin]] -- add hint areas to your pages ===== Discussion ===== Hints, comments, suggestions ...\\ //(Please add new issues at the document's end.)// Thank you for the last update ;-) > You're welcome! Hope you'll like the new [[#behaviour|folding feature]] as well.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-02-12 12:25// ---- Hello Matthias, how can I change the default behaviour of the header/footer line * a "''h''" (for //"header"//)((or "''t''" for //"top"//)) following immediately the pipe character causes the remaining "''text''" to get placed //above// the code, a "''f''" (for //"foot"//)((or "''b''" for //"bottom"//)) places the "''text''" below the code (note that this flag is //optional//; if omitted it defaults to "''f''"); I want to change the default to "''h''" = //"header"//. Or better is it possible to make this configurable?\\ Thanks -- Uwe Kirbach 2007-02-17 > Well, I designed the placement based on the model of books where you'll usually find something like "Listing 1" or "Example 12.4" //below// a listing. That's what readers are used to and I think it reasonable to resemble it in web pages((It's a quite important point -- but, alas, obviously unknown to many so-called "web designers" -- always to //adapt// the reader's expectations and habits but not contradict them.)). I can, however, imagine use cases where the text should appear //above// a listing and so I introduced the "''h''" and "''f''" flags to allow for controlling the text's placement. The default position ("''f''"oot) can be changed by one single keystroke ("''h''"). So, Uwe, I've to admit that I probably don't really understand your problem. Anyway, if you want to save that single keystroke you can patch the plugin's ''handle()'' method: --- syntax_plugin_code.php.orig 2007-02-23 11:33:35.000000000 +0100 +++ syntax_plugin_code.php 2007-02-23 11:53:17.000000000 +0100 @@ -595,14 +595,14 @@ $n = (int)$hits[5]; } // if if (isset($hits[7]) && ($hits[7])) { - $hits[6] = (isset($hits[6])) ? strtolower($hits[6]) . 'f' : 'f'; + $hits[6] = (isset($hits[6])) ? strtolower($hits[6]) . 'h' : 'h'; switch ($hits[6]{0}) { - case 'h': - case 't': - $ht = trim($hits[7]); + case 'b': + case 'f': + $ft = trim($hits[7]); break; default: - $ft = trim($hits[7]); + $ht = trim($hits[7]); break; } // switch if (isset($hits[8])) { About your suggestion to make the default placement configurable: I don't think it's worth it to maintain((by both //you// and the plugin)) external configuration files, implement the required program logic and sacrifice both memory and runtime just to save a single keystroke for some use cases while requiring another keystroke for the standard use case. Apart from that there is a general problem with configuration files & options: The writer preparing a page can not rely on a well defined result since someone else (i.e. the wiki admin) configures something this way or the other in files the page writer has no access to((turning the writing of a page into an experience of surprises)). -- So for the time being I'd suggest to think about it and in case you're sure apply the patch given above.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-02-18 11:44// >> Thanks, you're right about performance and complexity of configurability. The patch is enough for me. -- Uwe 2007-02-18 ---- There seems to be a bug in your highlighting. When inserting code like this: #!/bin/bash # # here's the highlighting error # ---> ... exit 1; Everything from the tick will be in red but since it is a comment, highlighting should not change. -- //Dagobert 2007-06-08// > Yes, I can confirm that problem. Please contact the [[http://qbnz.com/highlighter/|GeSHi]] developers to let them fix it. Thank you.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-06-09 18:16// >> Even worse... I think the whole commentary thing does not work. On the example above a keyword ''for'' would also be highlighted! -- //Dagobert 2007-06-11// >>> I just checked with the latest [[http://kent.dl.sourceforge.net/sourceforge/geshi/GeSHi-1.0.7.19.tar.gz|GeSHi v1.0.7.19]] but still the same. Scanning the source, however, I found that the leading DokuWiki developer is the author of the ''bash'' highlighting script as well. So probably contacting [[andi@splitbrain.org|Andreas Gohr]] directly might help.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-06-12 10:58// >>>> FYI, I have just checked it with the latest [[http://kent.dl.sourceforge.net/sourceforge/geshi/GeSHi-1.0.7.20.tar.gz|GeSHi v1.0.7.20]] but still the same.\\ --- //[[mischa_the_evil@hotmail.com|Mischa The Evil]] 2007-07-23 01:52// >>>>> I've implemented a workaround for //some// of the problems. See the [[#shell|shell]] section above.\\ --- //[[support@mwat.de|Matthias Watermann]] 2007-08-05 16:56// > The [[http://qbnz.com/highlighter/|GeSHi]] version 1.0.7.21 (2008-03-23) seems to have at least the ''bash'' comment bug fixed. You might want to try installing that version over the one that came with your DokuWiki.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008-04-05 19:25// ---- The plugin doesn't seem to work with the latest DokuWiki edition 2007-06-26b... Any ideas?\\ --- //[[tetsuo@go2.pl|Tetsuo]] 2008-02-20 23:12// > Well, if you could be a little more specific -- e.g. elaborate what "doesn't seem to work" means -- someone just might have an idea. For all I know, it works without problems on numerous 2007-06-26b installations.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008-03-05 21:26// >> I was having issues with this as well (2007-06-26b) but then I spotted something very subtle between my code and the examples. In my text I was using: SELECT blah FROM BLAH which wouldn't highlight. However changing it to: SELECT foo FROM bar did. HTH\\ --- //[[fred@rizsilverthorn.net|Fred]] 2008-03-19 13:31// >>> This is discussed in the [[#Markup]] section above, Fred. Thanks for emphasizing it.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/03/26 18:35// ---- Hi, I really enjoy that plugin. I saw another plugin witch feet to my need I'm talking about [[plugin:repo|repo]]. I would like to have both in one code2 cool features and repo capabilities to include remote code. I made quite an ugly patch for code2 to do the work, you just need to do: repo>http://my_remote_url --- //[[macfly_email@yahoo.fr|Macfly]] 2008/04/16 22:09// > Hi Macfly, please see the [[#external_files|External Files]] section above. I've incorporated that functionality with the plugin in a hopefully secure way. However, I changed the keyword to "''extern''" which seems more intuitive to me than "repo". I hope it helps nonetheless.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/05/26 10:25// ---- I made a basic patch to make code2 work with [[plugin:odt|odt]] plugin: //(obsolete patch removed)//\\ --- //[[macfly_email@yahoo.fr|Macfly]] 2008/04/16 23:47// > Thanks, Macfly, for your suggestion. Please see the [[#Open Document Format]] section above. I hope you're happy with the results.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/06/02 10:59// ---- :!: I just installed this plugin (and it's excellent). I seem to have two problems: - the hide functionality doesn't seem to work (which really isn't much of a problem, unless I'm using large code blocks) - oddly the extern> function seems to **run** php code (and markup the result)! This isn't intended, right?\\ Thanks for this great plugin! //Juice 2008-06-03// > Umh, Juice, could you explain what you mean by "doesn't seem to work" with your first point? The "''hide''" feature relies on a combination of presentation (i.e. CSS) and behaviour (i.e. JavaScript), so if one of them is disabled (or doesn't work as supposed) the respective code fragment is //shown//. And that feature only works in combination with a header/footer text (without it there wouldn't be an element to use for toggling the visibility). Could you show (or send by email if it's confidential) a wiki markup fragment that doesn't work as you expected?\\ About your second point: The "''extern>''" option highlights whatever the specified URI happens to resolve to (i.e. whatever the remote server sends back, see [[#External Files|above]]). That means if you want to highlight a PHP file that is stored on a remote server you'd have to use an URI that lets the remote server return the //source code// instead of executing it. This plugin -- or any browser, for that matter -- has no way to force a remote server to send source code of server-side scripts (in whatever language) if that server does not willingly so. //If// you have access to the remote server in question you should consider preparing some URIs for inclusion with your DokuWiki system (by means of this plugin), for example you could prepare a symbolic link (e.g. ''"ln -s thescript.php thescript.php.txt"'') and then use that (latter) name in the URI of the "''extern>''" option.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/06/05 11:26// >> okay, thanks. I've got it sorted. Thanks! //Juice 2008-08-15// ---- :!:Help me. I don't know how to install. I got download file "syntax_plugin_code.zip" and upload /lib/plugins/code. But Not Operation. What is my mistake? > It appears, anonymous, that you forgot to extract the archive's contents:''\\ $\\ $ cd /path/to/dokuwiki/lib/plugins\\ $ unzip syntax_plugin_code.zip\\ Archive:  syntax_plugin_code.zip\\   inflating: code/style.css\\   inflating: code/syntax_plugin_code.css\\   inflating: code/script.js\\   inflating: code/syntax_plugin_code.js\\   inflating: code/syntax_plugin_code_styles.xml\\   inflating: code/syntax.php\\    creating: code/img/\\  extracting: code/img/minus-11x11.gif\\  extracting: code/img/plus-11x11.gif\\ $''\\ Those two steps should do the trick.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/07/24 09:56// >> I solved this problem. I downloaded by Internet Explore 6.0. But downloaded file is crashed. Then I downloaded by Fire Fox 3.0. Then the zip file is not crashed. 8-O M$ is bad. ---- :?:Great plugin! I was only wondering if there would be a way to set the size of the box, and to turn on line wrapping.\\ For the first, what I mean is for example, sometimes you may want to see the full code block, without needing to move the bars.\\ About line wrapping, the geshi engine supports this, so that lines that exceed the width would be wrapped. This produces nice results combined with line numbering.\\ What do you think? > Well, Anonymous, obviously you want to change the [[#Presentation|presentation]] of your code. So just adjust its rules i.e. modify the CSS according to your needs. As for the code's width replace the ''"max-width: 99%;"'' with something like ''"width: 80ex;"'' in the plugin stylesheet's first selector line.\\ To let your browser wrap the code lines add the ''"white-space: normal;"'' presentation rule in the same line. You might also want to replace the ''"overflow: auto;"'' rule by ''"overflow: visible;"'' to get rid of the browser-provided scrollbars.\\ However, if you're thinking about another markup option just have a look at the [[#Usage]] section above: There's already quite a lot of possible option arguments (and their respective combinations). Adding another one would not only make it more difficult to remember the correct syntax but as well would involve (plugin) code additions which, ironically, would have to boil down to using the exact CSS rules mentioned above.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/08/17 12:40// >> Thank you Matthias for your response! Playing with the CSS I'm able to fit my needs. On the other hand, have you realized that there is a new version of geshi? I know that it is more related to the DokuWiki distribution, but as it has some interesting new features, I tried to put it into my installation.\\ It works seamlessly, but there is an interesting improvement that splits line numbers and code into two columns of a table thus allowing you to select only the code and not the line numbers. To use that, you have to change the header type: ''$geshi->set_header_type(GESHI_HEADER_PRE_TABLE)''. However this is not working, somewhere DokuWiki seems to strip out the table part of the geshi output. It would be very nice to have a syntax highlighting such as this: http://qbnz.com/highlighter/geshi-doc.html#basic-usage.\\ Thank you for your attention and your plugin! >>> Glad, that you got your problems solved. -- As about [[http://qbnz.com/highlighter/demo.php|GeSHi]] v1.0.8 I have to admit that I'm not impressed at all. Actually, I'm rather disappointed with it. Just two examples: The line numbering you mention, is implemented by generating an OL((i.e. ordered list)) thus completely changing (breaking) the semantics of every page including GeSHi processed code fragments; and for ''"bash"'' again some fool dumped an arbitrary selection of binary programs (which are _not_ and never have been shell keywords) to the keywords list which renders that module unusable for serious work.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/08/24 14:45// >> I just got that working, changing the code on syntax.php. However, I couldn't get the PRE_TABLE to display well (it shows disaligned) so I chose to use ''GESHI_HEADER_DIV'' and ''$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS,2)'' in case of having line numbers. In this latter case, I commented out the code for line numbering, just leaving this:\\ $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS,2); $geshi->set_line_style('background: #fcfcfc;', 'background: #f0f0f0;'); // For this to work, I disabled classes $aRenderer->doc .= $geshi->parse_code(); $geshi = NULL; >> I know this is not very elegant, but it works for me(tm). I should investigate further, as this breaks folding feature (no classes, no folding). >>> If it works for you it's just fine with me. However, I will not incorporate the use of broken GeSHi "features" into this plugin. In fact, I guess I have to investigate further as well and implement some kind of workaround for at least some of the problems caused by GeSHi (such as the wrong "keywords" in ''bash'' markup mentioned above).\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/08/24 14:40// ---- :!: I just realized that line numbering isn't working for me anymore..? \\ I switched to using monobook as my template a while ago (I'm not sure if this has anything to do with it). Code highlighting appears to work well, but if I add the line number option highlighting turns off... Any ideas? //Juice 2008-08-15// > Hmmm, "ideas" ... I'd //guess//, Juice, it's a problem with the template's main CSS file (e.g. too specific selectors there). You could try to replace all ''".code .lno"'' occurrences in the plugin's stylesheet by ''"pre.code .lno"'' (which makes the respective selector a little more specific) and see whether that helps. In case that doesn't work out you should provide the URL of a page demonstrating the problem. You can do this by private email if it shouldn't go public.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/08/17 12:45// ---- ==== Referencing a local file ==== This is a nice plugin, I particularly appreciate the possibility to include EXTERNAL code file specifying their URL. Is there any possibility to do the same thing with a code file that should be located in the media folder of the Wiki. I added the following code which is not very elegant: case 'wiki': $nom=substr ($n[1],5); if ($handle = @fopen($nom, 'rb')) { $aMatch[1] = ''; while (! @feof($handle)) { //XXX This might fail due to // memory constraints: $aMatch[1] .= @fread($handle, 0x8000); } // while @fclose($handle); } else { $aMatch = array($l, 'Failed to retrieve: ' . $nom); } // if break; Didier - 2008/09/03 11:59 Add ''$aMatch[1] = utf8_encode ($aMatch[1]);'' after ''@fclose($handle);'' to display **ANSI** files. //Joachim 05.11.2011// > I fail to see your point, Didier, I'm afraid. As the DokuWiki media files are accessible by their respective URLs you could use the ''"extern"'' mechanism to refer to them (see my response to Juice as of 2008/06/05 above). So why would you need just another keyword? -- Apart from that I tend to avoid foreseeable security problems (therefor the ''"file"'' URI scheme, for example, is not supported). To implement an unfiltered access to raw files in the server's filesystem doesn't seem to be a good idea to me. At least tests for (sym-)links and devices would be needed before using those files. Additionally it doesn't look good to place raw filesystem path/names in a publicly accessible resource like wiki pages.\\ --- //[[support@mwat.de|Matthias Watermann]] 2008/10/18 10:25// >> I think the idea is to be able to use internal or relative links without giving the full http link path of your own server, it took me a while to figure this out but here's one approach. \\ >> instead of extern> wiki:D:\Sites\dokuwiki\data\media\root\namespace1\namespace2\filename1.txt >> with the below patch instead you can do extern> wiki:root:namespace1:namespace2:filename1.txt >> Or if the code file your referencing is in the same namespace as the page it's being viewed from extern> wiki:.filename1.txt --- D:\Sites\dokuwiki\lib\plugins\code\syntax.php.bak 2008-07-22 11:36:40.000000000 +0100 +++ D:\Sites\dokuwiki\lib\plugins\code\syntax.php 2010-12-14 16:02:35.526048400 +0000 @@ -525,6 +525,25 @@ return 'protected'; } // getType() + + // Get the Absouloute Http path to a media file + function getFullHttpLink($dokuwikilink) { + $retval = null; + // Get the Relative link + $xhtml_renderer = p_get_renderer('xhtml'); + $xhtml_renderer->internalmedia($dokuwikilink,null,null,null,null,null,'linkonly'); + // Extract the href attribute from the relative link + $dom = new DOMDocument(); + @$dom->loadHTML($xhtml_renderer->doc); + $xpath = new DOMXPath($dom); + $tags = $xpath->query('//a'); + + foreach ($tags as $tag) { + if ($tag->hasAttribute('href')) { $retval = $tag->getAttribute('href'); } + } + return 'http://' . getenv("HTTP_HOST") . $retval; + } + /** * Handler to prepare matched data for the rendering process. * @@ -585,6 +604,24 @@ 'Failed to retrieve: ' . $n[1]); } // if break; + + case 'wiki': + $nom=substr ($n[1],5); + $nom=$this->getFullHttpLink($nom); + if ($handle = @fopen($nom, 'rb')) { + $aMatch[1] = ''; + while (! @feof($handle)) { + //XXX This might fail due to + // memory constraints: + $aMatch[1] .= @fread($handle, 0x8000); + } // while + @fclose($handle); + } else { + $aMatch = array($l, + 'Failed to retrieve: ' . substr ($n[1],5) . ' | ' . $nom); + } // if + break; + default: $aMatch = array($l, 'Unsupported URL scheme: ' . $n[0]); ** That indeed is a neat fix, working well - but sadly not for non-public wikis. Is there any way to work around that? I would like to upload c-Files and use them in wiki articles as collapsible source blocks.. ** ---- ==== XML, markup ==== Hi Matthias, great plugin, really. I have two points, which I have in my mind after long time I am using code2 plugin. First, for XML files in the comments like below the line break is inserted. Original code: what we get as the output: And the next point is: Is it possible to extend plugin in a way, that I can combine the features of colorer and add some markup on the top? For example: {{markup color=red,bold}}http://local-server.net:8080/manager{{/markup}} /rdf-manager or maybe you can give another way of how to achieve the same result with. Also I think that header position flag should be followed by a space (at least non-character), so the Regex should be fixed as the following: --- lib/plugins/code/syntax.php (revision 139) +++ lib/plugins/code/syntax.php (working copy) @@ -652,7 +652,7 @@ (?> \x7C # extract the position flag: - ([bfht])?\s* + (?:([bfht])(?>\s))?\s* # match 6 # extract the header,footer line: ([^\x7C]+) @@ -675,7 +675,7 @@ */ if (preg_match('/^\s*(?=\S)(?>(?>(diff)(?>\s+([cnrsu]?))?)|' . '(?>([a-z][^\x7C\s]*)(?>\s+(\d\d*))?)|(\d\d*)|\s*)[^\x7C]*' - . '(?>\x7C([bfht])?\s*([^\x7C]+)(?>\x7C\s*(h|s)?.*)?)?$/iu', + . '(?>\x7C(?:([bfht])(?>\s))?\s*([^\x7C]+)(?>\x7C\s*(h|s)?.*)?)?$/iu', $aMatch[0], $hits)) { unset($hits[0]); // free mem // $hits[1] = "diff" //[[dma_k@mail.ru|Dmitry Katsubo]] 2009-09-03 13:24// ==== Differentiating input and output in console ==== Hi, I just quickly hack a ''consoleio'' mode. It is strongly based on ''console''. However it lets the user add markup () to indicate what's to be input at the console, and differentiate from the program's output. Default console output is not bold anymore, but user input is. Index: code/syntax_plugin_code.css =================================================================== --- code.orig/syntax_plugin_code.css 2010-10-28 06:05:53.N +0200 +++ code/syntax_plugin_code.css 2010-10-28 07:21:01.N +0200 @@ -173,9 +173,12 @@ pre.console { background: #333 none; color: #fff; - font-weight: 900; padding: 0.4ex 0.3ex 0.6ex 0.6ex; } +div.dokuwiki pre.console span.input, +pre.console span.input { + font-weight: 900; +} div.dokuwiki pre.console .lno, pre.console .lno { background: inherit; Index: code/syntax.php =================================================================== --- code.orig/syntax.php 2010-10-28 06:05:53.N +0200 +++ code/syntax.php 2010-10-28 09:58:28.N +0200 @@ -390,6 +390,29 @@ } // _rawMarkup() /** + * Add the lines of the given $aText to the specified + * $aDoc beginning with the given $aStart linenumber. + * + * @param $aText String the text lines as prepared by handle() + * @param $addTags Boolean Used in "ODT" mode to suppress tagging + * the line numbers. + * @param $aMarker String The marker identifying user input. + * @private + * @since created 28-Oct-2010 + * @author Olivier Mehani + * @see render() + */ + function _ioTag($aText, $addTags = TRUE, $aMarker="in") { + $search = array("<$aMarker>", "</$aMarker>"); + if ($addTags) { + $replace = array("", ""); + } else { + $replace = array("", ""); + } + return str_replace($search, $replace, $aText); + } // _ioTag() + + /** * RegEx callback to replace SPAN tags in ODT mode. * * @param $aList Array A list of RegEx matches. @@ -729,6 +752,7 @@ } // if switch ($l) { case 'console': + case 'consoleio': // nothing additional to setup here break; case 'diff': @@ -857,6 +881,9 @@ if ('console' == $aData[2]) { $this->_rawMarkup($this->_entities($aData[1]), $aData[3], $aRenderer->doc, $aData[2]); + } else if ('consoleio' == $aData[2]) { + $this->_rawMarkup($this->_ioTag($this->_entities($aData[1])), + $aData[3], $aRenderer->doc, 'console'); } else if ('diff' == $aData[2]) { $this->_entities($aData[1]); $aRenderer->doc .= '
';
@@ -993,6 +1020,9 @@
                                if ('console' == $aData[2]) {
                                        $this->_rawMarkup($this->_entities($aData[1]),
                                                $aData[3], $aData[0], $aData[2], FALSE);
+                               } else if ('consoleio' == $aData[2]) {
+                                       $this->_rawMarkup($this->_ioTag($this->_entities($aData[1], FALSE)),
+                                               $aData[3], $aRenderer->doc, 'console');
                                } else if ('diff' == $aData[2]) {
                                        $this->_addDiff($this->_entities($aData[1]),
                                                $aData[3], $aData[0]);
@@ -1055,6 +1085,9 @@
                        if ('console' == $aData[2]) {
                                $aRenderer->doc .=
                                        '';
+                       } else if ('consoleio' == $aData[2]) {
+                               $aRenderer->doc .=
+                                       '';
                        } else {
                                $aRenderer->doc .=
                                        '';
Index: code/style.css
===================================================================
--- code.orig/style.css 2010-02-21 15:37:56.N +0100
+++ code/style.css      2010-10-28 06:46:36.N +0200
@@ -31,7 +31,8 @@
 .code .st0{background:inherit;color:#900;}
 .code .sy0{background:inherit;color:#6c6;}
 pre.code a{border:none;}
-div.dokuwiki pre.console,pre.console{background:#333 none;color:#fff;font-weight:900;padding:0.4ex 0.3ex 0.6ex 0.6ex;}
+div.dokuwiki pre.console,pre.console{background:#333 none;color:#fff;padding:0.4ex 0.3ex 0.6ex 0.6ex;}
+div.dokuwiki pre.console span.input,pre.console span.input{font-weight:900;}
 div.dokuwiki pre.console .lno,pre.console .lno{background:inherit;color:#cff;font-size:smaller;font-style:italic;}
 div.code{margin:0.4ex 0;padding:0.4ex 0;}
 div.code pre.code{margin:0;}

 --- [[user>shtrom]] //2010/10/28 10:10//
----

==== Two small problems ====

I'm so happy I found this plugin this morning! It's absolutely indispensable for me.

However, I found two small problems:
  - In documenting some bits of software, I used strings like ''function _fixJS()'' as (intended) footer text. What happened instead is that the starting 'f' was taken as the footer marker, and the text was displayed as ''unction _fixJS()''. :-)\\ Suggestion: add one space to string to look for (''h '' or ''f '') to decide whether the text should be header or footer.
  - When footers (or headers) appeared, they did not appear to be active to fold the code block. At first I thought it might be a conflict with the Folded plugin which I also have installed, but digging a bit deeper I found that ''script.js'' was not being included at all. When I commented out the first if statement in function ''_fixJS()'' to force the script to be included, it suddenly started working. So, it appears the plugin is not entirely compatible with the latest version of DokuWiki (which BTW uses jQuery, so script for this show/hide functionality could be a lot simpler).
So, maybe a small update is in order?

--- [[user>marjolein]] //2013-06-17 19:46//
----
==== Release 2015-08-10 "Detritus" Problems ====

**2015-08-11**

It seems that the code2 plugin isn't compatible with dokuwiki detritus anymore.

  * Here activating the code2 plugin causes blank pages in dokuwiki

 --- [[user>coastGNU]] //2015-08-11 08:43//

----
\\