You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

243 line
6.7 KiB

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