Renderer.php 6.6 KB

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