Refactor diffs
authorMax Semenik <maxsem.wiki@gmail.com>
Tue, 26 Apr 2016 18:57:44 +0000 (11:57 -0700)
committerMax Semenik <maxsem.wiki@gmail.com>
Wed, 18 May 2016 01:17:05 +0000 (18:17 -0700)
* Merge MappedDiff into WordLevelDiff
* Rename <something insane>WordAccumulator into WordAccumulator and namespace
* Better variable names

Change-Id: I5847f2bb89402d0537b9e99cccd24c547ce4e4e2

autoload.php
includes/diff/DairikiDiff.php
includes/diff/WordAccumulator.php [new file with mode: 0644]
includes/diff/WordLevelDiff.php [new file with mode: 0644]

index cf0e417..2f7a9ba 100644 (file)
@@ -779,7 +779,6 @@ $wgAutoloadLocalClasses = [
        'MalformedTitleException' => __DIR__ . '/includes/title/MalformedTitleException.php',
        'ManualLogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
        'MapCacheLRU' => __DIR__ . '/includes/libs/MapCacheLRU.php',
        'MalformedTitleException' => __DIR__ . '/includes/title/MalformedTitleException.php',
        'ManualLogEntry' => __DIR__ . '/includes/logging/LogEntry.php',
        'MapCacheLRU' => __DIR__ . '/includes/libs/MapCacheLRU.php',
-       'MappedDiff' => __DIR__ . '/includes/diff/DairikiDiff.php',
        'MappedIterator' => __DIR__ . '/includes/libs/MappedIterator.php',
        'MarkpatrolledAction' => __DIR__ . '/includes/actions/MarkpatrolledAction.php',
        'McTest' => __DIR__ . '/maintenance/mctest.php',
        'MappedIterator' => __DIR__ . '/includes/libs/MappedIterator.php',
        'MarkpatrolledAction' => __DIR__ . '/includes/actions/MarkpatrolledAction.php',
        'McTest' => __DIR__ . '/maintenance/mctest.php',
@@ -829,6 +828,7 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Auth\\Throttler' => __DIR__ . '/includes/auth/Throttler.php',
        'MediaWiki\\Auth\\UserDataAuthenticationRequest' => __DIR__ . '/includes/auth/UserDataAuthenticationRequest.php',
        'MediaWiki\\Auth\\UsernameAuthenticationRequest' => __DIR__ . '/includes/auth/UsernameAuthenticationRequest.php',
        'MediaWiki\\Auth\\Throttler' => __DIR__ . '/includes/auth/Throttler.php',
        'MediaWiki\\Auth\\UserDataAuthenticationRequest' => __DIR__ . '/includes/auth/UserDataAuthenticationRequest.php',
        'MediaWiki\\Auth\\UsernameAuthenticationRequest' => __DIR__ . '/includes/auth/UsernameAuthenticationRequest.php',
+       'MediaWiki\\Diff\\WordAccumulator' => __DIR__ . '/includes/diff/WordAccumulator.php',
        'MediaWiki\\Languages\\Data\\Names' => __DIR__ . '/languages/data/Names.php',
        'MediaWiki\\Languages\\Data\\ZhConversion' => __DIR__ . '/languages/data/ZhConversion.php',
        'MediaWiki\\Linker\\LinkTarget' => __DIR__ . '/includes/linker/LinkTarget.php',
        'MediaWiki\\Languages\\Data\\Names' => __DIR__ . '/languages/data/Names.php',
        'MediaWiki\\Languages\\Data\\ZhConversion' => __DIR__ . '/languages/data/ZhConversion.php',
        'MediaWiki\\Linker\\LinkTarget' => __DIR__ . '/includes/linker/LinkTarget.php',
@@ -1518,7 +1518,7 @@ $wgAutoloadLocalClasses = [
        'WikitextContentHandler' => __DIR__ . '/includes/content/WikitextContentHandler.php',
        'WinCacheBagOStuff' => __DIR__ . '/includes/libs/objectcache/WinCacheBagOStuff.php',
        'WithoutInterwikiPage' => __DIR__ . '/includes/specials/SpecialWithoutinterwiki.php',
        'WikitextContentHandler' => __DIR__ . '/includes/content/WikitextContentHandler.php',
        'WinCacheBagOStuff' => __DIR__ . '/includes/libs/objectcache/WinCacheBagOStuff.php',
        'WithoutInterwikiPage' => __DIR__ . '/includes/specials/SpecialWithoutinterwiki.php',
-       'WordLevelDiff' => __DIR__ . '/includes/diff/DairikiDiff.php',
+       'WordLevelDiff' => __DIR__ . '/includes/diff/WordLevelDiff.php',
        'WrapOldPasswords' => __DIR__ . '/maintenance/wrapOldPasswords.php',
        'XCFHandler' => __DIR__ . '/includes/media/XCF.php',
        'XCacheBagOStuff' => __DIR__ . '/includes/libs/objectcache/XCacheBagOStuff.php',
        'WrapOldPasswords' => __DIR__ . '/maintenance/wrapOldPasswords.php',
        'XCFHandler' => __DIR__ . '/includes/media/XCF.php',
        'XCacheBagOStuff' => __DIR__ . '/includes/libs/objectcache/XCacheBagOStuff.php',
index e5e082f..72db5e1 100644 (file)
@@ -559,236 +559,7 @@ class Diff {
 }
 
 /**
 }
 
 /**
- * @todo document, bad name.
- * @private
- * @ingroup DifferenceEngine
- */
-class MappedDiff extends Diff {
-       /**
-        * Constructor.
-        *
-        * Computes diff between sequences of strings.
-        *
-        * This can be used to compute things like
-        * case-insensitve diffs, or diffs which ignore
-        * changes in white-space.
-        *
-        * @param string[] $from_lines An array of strings.
-        *   Typically these are lines from a file.
-        * @param string[] $to_lines An array of strings.
-        * @param string[] $mapped_from_lines This array should
-        *   have the same size number of elements as $from_lines.
-        *   The elements in $mapped_from_lines and
-        *   $mapped_to_lines are what is actually compared
-        *   when computing the diff.
-        * @param string[] $mapped_to_lines This array should
-        *   have the same number of elements as $to_lines.
-        */
-       public function __construct( $from_lines, $to_lines,
-               $mapped_from_lines, $mapped_to_lines ) {
-
-               assert( count( $from_lines ) == count( $mapped_from_lines ) );
-               assert( count( $to_lines ) == count( $mapped_to_lines ) );
-
-               parent::__construct( $mapped_from_lines, $mapped_to_lines );
-
-               $xi = $yi = 0;
-               $editCount = count( $this->edits );
-               for ( $i = 0; $i < $editCount; $i++ ) {
-                       $orig = &$this->edits[$i]->orig;
-                       if ( is_array( $orig ) ) {
-                               $orig = array_slice( $from_lines, $xi, count( $orig ) );
-                               $xi += count( $orig );
-                       }
-
-                       $closing = &$this->edits[$i]->closing;
-                       if ( is_array( $closing ) ) {
-                               $closing = array_slice( $to_lines, $yi, count( $closing ) );
-                               $yi += count( $closing );
-                       }
-               }
-       }
-}
-
-/**
- * Additions by Axel Boldt follow, partly taken from diff.php, phpwiki-1.3.3
- */
-
-/**
- * @todo document
- * @private
- * @ingroup DifferenceEngine
+ * @deprecated Alias for WordAccumulator, to be soon removed
  */
  */
-class HWLDFWordAccumulator {
-       public $insClass = ' class="diffchange diffchange-inline"';
-       public $delClass = ' class="diffchange diffchange-inline"';
-
-       private $lines = [];
-       private $line = '';
-       private $group = '';
-       private $tag = '';
-
-       /**
-        * @param string $new_tag
-        */
-       private function flushGroup( $new_tag ) {
-               if ( $this->group !== '' ) {
-                       if ( $this->tag == 'ins' ) {
-                               $this->line .= "<ins{$this->insClass}>" .
-                                       htmlspecialchars( $this->group ) . '</ins>';
-                       } elseif ( $this->tag == 'del' ) {
-                               $this->line .= "<del{$this->delClass}>" .
-                                       htmlspecialchars( $this->group ) . '</del>';
-                       } else {
-                               $this->line .= htmlspecialchars( $this->group );
-                       }
-               }
-               $this->group = '';
-               $this->tag = $new_tag;
-       }
-
-       /**
-        * @param string $new_tag
-        */
-       private function flushLine( $new_tag ) {
-               $this->flushGroup( $new_tag );
-               if ( $this->line != '' ) {
-                       array_push( $this->lines, $this->line );
-               } else {
-                       # make empty lines visible by inserting an NBSP
-                       array_push( $this->lines, '&#160;' );
-               }
-               $this->line = '';
-       }
-
-       /**
-        * @param string[] $words
-        * @param string $tag
-        */
-       public function addWords( $words, $tag = '' ) {
-               if ( $tag != $this->tag ) {
-                       $this->flushGroup( $tag );
-               }
-
-               foreach ( $words as $word ) {
-                       // new-line should only come as first char of word.
-                       if ( $word == '' ) {
-                               continue;
-                       }
-                       if ( $word[0] == "\n" ) {
-                               $this->flushLine( $tag );
-                               $word = substr( $word, 1 );
-                       }
-                       assert( !strstr( $word, "\n" ) );
-                       $this->group .= $word;
-               }
-       }
-
-       /**
-        * @return string[]
-        */
-       public function getLines() {
-               $this->flushLine( '~done' );
-
-               return $this->lines;
-       }
-}
-
-/**
- * @todo document
- * @private
- * @ingroup DifferenceEngine
- */
-class WordLevelDiff extends MappedDiff {
-       const MAX_LINE_LENGTH = 10000;
-
-       /**
-        * @param string[] $orig_lines
-        * @param string[] $closing_lines
-        */
-       public function __construct( $orig_lines, $closing_lines ) {
-
-               list( $orig_words, $orig_stripped ) = $this->split( $orig_lines );
-               list( $closing_words, $closing_stripped ) = $this->split( $closing_lines );
-
-               parent::__construct( $orig_words, $closing_words,
-                       $orig_stripped, $closing_stripped );
-       }
-
-       /**
-        * @param string[] $lines
-        *
-        * @return array[]
-        */
-       private function split( $lines ) {
-
-               $words = [];
-               $stripped = [];
-               $first = true;
-               foreach ( $lines as $line ) {
-                       # If the line is too long, just pretend the entire line is one big word
-                       # This prevents resource exhaustion problems
-                       if ( $first ) {
-                               $first = false;
-                       } else {
-                               $words[] = "\n";
-                               $stripped[] = "\n";
-                       }
-                       if ( strlen( $line ) > self::MAX_LINE_LENGTH ) {
-                               $words[] = $line;
-                               $stripped[] = $line;
-                       } else {
-                               $m = [];
-                               if ( preg_match_all( '/ ( [^\S\n]+ | [0-9_A-Za-z\x80-\xff]+ | . ) (?: (?!< \n) [^\S\n])? /xs',
-                                       $line, $m )
-                               ) {
-                                       foreach ( $m[0] as $word ) {
-                                               $words[] = $word;
-                                       }
-                                       foreach ( $m[1] as $stripped_word ) {
-                                               $stripped[] = $stripped_word;
-                                       }
-                               }
-                       }
-               }
-
-               return [ $words, $stripped ];
-       }
-
-       /**
-        * @return string[]
-        */
-       public function orig() {
-               $orig = new HWLDFWordAccumulator;
-
-               foreach ( $this->edits as $edit ) {
-                       if ( $edit->type == 'copy' ) {
-                               $orig->addWords( $edit->orig );
-                       } elseif ( $edit->orig ) {
-                               $orig->addWords( $edit->orig, 'del' );
-                       }
-               }
-               $lines = $orig->getLines();
-
-               return $lines;
-       }
-
-       /**
-        * @return string[]
-        */
-       public function closing() {
-               $closing = new HWLDFWordAccumulator;
-
-               foreach ( $this->edits as $edit ) {
-                       if ( $edit->type == 'copy' ) {
-                               $closing->addWords( $edit->closing );
-                       } elseif ( $edit->closing ) {
-                               $closing->addWords( $edit->closing, 'ins' );
-                       }
-               }
-               $lines = $closing->getLines();
-
-               return $lines;
-       }
-
+class HWLDFWordAccumulator extends MediaWiki\Diff\WordAccumulator {
 }
 }
diff --git a/includes/diff/WordAccumulator.php b/includes/diff/WordAccumulator.php
new file mode 100644 (file)
index 0000000..a26775f
--- /dev/null
@@ -0,0 +1,107 @@
+<?php
+/**
+ * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * You may copy this code freely under the conditions of the GPL.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup DifferenceEngine
+ * @defgroup DifferenceEngine DifferenceEngine
+ */
+
+namespace MediaWiki\Diff;
+
+/**
+ * Stores, escapes and formats the results of word-level diff
+ *
+ * @private
+ * @ingroup DifferenceEngine
+ */
+class WordAccumulator {
+       public $insClass = ' class="diffchange diffchange-inline"';
+       public $delClass = ' class="diffchange diffchange-inline"';
+
+       private $lines = [];
+       private $line = '';
+       private $group = '';
+       private $tag = '';
+
+       /**
+        * @param string $new_tag
+        */
+       private function flushGroup( $new_tag ) {
+               if ( $this->group !== '' ) {
+                       if ( $this->tag == 'ins' ) {
+                               $this->line .= "<ins{$this->insClass}>" .
+                                                          htmlspecialchars( $this->group ) . '</ins>';
+                       } elseif ( $this->tag == 'del' ) {
+                               $this->line .= "<del{$this->delClass}>" .
+                                                          htmlspecialchars( $this->group ) . '</del>';
+                       } else {
+                               $this->line .= htmlspecialchars( $this->group );
+                       }
+               }
+               $this->group = '';
+               $this->tag = $new_tag;
+       }
+
+       /**
+        * @param string $new_tag
+        */
+       private function flushLine( $new_tag ) {
+               $this->flushGroup( $new_tag );
+               if ( $this->line != '' ) {
+                       array_push( $this->lines, $this->line );
+               } else {
+                       # make empty lines visible by inserting an NBSP
+                       array_push( $this->lines, '&#160;' );
+               }
+               $this->line = '';
+       }
+
+       /**
+        * @param string[] $words
+        * @param string $tag
+        */
+       public function addWords( $words, $tag = '' ) {
+               if ( $tag != $this->tag ) {
+                       $this->flushGroup( $tag );
+               }
+
+               foreach ( $words as $word ) {
+                       // new-line should only come as first char of word.
+                       if ( $word == '' ) {
+                               continue;
+                       }
+                       if ( $word[0] == "\n" ) {
+                               $this->flushLine( $tag );
+                               $word = substr( $word, 1 );
+                       }
+                       assert( !strstr( $word, "\n" ) );
+                       $this->group .= $word;
+               }
+       }
+
+       /**
+        * @return string[]
+        */
+       public function getLines() {
+               $this->flushLine( '~done' );
+
+               return $this->lines;
+       }
+}
diff --git a/includes/diff/WordLevelDiff.php b/includes/diff/WordLevelDiff.php
new file mode 100644 (file)
index 0000000..12cf376
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+/**
+ * Copyright © 2000, 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
+ * You may copy this code freely under the conditions of the GPL.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup DifferenceEngine
+ * @defgroup DifferenceEngine DifferenceEngine
+ */
+
+use MediaWiki\Diff\WordAccumulator;
+
+/**
+ * Performs a word-level diff on several lines
+ *
+ * @ingroup DifferenceEngine
+ */
+class WordLevelDiff extends \Diff {
+       const MAX_LINE_LENGTH = 10000;
+
+       /**
+        * @param string[] $linesBefore
+        * @param string[] $linesAfter
+        */
+       public function __construct( $linesBefore, $linesAfter ) {
+
+               list( $wordsBefore, $wordsBeforeStripped ) = $this->split( $linesBefore );
+               list( $wordsAfter, $wordsAfterStripped ) = $this->split( $linesAfter );
+
+               parent::__construct( $wordsBeforeStripped, $wordsAfterStripped );
+
+               $xi = $yi = 0;
+               $editCount = count( $this->edits );
+               for ( $i = 0; $i < $editCount; $i++ ) {
+                       $orig = &$this->edits[$i]->orig;
+                       if ( is_array( $orig ) ) {
+                               $orig = array_slice( $wordsBefore, $xi, count( $orig ) );
+                               $xi += count( $orig );
+                       }
+
+                       $closing = &$this->edits[$i]->closing;
+                       if ( is_array( $closing ) ) {
+                               $closing = array_slice( $wordsAfter, $yi, count( $closing ) );
+                               $yi += count( $closing );
+                       }
+               }
+
+       }
+
+       /**
+        * @param string[] $lines
+        *
+        * @return array[]
+        */
+       private function split( $lines ) {
+
+               $words = [];
+               $stripped = [];
+               $first = true;
+               foreach ( $lines as $line ) {
+                       # If the line is too long, just pretend the entire line is one big word
+                       # This prevents resource exhaustion problems
+                       if ( $first ) {
+                               $first = false;
+                       } else {
+                               $words[] = "\n";
+                               $stripped[] = "\n";
+                       }
+                       if ( strlen( $line ) > self::MAX_LINE_LENGTH ) {
+                               $words[] = $line;
+                               $stripped[] = $line;
+                       } else {
+                               $m = [];
+                               if ( preg_match_all( '/ ( [^\S\n]+ | [0-9_A-Za-z\x80-\xff]+ | . ) (?: (?!< \n) [^\S\n])? /xs',
+                                       $line, $m )
+                               ) {
+                                       foreach ( $m[0] as $word ) {
+                                               $words[] = $word;
+                                       }
+                                       foreach ( $m[1] as $stripped_word ) {
+                                               $stripped[] = $stripped_word;
+                                       }
+                               }
+                       }
+               }
+
+               return [ $words, $stripped ];
+       }
+
+       /**
+        * @return string[]
+        */
+       public function orig() {
+               $orig = new WordAccumulator;
+
+               foreach ( $this->edits as $edit ) {
+                       if ( $edit->type == 'copy' ) {
+                               $orig->addWords( $edit->orig );
+                       } elseif ( $edit->orig ) {
+                               $orig->addWords( $edit->orig, 'del' );
+                       }
+               }
+               $lines = $orig->getLines();
+
+               return $lines;
+       }
+
+       /**
+        * @return string[]
+        */
+       public function closing() {
+               $closing = new WordAccumulator;
+
+               foreach ( $this->edits as $edit ) {
+                       if ( $edit->type == 'copy' ) {
+                               $closing->addWords( $edit->closing );
+                       } elseif ( $edit->closing ) {
+                               $closing->addWords( $edit->closing, 'ins' );
+                       }
+               }
+               $lines = $closing->getLines();
+
+               return $lines;
+       }
+
+}