angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

Renderer.php (6813B)


      1 <?php
      2 /**
      3  * A class to render Diffs in different formats.
      4  *
      5  * This class renders the diff in classic diff format. It is intended that
      6  * this class be customized via inheritance, to obtain fancier outputs.
      7  *
      8  * Copyright 2004-2010 The Horde Project (http://www.horde.org/)
      9  *
     10  * See the enclosed file COPYING for license information (LGPL). If you did
     11  * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
     12  *
     13  * @package Text_Diff
     14  */
     15 class Text_Diff_Renderer {
     16 
     17     /**
     18      * Number of leading context "lines" to preserve.
     19      *
     20      * This should be left at zero for this class, but subclasses may want to
     21      * set this to other values.
     22      */
     23     var $_leading_context_lines = 0;
     24 
     25     /**
     26      * Number of trailing context "lines" to preserve.
     27      *
     28      * This should be left at zero for this class, but subclasses may want to
     29      * set this to other values.
     30      */
     31     var $_trailing_context_lines = 0;
     32 
     33     /**
     34      * Constructor.
     35      */
     36     function __construct( $params = array() )
     37     {
     38         foreach ($params as $param => $value) {
     39             $v = '_' . $param;
     40             if (isset($this->$v)) {
     41                 $this->$v = $value;
     42             }
     43         }
     44     }
     45 
     46 	/**
     47 	 * PHP4 constructor.
     48 	 */
     49 	public function Text_Diff_Renderer( $params = array() ) {
     50 		self::__construct( $params );
     51 	}
     52 
     53     /**
     54      * Get any renderer parameters.
     55      *
     56      * @return array  All parameters of this renderer object.
     57      */
     58     function getParams()
     59     {
     60         $params = array();
     61         foreach (get_object_vars($this) as $k => $v) {
     62             if ($k[0] == '_') {
     63                 $params[substr($k, 1)] = $v;
     64             }
     65         }
     66 
     67         return $params;
     68     }
     69 
     70     /**
     71      * Renders a diff.
     72      *
     73      * @param Text_Diff $diff  A Text_Diff object.
     74      *
     75      * @return string  The formatted output.
     76      */
     77     function render($diff)
     78     {
     79         $xi = $yi = 1;
     80         $block = false;
     81         $context = array();
     82 
     83         $nlead = $this->_leading_context_lines;
     84         $ntrail = $this->_trailing_context_lines;
     85 
     86         $output = $this->_startDiff();
     87 
     88         $diffs = $diff->getDiff();
     89         foreach ($diffs as $i => $edit) {
     90             /* If these are unchanged (copied) lines, and we want to keep
     91              * leading or trailing context lines, extract them from the copy
     92              * block. */
     93             if (is_a($edit, 'Text_Diff_Op_copy')) {
     94                 /* Do we have any diff blocks yet? */
     95                 if (is_array($block)) {
     96                     /* How many lines to keep as context from the copy
     97                      * block. */
     98                     $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
     99                     if (count($edit->orig) <= $keep) {
    100                         /* We have less lines in the block than we want for
    101                          * context => keep the whole block. */
    102                         $block[] = $edit;
    103                     } else {
    104                         if ($ntrail) {
    105                             /* Create a new block with as many lines as we need
    106                              * for the trailing context. */
    107                             $context = array_slice($edit->orig, 0, $ntrail);
    108                             $block[] = new Text_Diff_Op_copy($context);
    109                         }
    110                         /* @todo */
    111                         $output .= $this->_block($x0, $ntrail + $xi - $x0,
    112                                                  $y0, $ntrail + $yi - $y0,
    113                                                  $block);
    114                         $block = false;
    115                     }
    116                 }
    117                 /* Keep the copy block as the context for the next block. */
    118                 $context = $edit->orig;
    119             } else {
    120                 /* Don't we have any diff blocks yet? */
    121                 if (!is_array($block)) {
    122                     /* Extract context lines from the preceding copy block. */
    123                     $context = array_slice($context, count($context) - $nlead);
    124                     $x0 = $xi - count($context);
    125                     $y0 = $yi - count($context);
    126                     $block = array();
    127                     if ($context) {
    128                         $block[] = new Text_Diff_Op_copy($context);
    129                     }
    130                 }
    131                 $block[] = $edit;
    132             }
    133 
    134             if ($edit->orig) {
    135                 $xi += count($edit->orig);
    136             }
    137             if ($edit->final) {
    138                 $yi += count($edit->final);
    139             }
    140         }
    141 
    142         if (is_array($block)) {
    143             $output .= $this->_block($x0, $xi - $x0,
    144                                      $y0, $yi - $y0,
    145                                      $block);
    146         }
    147 
    148         return $output . $this->_endDiff();
    149     }
    150 
    151     function _block($xbeg, $xlen, $ybeg, $ylen, &$edits)
    152     {
    153         $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen));
    154 
    155         foreach ($edits as $edit) {
    156             switch (strtolower(get_class($edit))) {
    157             case 'text_diff_op_copy':
    158                 $output .= $this->_context($edit->orig);
    159                 break;
    160 
    161             case 'text_diff_op_add':
    162                 $output .= $this->_added($edit->final);
    163                 break;
    164 
    165             case 'text_diff_op_delete':
    166                 $output .= $this->_deleted($edit->orig);
    167                 break;
    168 
    169             case 'text_diff_op_change':
    170                 $output .= $this->_changed($edit->orig, $edit->final);
    171                 break;
    172             }
    173         }
    174 
    175         return $output . $this->_endBlock();
    176     }
    177 
    178     function _startDiff()
    179     {
    180         return '';
    181     }
    182 
    183     function _endDiff()
    184     {
    185         return '';
    186     }
    187 
    188     function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
    189     {
    190         if ($xlen > 1) {
    191             $xbeg .= ',' . ($xbeg + $xlen - 1);
    192         }
    193         if ($ylen > 1) {
    194             $ybeg .= ',' . ($ybeg + $ylen - 1);
    195         }
    196 
    197         // this matches the GNU Diff behaviour
    198         if ($xlen && !$ylen) {
    199             $ybeg--;
    200         } elseif (!$xlen) {
    201             $xbeg--;
    202         }
    203 
    204         return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg;
    205     }
    206 
    207     function _startBlock($header)
    208     {
    209         return $header . "\n";
    210     }
    211 
    212     function _endBlock()
    213     {
    214         return '';
    215     }
    216 
    217     function _lines($lines, $prefix = ' ')
    218     {
    219         return $prefix . implode("\n$prefix", $lines) . "\n";
    220     }
    221 
    222     function _context($lines)
    223     {
    224         return $this->_lines($lines, '  ');
    225     }
    226 
    227     function _added($lines)
    228     {
    229         return $this->_lines($lines, '> ');
    230     }
    231 
    232     function _deleted($lines)
    233     {
    234         return $this->_lines($lines, '< ');
    235     }
    236 
    237     function _changed($orig, $final)
    238     {
    239         return $this->_deleted($orig) . "---\n" . $this->_added($final);
    240     }
    241 
    242 }