]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/diff/DiffFormatter.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / diff / DiffFormatter.php
1 <?php
2 /**
3  * Base for diff rendering classes. Portions taken from phpwiki-1.3.3.
4  *
5  * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
6  * You may copy this code freely under the conditions of the GPL.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  * http://www.gnu.org/copyleft/gpl.html
22  *
23  * @file
24  * @ingroup DifferenceEngine
25  */
26
27 /**
28  * Base class for diff formatters
29  *
30  * This class formats the diff in classic diff format.
31  * It is intended that this class be customized via inheritance,
32  * to obtain fancier outputs.
33  * @todo document
34  * @ingroup DifferenceEngine
35  */
36 abstract class DiffFormatter {
37
38         /** @var int Number of leading context "lines" to preserve.
39          *
40          * This should be left at zero for this class, but subclasses
41          * may want to set this to other values.
42          */
43         protected $leadingContextLines = 0;
44
45         /** @var int Number of trailing context "lines" to preserve.
46          *
47          * This should be left at zero for this class, but subclasses
48          * may want to set this to other values.
49          */
50         protected $trailingContextLines = 0;
51
52         /** @var string The output buffer; holds the output while it is built. */
53         private $result = '';
54
55         /**
56          * Format a diff.
57          *
58          * @param Diff $diff
59          *
60          * @return string The formatted output.
61          */
62         public function format( $diff ) {
63                 $xi = $yi = 1;
64                 $block = false;
65                 $context = [];
66
67                 $nlead = $this->leadingContextLines;
68                 $ntrail = $this->trailingContextLines;
69
70                 $this->startDiff();
71
72                 // Initialize $x0 and $y0 to prevent IDEs from getting confused.
73                 $x0 = $y0 = 0;
74                 foreach ( $diff->edits as $edit ) {
75                         if ( $edit->type == 'copy' ) {
76                                 if ( is_array( $block ) ) {
77                                         if ( count( $edit->orig ) <= $nlead + $ntrail ) {
78                                                 $block[] = $edit;
79                                         } else {
80                                                 if ( $ntrail ) {
81                                                         $context = array_slice( $edit->orig, 0, $ntrail );
82                                                         $block[] = new DiffOpCopy( $context );
83                                                 }
84                                                 $this->block( $x0, $ntrail + $xi - $x0,
85                                                         $y0, $ntrail + $yi - $y0,
86                                                         $block );
87                                                 $block = false;
88                                         }
89                                 }
90                                 $context = $edit->orig;
91                         } else {
92                                 if ( !is_array( $block ) ) {
93                                         $context = array_slice( $context, count( $context ) - $nlead );
94                                         $x0 = $xi - count( $context );
95                                         $y0 = $yi - count( $context );
96                                         $block = [];
97                                         if ( $context ) {
98                                                 $block[] = new DiffOpCopy( $context );
99                                         }
100                                 }
101                                 $block[] = $edit;
102                         }
103
104                         if ( $edit->orig ) {
105                                 $xi += count( $edit->orig );
106                         }
107                         if ( $edit->closing ) {
108                                 $yi += count( $edit->closing );
109                         }
110                 }
111
112                 if ( is_array( $block ) ) {
113                         $this->block( $x0, $xi - $x0,
114                                 $y0, $yi - $y0,
115                                 $block );
116                 }
117
118                 $end = $this->endDiff();
119
120                 return $end;
121         }
122
123         /**
124          * @param int $xbeg
125          * @param int $xlen
126          * @param int $ybeg
127          * @param int $ylen
128          * @param array &$edits
129          *
130          * @throws MWException If the edit type is not known.
131          */
132         protected function block( $xbeg, $xlen, $ybeg, $ylen, &$edits ) {
133                 $this->startBlock( $this->blockHeader( $xbeg, $xlen, $ybeg, $ylen ) );
134                 foreach ( $edits as $edit ) {
135                         if ( $edit->type == 'copy' ) {
136                                 $this->context( $edit->orig );
137                         } elseif ( $edit->type == 'add' ) {
138                                 $this->added( $edit->closing );
139                         } elseif ( $edit->type == 'delete' ) {
140                                 $this->deleted( $edit->orig );
141                         } elseif ( $edit->type == 'change' ) {
142                                 $this->changed( $edit->orig, $edit->closing );
143                         } else {
144                                 throw new MWException( "Unknown edit type: {$edit->type}" );
145                         }
146                 }
147                 $this->endBlock();
148         }
149
150         protected function startDiff() {
151                 $this->result = '';
152         }
153
154         /**
155          * Writes a string to the output buffer.
156          *
157          * @param string $text
158          */
159         protected function writeOutput( $text ) {
160                 $this->result .= $text;
161         }
162
163         /**
164          * @return string
165          */
166         protected function endDiff() {
167                 $val = $this->result;
168                 $this->result = '';
169
170                 return $val;
171         }
172
173         /**
174          * @param int $xbeg
175          * @param int $xlen
176          * @param int $ybeg
177          * @param int $ylen
178          *
179          * @return string
180          */
181         protected function blockHeader( $xbeg, $xlen, $ybeg, $ylen ) {
182                 if ( $xlen > 1 ) {
183                         $xbeg .= ',' . ( $xbeg + $xlen - 1 );
184                 }
185                 if ( $ylen > 1 ) {
186                         $ybeg .= ',' . ( $ybeg + $ylen - 1 );
187                 }
188
189                 return $xbeg . ( $xlen ? ( $ylen ? 'c' : 'd' ) : 'a' ) . $ybeg;
190         }
191
192         /**
193          * Called at the start of a block of connected edits.
194          * This default implementation writes the header and a newline to the output buffer.
195          *
196          * @param string $header
197          */
198         protected function startBlock( $header ) {
199                 $this->writeOutput( $header . "\n" );
200         }
201
202         /**
203          * Called at the end of a block of connected edits.
204          * This default implementation does nothing.
205          */
206         protected function endBlock() {
207         }
208
209         /**
210          * Writes all (optionally prefixed) lines to the output buffer, separated by newlines.
211          *
212          * @param string[] $lines
213          * @param string $prefix
214          */
215         protected function lines( $lines, $prefix = ' ' ) {
216                 foreach ( $lines as $line ) {
217                         $this->writeOutput( "$prefix $line\n" );
218                 }
219         }
220
221         /**
222          * @param string[] $lines
223          */
224         protected function context( $lines ) {
225                 $this->lines( $lines );
226         }
227
228         /**
229          * @param string[] $lines
230          */
231         protected function added( $lines ) {
232                 $this->lines( $lines, '>' );
233         }
234
235         /**
236          * @param string[] $lines
237          */
238         protected function deleted( $lines ) {
239                 $this->lines( $lines, '<' );
240         }
241
242         /**
243          * Writes the two sets of lines to the output buffer, separated by "---" and a newline.
244          *
245          * @param string[] $orig
246          * @param string[] $closing
247          */
248         protected function changed( $orig, $closing ) {
249                 $this->deleted( $orig );
250                 $this->writeOutput( "---\n" );
251                 $this->added( $closing );
252         }
253
254 }