DokuWiki

It's better when it's simple

User Tools

Site Tools


plugin:diff

Diff Syntax PlugIn

Compatible with DokuWiki

2005-07-13+

plugin Format diff output

Last updated on
2007-08-15
Provides
Syntax

This extension has not been updated in over 2 years. It may no longer be maintained or supported and may have compatibility issues.

Similar to code2

Tagged with code, revisions, syntaxhighlight

The diff tool is a handy commandline program1) to find differences between two files; it's available and widely in use2) for about two decades. This dif­fe­ren­ces are often called patches and in turn are used to modify other un­patched source files. It doesn't matter whether it's source code of some programming language or an HTML page or an XML document or … As long as it is a text file3) one can produce – and distribute if appropriate – a patch file4). Instead of being forced to manually type in the differences (e.g. mo­di­fied source code) one can simply pass the output of diff to the patch tool; hence even hun­dreds or thou­sands of code changes can be applied by a simple one-liner:
patch < patchfile

This plugin not only allows to insert such a patchfile into your Wiki page5) but provides syntax highlighting6) thus making it easier to read and faster to re­cog­nize the actual changes a patch offers. It is intended for developers wan­ting to document and/or distribute changes they've made. The plugin handles besides the most often used unified output format also the context out­put for­mat as well as the less common RCS and simple (default) output formats.

:!: Note, there's a replacement plugin for the DokuWiki's builtin code tag which provides the same functionality as this plugin. So if you've installed that code plugin you won't need this one!

Usage

To actually use this plugin just place the output of the diff tool between the
<diff>

</diff>
tags in your Wiki file (see the examples below). Both the opening and closing tag must stand on its own line before and behind the diff output. – In prac­tice you would do something like
diff -u sourcefile.old sourcefile.current > sourcefile.patch
to write the differences between sourcefile.old and sourcefile.current to the file sourcefile.patch. The latter (surrounded by <diff> | </diff>) will then be pasted into your Wiki page offering/documenting that very patch.

The plugin understands not only the unified output format but the context and RCS formats as well as the default simple format. To speed up operation you should use
<diff simple> or just <diff s>
to start a simple patch7) or
<diff context> or just <diff c>
to start a context patch8) or
<diff RCS> or just <diff r>
to start a context patch9) or
<diff unified> or just <diff u>
to start a unified patch10).

By providing such a hint some RegEx tests are avoided and hence both me­mo­ry and run­time are saved.

Examples

For demonstration purposes consider the following HTML fragment:

  <p>Aenean ligula mi, feugiat nec, eleifend ac,
  ullamcorper id, velit. Donec at orci in leo nonummy
  fermentum. Vestibulum pellentesque wisi sed magna.
  This line will get deleted. Elementum mattis nullam.
  vehicula gravida tortor. Donec vel ante quis ipsum
  dapibus. Cras ultrices. Sed egestas neque nec augue
  congue ornare. This line will get modified.
  Sed sit amet lectus ac nibh porta mollis. Aenean justo.
  Pellentesque nonummy nunc eu lacus. Praesent tincidunt
  mollis wisi donec id est. Proin odio wisi, ornare vitae,
  scelerisque non, ultrices vel, dolor. Vestibulum ante
  ipsum primis in faucibus orci luctus et ultrices posuere
  cubilia Curae; there will be an addition!
  Nulla nec risus. Aliquam erat volutpat. Integer non dui
  id justo sodales dignissim. In molestie suscipit ipsum.
  Donec nonummy mauris id justo.</p>

