includes/diffengine/Renderer/inline.php
changeset 1 fe660c52c48f
child 1227 bdac73ed481e
equal deleted inserted replaced
0:902822492a68 1:fe660c52c48f
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * "Inline" diff renderer.
       
     5  *
       
     6  * This class renders diffs in the Wiki-style "inline" format.
       
     7  *
       
     8  * $Horde: framework/Text_Diff/Diff/Renderer/inline.php,v 1.16 2006/01/08 00:06:57 jan Exp $
       
     9  *
       
    10  * @author  Ciprian Popovici
       
    11  * @package Text_Diff
       
    12  */
       
    13 class Text_Diff_Renderer_inline extends Text_Diff_Renderer {
       
    14 
       
    15     /**
       
    16      * Number of leading context "lines" to preserve.
       
    17      */
       
    18     var $_leading_context_lines = 10000;
       
    19 
       
    20     /**
       
    21      * Number of trailing context "lines" to preserve.
       
    22      */
       
    23     var $_trailing_context_lines = 10000;
       
    24 
       
    25     /**
       
    26      * Prefix for inserted text.
       
    27      */
       
    28     var $_ins_prefix = '<ins>';
       
    29 
       
    30     /**
       
    31      * Suffix for inserted text.
       
    32      */
       
    33     var $_ins_suffix = '</ins>';
       
    34 
       
    35     /**
       
    36      * Prefix for deleted text.
       
    37      */
       
    38     var $_del_prefix = '<del>';
       
    39 
       
    40     /**
       
    41      * Suffix for deleted text.
       
    42      */
       
    43     var $_del_suffix = '</del>';
       
    44 
       
    45     /**
       
    46      * Header for each change block.
       
    47      */
       
    48     var $_block_header = '';
       
    49 
       
    50     /**
       
    51      * What are we currently splitting on? Used to recurse to show word-level
       
    52      * changes.
       
    53      */
       
    54     var $_split_level = 'words';
       
    55 
       
    56     function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
       
    57     {
       
    58         return $this->_block_header;
       
    59     }
       
    60 
       
    61     function _startBlock($header)
       
    62     {
       
    63         return $header;
       
    64     }
       
    65 
       
    66     function _lines($lines, $prefix = ' ', $encode = true)
       
    67     {
       
    68         if ($encode) {
       
    69             array_walk($lines, array(&$this, '_encode'));
       
    70         }
       
    71 
       
    72         if ($this->_split_level == 'words') {
       
    73             return implode('', $lines);
       
    74         } else {
       
    75             return implode("\n", $lines) . "\n";
       
    76         }
       
    77     }
       
    78 
       
    79     function _added($lines)
       
    80     {
       
    81         array_walk($lines, array(&$this, '_encode'));
       
    82         $lines[0] = $this->_ins_prefix . $lines[0];
       
    83         $lines[count($lines) - 1] .= $this->_ins_suffix;
       
    84         return $this->_lines($lines, ' ', false);
       
    85     }
       
    86 
       
    87     function _deleted($lines, $words = false)
       
    88     {
       
    89         array_walk($lines, array(&$this, '_encode'));
       
    90         $lines[0] = $this->_del_prefix . $lines[0];
       
    91         $lines[count($lines) - 1] .= $this->_del_suffix;
       
    92         return $this->_lines($lines, ' ', false);
       
    93     }
       
    94     
       
    95     function _context($lines)
       
    96     {
       
    97         return "<!-- Start context -->\n<tr><td></td><td class=\"diff-context\">".$this->_lines($lines).'</td></tr>'."\n<!-- End context -->\n\n";
       
    98     }
       
    99 
       
   100     function _changed($orig, $final)
       
   101     {
       
   102         /* If we've already split on words, don't try to do so again - just
       
   103          * display. */
       
   104         if ($this->_split_level == 'words') {
       
   105             $prefix = '';
       
   106             while ($orig[0] !== false && $final[0] !== false &&
       
   107                    substr($orig[0], 0, 1) == ' ' &&
       
   108                    substr($final[0], 0, 1) == ' ') {
       
   109                 $prefix .= substr($orig[0], 0, 1);
       
   110                 $orig[0] = substr($orig[0], 1);
       
   111                 $final[0] = substr($final[0], 1);
       
   112             }
       
   113             return $prefix . $this->_deleted($orig) . $this->_added($final);
       
   114         }
       
   115 
       
   116         $text1 = implode("\n", $orig);
       
   117         $text2 = implode("\n", $final);
       
   118 
       
   119         /* Non-printing newline marker. */
       
   120         $nl = "\0";
       
   121 
       
   122         /* We want to split on word boundaries, but we need to
       
   123          * preserve whitespace as well. Therefore we split on words,
       
   124          * but include all blocks of whitespace in the wordlist. */
       
   125         $diff = &new Text_Diff($this->_splitOnWords($text1, $nl),
       
   126                                $this->_splitOnWords($text2, $nl));
       
   127 
       
   128         /* Get the diff in inline format. */
       
   129         $renderer = &new Text_Diff_Renderer_inline(array_merge($this->getParams(),
       
   130                                                                array('split_level' => 'words')));
       
   131 
       
   132         /* Run the diff and get the output. */
       
   133         return str_replace($nl, "\n", $renderer->render($diff)) . "\n";
       
   134     }
       
   135 
       
   136     function _splitOnWords($string, $newlineEscape = "\n")
       
   137     {
       
   138         $words = array();
       
   139         $length = strlen($string);
       
   140         $pos = 0;
       
   141 
       
   142         while ($pos < $length) {
       
   143             // Eat a word with any preceding whitespace.
       
   144             $spaces = strspn(substr($string, $pos), " \n");
       
   145             $nextpos = strcspn(substr($string, $pos + $spaces), " \n");
       
   146             $words[] = str_replace("\n", $newlineEscape, substr($string, $pos, $spaces + $nextpos));
       
   147             $pos += $spaces + $nextpos;
       
   148         }
       
   149 
       
   150         return $words;
       
   151     }
       
   152 
       
   153     function _encode(&$string)
       
   154     {
       
   155         $string = htmlspecialchars($string);
       
   156     }
       
   157 
       
   158 }