MediaWiki  master
TableDiffFormatter.php
Go to the documentation of this file.
1 <?php
28 
36 
40  private const SIDE_DELETED = 'deleted';
41  private const SIDE_ADDED = 'added';
42  private const SIDE_CLASSES = [
43  self::SIDE_DELETED => 'diff-side-deleted',
44  self::SIDE_ADDED => 'diff-side-added'
45  ];
46 
47  public function __construct() {
48  $this->leadingContextLines = 2;
49  $this->trailingContextLines = 2;
50  }
51 
57  public static function escapeWhiteSpace( $msg ) {
58  $msg = preg_replace( '/^ /m', "\u{00A0} ", $msg );
59  $msg = preg_replace( '/ $/m', " \u{00A0}", $msg );
60  $msg = preg_replace( '/ /', "\u{00A0} ", $msg );
61 
62  return $msg;
63  }
64 
73  protected function blockHeader( $xbeg, $xlen, $ybeg, $ylen ) {
74  // '<!--LINE \d+ -->' get replaced by a localised line number
75  // in DifferenceEngine::localiseLineNumbers
76  return Html::rawElement(
77  'tr',
78  [],
79  Html::rawElement(
80  'td',
81  [ 'colspan' => '2', 'class' => 'diff-lineno', 'id' => 'mw-diff-left-l' . $xbeg ],
82  '<!--LINE ' . $xbeg . '-->'
83  ) .
84  "\n" .
85  Html::rawElement(
86  'td',
87  [ 'colspan' => '2', 'class' => 'diff-lineno' ],
88  '<!--LINE ' . $ybeg . '-->'
89  )
90  ) . "\n";
91  }
92 
94  protected function startBlock( $header ) {
95  $this->writeOutput( $header );
96  }
97 
99  protected function endBlock() {
100  }
101 
107  protected function lines( $lines, $prefix = ' ', $color = 'white' ) {
108  }
109 
117  protected function addedLine( $line ) {
118  return $this->wrapLine( '+', [ 'diff-addedline', $this->getClassForSide( self::SIDE_ADDED ) ], $line );
119  }
120 
128  protected function deletedLine( $line ) {
129  return $this->wrapLine( '−', [ 'diff-deletedline', $this->getClassForSide( self::SIDE_DELETED ) ], $line );
130  }
131 
140  protected function contextLine( $line, string $side ) {
141  return $this->wrapLine( '', [ 'diff-context', $this->getClassForSide( $side ) ], $line );
142  }
143 
151  protected function wrapLine( $marker, $class, $line ) {
152  if ( $line !== '' ) {
153  // The <div> wrapper is needed for 'overflow: auto' style to scroll properly
154  $line = Html::rawElement( 'div', [], $this->escapeWhiteSpace( $line ) );
155  } else {
156  $line = Html::element( 'br' );
157  }
158 
159  $markerAttrs = [ 'class' => 'diff-marker' ];
160  if ( $marker ) {
161  $markerAttrs['data-marker'] = $marker;
162  }
163 
164  return Html::element( 'td', $markerAttrs ) .
165  Html::rawElement( 'td', [ 'class' => $class ], $line );
166  }
167 
172  protected function emptyLine( string $side ) {
173  return Html::element( 'td', [ 'colspan' => '2', 'class' => $this->getClassForSide( $side ) ] );
174  }
175 
181  protected function added( $lines ) {
182  foreach ( $lines as $line ) {
183  $this->writeOutput(
184  Html::rawElement(
185  'tr',
186  [],
187  $this->emptyLine( self::SIDE_DELETED ) .
188  $this->addedLine(
189  Html::element(
190  'ins',
191  [ 'class' => 'diffchange' ],
192  $line
193  )
194  )
195  ) .
196  "\n"
197  );
198  }
199  }
200 
206  protected function deleted( $lines ) {
207  foreach ( $lines as $line ) {
208  $this->writeOutput(
209  Html::rawElement(
210  'tr',
211  [],
212  $this->deletedLine(
213  Html::element(
214  'del',
215  [ 'class' => 'diffchange' ],
216  $line
217  )
218  ) .
219  $this->emptyLine( self::SIDE_ADDED )
220  ) .
221  "\n"
222  );
223  }
224  }
225 
231  protected function context( $lines ) {
232  foreach ( $lines as $line ) {
233  $this->writeOutput(
234  Html::rawElement(
235  'tr',
236  [],
237  $this->contextLine( htmlspecialchars( $line ), self::SIDE_DELETED ) .
238  $this->contextLine( htmlspecialchars( $line ), self::SIDE_ADDED )
239  ) .
240  "\n"
241  );
242  }
243  }
244 
251  protected function changed( $orig, $closing ) {
252  $diff = new WordLevelDiff( $orig, $closing );
253  $del = $diff->orig();
254  $add = $diff->closing();
255 
256  # Notice that WordLevelDiff returns HTML-escaped output.
257  # Hence, we will be calling addedLine/deletedLine without HTML-escaping.
258 
259  $ndel = count( $del );
260  $nadd = count( $add );
261  $n = max( $ndel, $nadd );
262  for ( $i = 0; $i < $n; $i++ ) {
263  $delLine = $i < $ndel ? $this->deletedLine( $del[$i] ) : $this->emptyLine( self::SIDE_DELETED );
264  $addLine = $i < $nadd ? $this->addedLine( $add[$i] ) : $this->emptyLine( self::SIDE_ADDED );
265  $this->writeOutput(
266  Html::rawElement(
267  'tr',
268  [],
269  $delLine . $addLine
270  ) .
271  "\n"
272  );
273  }
274  }
275 
283  private function getClassForSide( string $side ): string {
284  if ( !isset( self::SIDE_CLASSES[$side] ) ) {
285  throw new InvalidArgumentException( "Invalid diff side: $side" );
286  }
287  return self::SIDE_CLASSES[$side];
288  }
289 }
if(!defined('MW_SETUP_CALLBACK'))
Definition: WebStart.php:88
Base class for diff formatters.
writeOutput( $text)
Writes a string to the output buffer.
This class is a collection of static functions that serve two purposes:
Definition: Html.php:55
MediaWiki default table style diff formatter.
deletedLine( $line)
HTML-escape parameter before calling this.
startBlock( $header)
Called at the start of a block of connected edits.This default implementation writes the header and a...
wrapLine( $marker, $class, $line)
added( $lines)
Writes all lines to the output buffer, each enclosed in .
changed( $orig, $closing)
Writes the two sets of lines to the output buffer, each enclosed in .
lines( $lines, $prefix=' ', $color='white')
blockHeader( $xbeg, $xlen, $ybeg, $ylen)
endBlock()
Called at the end of a block of connected edits.This default implementation does nothing.
static escapeWhiteSpace( $msg)
addedLine( $line)
HTML-escape parameter before calling this.
contextLine( $line, string $side)
HTML-escape parameter before calling this.
context( $lines)
Writes all lines to the output buffer, each enclosed in .
deleted( $lines)
Writes all lines to the output buffer, each enclosed in .
Performs a word-level diff on several lines.
if(!file_exists( $CREDITS)) $lines
$header