-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, ' ' );
- }
- $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;
- }
-