After review by several people there were some changes to be made:

  <p>Aenean ligula mi, feugiat nec, eleifend ac,
  ullamcorper id, velit. Donec at orci in leo nonummy
  fermentum. Vestibulum pellentesque wisi sed magna.
  vehicula gravida tortor. Donec vel ante quis ipsum
  dapibus. Cras ultrices. Sed egestas neque nec augue
  congue ornare. This line is now modified as requested.
  Sed sit amet lectus ac nibh porta mollis. Aenean justo.
  Pellentesque nonummy nunc eu lacus. Praesent tincidunt
  mollis wisi donec id est. Proin odio wisi, ornare vitae,
  scelerisque non, ultrices vel, dolor. Vestibulum ante
  ipsum primis in faucibus orci luctus et ultrices posuere
  cubilia Curae; there will be an addition!
  Elementum mattis nullam. Maecenas egestas posuere wisi.
  Nulla nec risus. Aliquam erat volutpat. Integer non dui
  id justo sodales dignissim. In molestie suscipit ipsum.
  Donec nonummy mauris id justo.</p>

Now lets see the various diff formats11). There are many options for both the diff and patch programs. For more details about them please refer to the respective man pages of your beloved GNU/Linux (or legacy CygWin/WinDOS) system. I have ordered the diff results here by quantity of information.

Unified diff

	diff -u lorem.orig lorem.new

produces:

--- lorem.orig	2007-01-12 14:30:42.000000000 +0100
+++ lorem.new	2007-01-12 14:44:26.000000000 +0100
@@ -1,16 +1,16 @@
   <p>Aenean ligula mi, feugiat nec, eleifend ac,
   ullamcorper id, velit. Donec at orci in leo nonummy
   fermentum. Vestibulum pellentesque wisi sed magna.
-  This line will get deleted. Elementum mattis nullam.
   vehicula gravida tortor. Donec vel ante quis ipsum
   dapibus. Cras ultrices. Sed egestas neque nec augue
-  congue ornare. This line will get modified.
+  congue ornare. This line is now modified as requested.
   Sed sit amet lectus ac nibh porta mollis. Aenean justo.
   Pellentesque nonummy nunc eu lacus. Praesent tincidunt
   mollis wisi donec id est. Proin odio wisi, ornare vitae,
   scelerisque non, ultrices vel, dolor. Vestibulum ante
   ipsum primis in faucibus orci luctus et ultrices posuere
   cubilia Curae; there will be an addition!
+  Elementum mattis nullam. Maecenas egestas posuere wisi.
   Nulla nec risus. Aliquam erat volutpat. Integer non dui
   id justo sodales dignissim. In molestie suscipit ipsum.
   Donec nonummy mauris id justo.</p>

This is the most popular format. It contains filenames and -dates. It tells the starting line and length of a modified block. There are only added lines (mar­ked by “+”) and deleted ones (marked by “-”).

The differences are scannable by both the patch program and humans.

Context diff

	diff -c lorem.orig lorem.new

produces:

*** lorem.orig	2007-01-12 14:30:42.000000000 +0100
--- lorem.new	2007-01-12 14:44:26.000000000 +0100
***************
*** 1,16 ****
    <p>Aenean ligula mi, feugiat nec, eleifend ac,
    ullamcorper id, velit. Donec at orci in leo nonummy
    fermentum. Vestibulum pellentesque wisi sed magna.
-   This line will get deleted. Elementum mattis nullam.
    vehicula gravida tortor. Donec vel ante quis ipsum
    dapibus. Cras ultrices. Sed egestas neque nec augue
!   congue ornare. This line will get modified.
    Sed sit amet lectus ac nibh porta mollis. Aenean justo.
    Pellentesque nonummy nunc eu lacus. Praesent tincidunt
    mollis wisi donec id est. Proin odio wisi, ornare vitae,
    scelerisque non, ultrices vel, dolor. Vestibulum ante
    ipsum primis in faucibus orci luctus et ultrices posuere
    cubilia Curae; there will be an addition!
    Nulla nec risus. Aliquam erat volutpat. Integer non dui
    id justo sodales dignissim. In molestie suscipit ipsum.
    Donec nonummy mauris id justo.</p>
--- 1,16 ----
    <p>Aenean ligula mi, feugiat nec, eleifend ac,
    ullamcorper id, velit. Donec at orci in leo nonummy
    fermentum. Vestibulum pellentesque wisi sed magna.
    vehicula gravida tortor. Donec vel ante quis ipsum
    dapibus. Cras ultrices. Sed egestas neque nec augue
