3 * A class to render Diffs in different formats.
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.
8 * Copyright 2004-2010 The Horde Project (http://www.horde.org/)
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.
15 class Text_Diff_Renderer {
18 * Number of leading context "lines" to preserve.
20 * This should be left at zero for this class, but subclasses may want to
21 * set this to other values.
23 var $_leading_context_lines = 0;
26 * Number of trailing context "lines" to preserve.
28 * This should be left at zero for this class, but subclasses may want to
29 * set this to other values.
31 var $_trailing_context_lines = 0;
36 function __construct( $params = array() )
38 foreach ($params as $param => $value) {
40 if (isset($this->$v)) {
49 public function Text_Diff_Renderer( $params = array() ) {
50 self::__construct( $params );
54 * Get any renderer parameters.
56 * @return array All parameters of this renderer object.
61 foreach (get_object_vars($this) as $k => $v) {
63 $params[substr($k, 1)] = $v;
73 * @param Text_Diff $diff A Text_Diff object.
75 * @return string The formatted output.
77 function render($diff)
83 $nlead = $this->_leading_context_lines;
84 $ntrail = $this->_trailing_context_lines;
86 $output = $this->_startDiff();
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
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
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. */
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);
111 $output .= $this->_block($x0, $ntrail + $xi - $x0,
112 $y0, $ntrail + $yi - $y0,
117 /* Keep the copy block as the context for the next block. */
118 $context = $edit->orig;
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);
128 $block[] = new Text_Diff_Op_copy($context);
135 $xi += count($edit->orig);
138 $yi += count($edit->final);
142 if (is_array($block)) {
143 $output .= $this->_block($x0, $xi - $x0,
148 return $output . $this->_endDiff();
151 function _block($xbeg, $xlen, $ybeg, $ylen, &$edits)
153 $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen));
155 foreach ($edits as $edit) {
156 switch (strtolower(get_class($edit))) {
157 case 'text_diff_op_copy':
158 $output .= $this->_context($edit->orig);
161 case 'text_diff_op_add':
162 $output .= $this->_added($edit->final);
165 case 'text_diff_op_delete':
166 $output .= $this->_deleted($edit->orig);
169 case 'text_diff_op_change':
170 $output .= $this->_changed($edit->orig, $edit->final);
175 return $output . $this->_endBlock();
178 function _startDiff()
188 function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
191 $xbeg .= ',' . ($xbeg + $xlen - 1);
194 $ybeg .= ',' . ($ybeg + $ylen - 1);
197 // this matches the GNU Diff behaviour
198 if ($xlen && !$ylen) {
204 return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg;
207 function _startBlock($header)
209 return $header . "\n";
217 function _lines($lines, $prefix = ' ')
219 return $prefix . implode("\n$prefix", $lines) . "\n";
222 function _context($lines)
224 return $this->_lines($lines, ' ');
227 function _added($lines)
229 return $this->_lines($lines, '> ');
232 function _deleted($lines)
234 return $this->_lines($lines, '< ');
237 function _changed($orig, $final)
239 return $this->_deleted($orig) . "---\n" . $this->_added($final);