!   congue ornare. This line is now modified as requested.
    Sed sit amet lectus ac nibh porta mollis. Aenean justo.
    Pellentesque nonummy nunc eu lacus. Praesent tincidunt
    mollis wisi donec id est. Proin odio wisi, ornare vitae,
    scelerisque non, ultrices vel, dolor. Vestibulum ante
    ipsum primis in faucibus orci luctus et ultrices posuere
    cubilia Curae; there will be an addition!
+   Elementum mattis nullam. Maecenas egestas posuere wisi.
    Nulla nec risus. Aliquam erat volutpat. Integer non dui
    id justo sodales dignissim. In molestie suscipit ipsum.
    Donec nonummy mauris id justo.</p>

Here are filenames and dates transported as well as the starting line and modified range. There are three kind of changes: removed lines (“-”), chan­ged lines (“!”) and added lines (“+”).

But there are two “portions” of text you have to check get the overall picture: The first indicating removed lines and somehow changed ones; the second holding the actual chan­ges and additions.

Simple diff

	diff lorem.orig lorem.new

produces:

4d3
<   This line will get deleted. Elementum mattis nullam.
7c6
<   congue ornare. This line will get modified.
---
>   congue ornare. This line is now modified as requested.
13a13
>   Elementum mattis nullam. Maecenas egestas posuere wisi.

This format transports by default neither filenames nor -dates. There are two kind of changes: Removed lines (marked “<”) and added lines (marked “>”).

Besides the raw differences there's no context information which makes it hard for the patch program to use such a patchfile12) if the file to patch is only slightly modified13). Most probably such a patch will fail.

RCS diff

	diff -n lorem.orig lorem.new

produces:

d4 1
d7 1
a7 1
  congue ornare. This line is now modified as requested.
a13 1
  Elementum mattis nullam. Maecenas egestas posuere wisi.

As with the simple format there are neither filenames nor -dates. Removed lines are marked by “d” followed by linenumber and the number of deleted lines. Added lines are marked “a” followed by linenumber and the number of added lines.

This is the most compact patch format. But since no context is provided it not only makes it hard for humans to read and understand.

Nowadays, with distributed development and parallel source modifications by different persons, it's rather useless. In contrast the unified output format (as well as the – larger – context format) provides some context lines14) allowing the patch tool to synchronize the patchfile with the file to patch. Apart from that the unified format makes it easier for humans to figure out the mea­ning.

Installation

It's quite easy to integrate this plugin with your DokuWiki:

  1. Download the source archive (~5KB) and un­pack it in your Doku­Wiki plugin di­rec­to­ry {dokuwiki}/lib/plugins; this will create the di­rec­tory {dokuwiki}/lib/plugins/diff.
  2. Make sure both the new direc­tory and the files therein are read­able by the web-server e.g.
    	chown apache:apache dokuwiki/lib/plugins/* -Rc

You might as well use the plugin manager for installing or updating this plugin.

Plugin Source

Here comes the GPLed PHP source15) for those who'd like to scan it be­fore actu­ally in­stal­ling it:

<?php
if (! class_exists('syntax_plugin_diff')) {
  if (! defined('DOKU_PLUGIN')) {
    if (! defined('DOKU_INC')) {
      define('DOKU_INC', realpath(dirname(__FILE__) . '/../../') . '/');
    } // if
    define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
  } // if
  // Include parent class:
  require_once(DOKU_PLUGIN . 'syntax.php');
 
/**
 * <tt>syntax_plugin_diff.php </tt>- A PHP4 class that implements a
 * plugin for highlighting <tt>diff</tt> output in <tt>DokuWiki</tt>
 * pages.
 *
 * <p>
 * The purpose of this plugin is to provide a facility for inserting
 * a <tt>diff</tt> file into a Wiki page. While this could be done by
 * using the <tt>code</tt> tag this plugin additionally provides some
 * visual feedback (so-called "syntax highlighting") by emphasizing
 * added/deleted lines using CSS rules.
 * </p>
 * <p>
 * Three types of <tt>diff</tt> output formats are supported:
 * </p>
 * <dl>
 * <dt><tt>unified</tt></dt>
 * <dd>The output of the <tt>diff</tt> program with the <tt>-u</tt>
 * commandline format option.</dd>
 * <dt><tt>context</tt></dt>
 * <dd>The output of the <tt>diff</tt> program with the <tt>-c</tt>
 * commandline format option.</dd>
 * <dt><tt>context</tt></dt>
 * <dd>The output of the <tt>diff</tt> program with the <tt>-n</tt>
 * commandline format option.</dd>
 * <dt><tt>simple</tt></dt>
 * <dd>The output of the <tt>diff</tt> program without any commandline
 * format option.</dd>
 * </dl><pre>
 *  Copyright (C) 2005, 2007 DFG/M.Watermann, D-10247 Berlin, FRG
 *      All rights reserved
 *    EMail : &lt;support@mwat.de&gt;
 * </pre>
 * <div class="disclaimer">
 * 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
 * <a href="http://www.gnu.org/licenses/gpl.html">version 3</a> of the
 * License, or (at your option) any later version.<br>
 * 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.
 * </div>
 * @author <a href="mailto:support@mwat.de">Matthias Watermann</a>
 * @version <tt>$Id: syntax_plugin_diff.php,v 1.10 2007/08/15 12:36:19 matthias Exp $</tt>
 * @since created 14-Aug-2005
 */
class syntax_plugin_diff extends DokuWiki_Syntax_Plugin {
 
  /**
   * @privatesection
   */
  //@{
 
  /**
   * 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
   * <tt>doc</tt> property.
   * @return Boolean <tt>TRUE</tt>.
   * @private
   * @see render()
   */
  function _addDiff(&$aText, &$aFormat, &$aDoc) {
    // Since we're inside a PRE block we need the leading LFs:
    $ADD = "\n" . '<span class="diff-addedline">';
    $DEL = "\n" . '<span class="diff-deletedline">';
    $HEAD = "\n" . '<span class="diff-blockheader">';
    $CLOSE = '</span>';
    // 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()
 
  //@}
  /**
   * @publicsection
   */
  //@{
 
  /**
   * Tell the parser whether the plugin accepts syntax mode
   * <tt>$aMode</tt> within its own markup.
   *
   * <p>
   * This method returns <tt>FALSE</tt> since no other (DokuWiki)
   * types are allowed within a <tt>diff</tt> section.
   * </p>
   * @param $aMode String The requested syntaxmode.
   * @return Boolean <tt>FALSE</tt> (always).
   * @public
   */
  function accepts($aMode) {
    return FALSE;
  } // accepts()
 
  /**
   * Connect lookup pattern to lexer.
   *
   * @param $aMode String The desired rendermode.
   * @public
   * @see render()
   */
  function connectTo($aMode) {
    $this->Lexer->addEntryPattern(
      '\x3Cdiff(?=[^\n\r]*?\x3E.*?\n\x3C\x2Fdiff\x3E)',
      $aMode, 'plugin_diff');
  } // connectTo()
 
  /**
   * Get an associative array with plugin info.
   *
   * <p>
   * The returned array holds the following fields:
   * <dl>
   * <dt>author</dt><dd>Author of the plugin</dd>
   * <dt>email</dt><dd>Email address to contact the author</dd>
   * <dt>date</dt><dd>Last modified date of the plugin in
   * <tt>YYYY-MM-DD</tt> format</dd>
   * <dt>name</dt><dd>Name of the plugin</dd>
   * <dt>desc</dt><dd>Short description of the plugin (Text only)</dd>
   * <dt>url</dt><dd>Website with more information on the plugin
   * (eg. syntax description)</dd>
   * </dl>
   * @return Array Information about this plugin class.
   * @public
   * @static
   */
  function getInfo() {
    return array(
      'author' =>  'Matthias Watermann',
      'email' =>  'support@mwat.de',
      'date' =>  '2007-08-15',
      'name' =>  'diff Syntax Plugin',
      'desc' =>  'Add diff Style  [<diff> ... </diff>]',
      'url' =>  'http://www.dokuwiki.org/plugin:diff');
  } // getInfo()
 
  /**
   * Define how this plugin is handled regarding paragraphs.
   *
   * <p>
   * This method is important for correct XHTML nesting. It returns
   * one of the following values:
   * </p>
   * <dl>
   * <dt>normal</dt><dd>The plugin can be used inside paragraphs.</dd>
   * <dt>block</dt><dd>Open paragraphs need to be closed before
   * plugin output.</dd>
   * <dt>stack</dt><dd>Special case. Plugin wraps other paragraphs.</dd>
   * </dl>
   * @return String <tt>'block'</tt> .
   * @public
   * @static
   */
  function getPType() {
    return 'block';
  } // getPType()
 
  /**
   * Where to sort in?
   *
   * <p>
   * This method returns <tt>174</tt> an arbitrary value between
   * <tt>Doku_Parser_Mode_unformatted</tt> and
   * <tt>Doku_Parser_Mode_php</tt> (180).
   * </p>
   * @return Integer <tt>174</tt>.
   * @public
   * @static
   */
  function getSort() {
    return 174;
  } // getSort()
 
  /**
   * Get the type of syntax this plugin defines.
   *
   * @return String <tt>'protected'</tt>.
   * @public
   * @static
   */
  function getType() {
    return 'protected';
  } // getType()
 
  /**
   * Handler to prepare matched data for the rendering process.
   *
   * <p>
   * The <tt>$aState</tt> parameter gives the type of pattern which
   * triggered the call to this method:
   * </p><dl>
   * <dt>DOKU_LEXER_ENTER</dt>
   * <dd>a pattern set by <tt>addEntryPattern()</tt></dd>
   * <dt>DOKU_LEXER_EXIT</dt>
   * <dd> a pattern set by <tt>addExitPattern()</tt></dd>
   * <dt>DOKU_LEXER_UNMATCHED</dt>
   * <dd>ordinary text encountered within the plugin's syntax mode
   * which doesn't match any pattern.</dd>
   * </dl>
   * @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 <tt>[0]</tt> holds the current
   * <tt>$aState</tt>, index <tt>[1]</tt> the diff type (i.e. either
   * <tt>'c'</tt> for 'context' format, <tt>'u'</tt> for 'unified'
   * format or <tt>'s'</tt> for the 'simple' format) and
   * index <tt>[2]</tt> holding the diff's patch text.
   * @public
   * @see render()
   * @static
   */
  function handle($aMatch, $aState, $aPos, &$aHandler) {
    if (DOKU_LEXER_UNMATCHED == $aState) {
      $aMatch = explode('>', $aMatch, 2);
      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
    } else {
      return array($aState);
    } // if
    $aMatch[0] = strtolower(trim($aMatch[0])) . '?';
    switch ($aMatch[0] = $aMatch[0]{0}) {
      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'
        $aMatch[0] = 'n';
        break;
      default:  // try to figure out the diff format actually used
        if (preg_match(
          '|\n(?:\x2A{5,}\n\x2A{3}\s[1-9]+.*?\x2A{4}\n.+?)+|s',
          $aMatch[1])) {
          $aMatch[0] = 'c';
        } else if (preg_match(
          '|\n@@\s\-[0-9]+,[0-9]+[ \+,0-9]+?@@\n.+\n|s',
          $aMatch[1])) {
          $aMatch[0] = '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 similar (this one is slightly more specific)
          $aMatch[0] = 'n';
        } else if (preg_match(
          '|\n(?:[0-9a-z]+(?:,[0-9a-z]+)*)(?:[^\n]*\n.*?)+|',
          $aMatch[1])) {
          $aMatch[0] = 's';
        } else {
          $aMatch[0] = '?';
        } // if
    } // switch
    return array($aState, $aMatch[0], str_replace(
      array('&', '<', '>', "\t"),
      array('&#38;', '&#60;', '&#62;', '    '),
      $aMatch[1]));
  } // handle()
 
  /**
   * Add exit pattern to lexer.
   *
   * @public
   */
  function postConnect() {
    $this->Lexer->addExitPattern('(?<=\n)\x3C\x2Fdiff\x3E', 'plugin_diff');
  } // postConnect()
 
  /**
   * Handle the actual output creation.
   *
   * <p>
   * The method checks for the given <tt>$aMode</tt> and returns
   * <tt>FALSE</tt> when a mode isn't supported. <tt>$aRenderer</tt>
   * contains a reference to the renderer object which is currently
   * handling the rendering. The contents of <tt>$aData</tt> is the
   * return value of the <tt>handle()</tt> method.
   * </p>
   * @param $aFormat String The output format to being tendered.
   * @param $aRenderer Object A reference to the renderer object.
   * @param $aData Array The data created by the <tt>handle()</tt>
   * method.
   * @return Boolean <tt>TRUE</tt> if rendered correctly, or
   * <tt>FALSE</tt> otherwise.
   * @public
   * @see handle()
   */
  function render($aFormat, &$aRenderer, &$aData) {
    if ('xhtml' != $aFormat) {
      return FALSE;
    } // if
    switch ($aData[0]) {
      case DOKU_LEXER_UNMATCHED:
        return $this->_addDiff($aData[2], $aData[1], $aRenderer->doc);
      case DOKU_LEXER_ENTER:
        $aRenderer->doc .= '<pre class="code diff">';
        return TRUE;
      case DOKU_LEXER_EXIT:
        $aRenderer->doc .= '</pre>';
      default:
        return TRUE;
    } // switch
  } // render()
 
  //@}
} // class syntax_plugin_diff
} // if
//Setup VIM: ex: et ts=2 enc=utf-8 :
?>

Presentation

The accompanying CSS presentation rules:

.diff .diff-addedline{background:#cfc none;color:#000;}
.diff .diff-deletedline{background:#fcc none;color:#000;}
.diff .diff-blockheader{background:#ccf none;color:#000;}

This shows added lines with green and removed lines with red background, while the files/locations get a blue background.

Of course, you're free to modify this styles16) to suit your personal needs or aesthe­tics17).

Changes

2007-08-15:
* added GPL link and fixed some doc problems;

2007-02-02:
* made some REs nonpossessive in '_addDiff()';

2007-01-12:
+ added 'r' option for 'diff' highlighting;

2007-01-10:
+ implemented (recognition/highlighting of) 'RCS' diff format;

2007-01-05:
* modified 'handle()' to recognize DIFFs w/o leading filenames;
* replaced IF/ELSE blocks in 'render()' by a single 'switch' statement;
* moved entity encoding from 'render()' to 'handle()';

2005-09-06:
+ added highlighting of diff commandlines (in multi-file patches);
* improved handling of context diffs in multi-file patches;

2005-09-01:
* changed type from 'container' to 'protected';
* minor internal changes;

2005-08-28:
* modifications to improve recognition of context-diffs and multi-file patches;

2005-08-17:
+ implemented 'context' and 'simple' diff formats;

Matthias Watermann 2007-08-15

See also

There's a Code plugin which incorporates the functionality of this plugin; so if you've installed that one you won't need this.

The GNU utilities for Win32 provide diff and patch programs for those who are still forced to use proprietary WinDOS systems.

A GUI for diff is provided by e.g. KDiff3.

Plugins by the same author

Discussion

Hints, comments, suggestions …

Am I right in thinking that this plugin is irrelevant to those of us who do not have command line access to a *nix server? — ta' lajzar 2005-08-16 13:07
No, this plugin allows for inserting a patchfile into a wiki page; it does not create any diff files (and therefore doesn't need that binary). Apart from that: (1) There are diff binaries for WinDOS-based systems as well. (2) Wherever software development is undertaken patches created by diff are distributed (which you could publish with this plugin in your wiki installation).
Matthias Watermann 2005-08-16 16:18:27

Discussion

diff syntax highlighting is already provided for DokuWiki by GeSHi. Couldn't this be handled more generally by using the source plugin? — Christopher Smith 2005-08-16 12:24
Neither my DokuWiki installation (2005-07-13) nor the GeSHi page above mentions diff (however, the latter does support DIV). Would you kindly enlighten me, please?
Matthias Watermann 2005-08-16 12:39:07
diff was added with GeSHi 1.0.7.1 which is currently available in the development version of DokuWiki (see example below) — Andreas Gohr 2005-08-16 12:59
204c204,206
<             $data[1]);
---
>             str_replace(array('&','<', '>'),
>               array('&#38;', '&#60;', '&#62;'),
>               $data[1]));
Well, I downloaded that GeSHi archive. Its diff module gives, er, room for enhancement to say the least. IOW: Only the simplest diff output is recognized but neither the context nor the unified output formats which are by far more relevant/important. If you'll read the source you'll finds comments stating that it doesn't work. But, of course, if it suits your needs, it's OK. — Matthias Watermann 2005-08-16 16:20:42
I suggest to improve the GeSHi module instead of implementing this through a DokuWiki plugin. — Andreas Gohr 2005-08-17 20:11
Obviously that's not possible, as far as I can see. Since the diff output formats (e.g. simple, context, unified) are using different semantics to express the changes and provide different levels of information there must be kind of a “switch” to use one format or another. The GeSHi architecture seems unable to do that; probably one would have to write a module for each of the diff output formats.
Matthias Watermann 2005-08-31 12:33
Well it's not meant to be able to do that ;). In reality GeSHi is for highlighting languages, not outputs. So there's nothing wrong with splitting them into diff-unified, diff-context etc. etc. Admittedly GeSHi isn't too great for diff anyway at this time, although this will be fixed when GeSHi 1.2 is out — Nigel McNie
I've updated the plugin which now supports all three diff output formats (see →above).
Matthias Watermann 2005-08-17 22:52:21


1)
used for ages on *NIX and GNU/Linux systems; for WinDOS see e.g. the 32 bit GNU Diff and Patch programs (I haven't tried them myself since I'm mainly using GNU/Linux for over ten years now).
2)
the same is true for the CVS system's cvs diff command
3)
i.e. not a binary file produced by a bloated text processing software
4)
not to be confused with so-called “patches” distributed by a notorious company which are in fact not patches but binary file replacement packages
5)
which could be done easily by use of DokuWiki's <code> tag
6)
which can not be done using the builtin <code> tag because the underlying GeShi syntax highlighter doesn't support the various diff output formats
7)
created by diff without a format commandline switch
8)
created by the diff commandline switch -c
9)
created by the diff commandline switch -n (sic!) – therefor this plugin allows for using the n character (instead of r) as well
10)
created by the diff commandline switch -u
11)
You should have my Code plugin installed for proper highlighting
12)
For proper operation, the patch program typically needs at least two lines of context.
13)
i.e. some line(s) were added, modified or removed prior to applying the patch
14)
the number of which can be controlled by a commandline argument
15)
The comments within the source file are suitable for the OSS doxygen tool, a do­cu­men­ta­tion sy­stem for C++, C, Java, Ob­jec­tive-C, Python, IDL and to some extent PHP, C#, and D. — Since I'm working with dif­fe­rent pro­gram­ming lan­gua­ges it's a great ease to have one tool that handles the docs for all of them.
16)
The source archive contains a commented and indented stylesheet for your in­for­ma­tion.
17)
Just be careful when modifying a CSS file: both the order and the selector grou­pings are im­por­tant for CSS to work as intended/expected.
18)
obsoleted by incorporating its ability into the Code plugin
plugin/diff.txt · Last modified: 2013-03-23 23:29 by 91.64.94.195