[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/differential/render/ -> DifferentialChangesetRenderer.php (source)

   1  <?php
   2  
   3  abstract class DifferentialChangesetRenderer {
   4  
   5    private $user;
   6    private $changeset;
   7    private $renderingReference;
   8    private $renderPropertyChangeHeader;
   9    private $isTopLevel;
  10    private $hunkStartLines;
  11    private $oldLines;
  12    private $newLines;
  13    private $oldComments;
  14    private $newComments;
  15    private $oldChangesetID;
  16    private $newChangesetID;
  17    private $oldAttachesToNewFile;
  18    private $newAttachesToNewFile;
  19    private $highlightOld = array();
  20    private $highlightNew = array();
  21    private $codeCoverage;
  22    private $handles;
  23    private $markupEngine;
  24    private $oldRender;
  25    private $newRender;
  26    private $originalOld;
  27    private $originalNew;
  28    private $gaps;
  29    private $mask;
  30    private $depths;
  31    private $originalCharacterEncoding;
  32  
  33    private $oldFile = false;
  34    private $newFile = false;
  35  
  36    public function setOriginalCharacterEncoding($original_character_encoding) {
  37      $this->originalCharacterEncoding = $original_character_encoding;
  38      return $this;
  39    }
  40  
  41    public function getOriginalCharacterEncoding() {
  42      return $this->originalCharacterEncoding;
  43    }
  44  
  45    public function setDepths($depths) {
  46      $this->depths = $depths;
  47      return $this;
  48    }
  49    protected function getDepths() {
  50      return $this->depths;
  51    }
  52  
  53    public function setMask($mask) {
  54      $this->mask = $mask;
  55      return $this;
  56    }
  57    protected function getMask() {
  58      return $this->mask;
  59    }
  60  
  61    public function setGaps($gaps) {
  62      $this->gaps = $gaps;
  63      return $this;
  64    }
  65    protected function getGaps() {
  66      return $this->gaps;
  67    }
  68  
  69    public function attachOldFile(PhabricatorFile $old = null) {
  70      $this->oldFile = $old;
  71      return $this;
  72    }
  73  
  74    public function getOldFile() {
  75      if ($this->oldFile === false) {
  76        throw new PhabricatorDataNotAttachedException($this);
  77      }
  78      return $this->oldFile;
  79    }
  80  
  81    public function hasOldFile() {
  82      return (bool)$this->oldFile;
  83    }
  84  
  85    public function attachNewFile(PhabricatorFile $new = null) {
  86      $this->newFile = $new;
  87      return $this;
  88    }
  89  
  90    public function getNewFile() {
  91      if ($this->newFile === false) {
  92        throw new PhabricatorDataNotAttachedException($this);
  93      }
  94      return $this->newFile;
  95    }
  96  
  97    public function hasNewFile() {
  98      return (bool)$this->newFile;
  99    }
 100  
 101    public function setOriginalNew($original_new) {
 102      $this->originalNew = $original_new;
 103      return $this;
 104    }
 105    protected function getOriginalNew() {
 106      return $this->originalNew;
 107    }
 108  
 109    public function setOriginalOld($original_old) {
 110      $this->originalOld = $original_old;
 111      return $this;
 112    }
 113    protected function getOriginalOld() {
 114      return $this->originalOld;
 115    }
 116  
 117    public function setNewRender($new_render) {
 118      $this->newRender = $new_render;
 119      return $this;
 120    }
 121    protected function getNewRender() {
 122      return $this->newRender;
 123    }
 124  
 125    public function setOldRender($old_render) {
 126      $this->oldRender = $old_render;
 127      return $this;
 128    }
 129    protected function getOldRender() {
 130      return $this->oldRender;
 131    }
 132  
 133    public function setMarkupEngine(PhabricatorMarkupEngine $markup_engine) {
 134      $this->markupEngine = $markup_engine;
 135      return $this;
 136    }
 137    public function getMarkupEngine() {
 138      return $this->markupEngine;
 139    }
 140  
 141    public function setHandles(array $handles) {
 142      assert_instances_of($handles, 'PhabricatorObjectHandle');
 143      $this->handles = $handles;
 144      return $this;
 145    }
 146    protected function getHandles() {
 147      return $this->handles;
 148    }
 149  
 150    public function setCodeCoverage($code_coverage) {
 151      $this->codeCoverage = $code_coverage;
 152      return $this;
 153    }
 154    protected function getCodeCoverage() {
 155      return $this->codeCoverage;
 156    }
 157  
 158    public function setHighlightNew($highlight_new) {
 159      $this->highlightNew = $highlight_new;
 160      return $this;
 161    }
 162    protected function getHighlightNew() {
 163      return $this->highlightNew;
 164    }
 165  
 166    public function setHighlightOld($highlight_old) {
 167      $this->highlightOld = $highlight_old;
 168      return $this;
 169    }
 170    protected function getHighlightOld() {
 171      return $this->highlightOld;
 172    }
 173  
 174    public function setNewAttachesToNewFile($attaches) {
 175      $this->newAttachesToNewFile = $attaches;
 176      return $this;
 177    }
 178    protected function getNewAttachesToNewFile() {
 179      return $this->newAttachesToNewFile;
 180    }
 181  
 182    public function setOldAttachesToNewFile($attaches) {
 183      $this->oldAttachesToNewFile = $attaches;
 184      return $this;
 185    }
 186    protected function getOldAttachesToNewFile() {
 187      return $this->oldAttachesToNewFile;
 188    }
 189  
 190    public function setNewChangesetID($new_changeset_id) {
 191      $this->newChangesetID = $new_changeset_id;
 192      return $this;
 193    }
 194    protected function getNewChangesetID() {
 195      return $this->newChangesetID;
 196    }
 197  
 198    public function setOldChangesetID($old_changeset_id) {
 199      $this->oldChangesetID = $old_changeset_id;
 200      return $this;
 201    }
 202    protected function getOldChangesetID() {
 203      return $this->oldChangesetID;
 204    }
 205  
 206    public function setNewComments(array $new_comments) {
 207      foreach ($new_comments as $line_number => $comments) {
 208        assert_instances_of($comments, 'PhabricatorInlineCommentInterface');
 209      }
 210      $this->newComments = $new_comments;
 211      return $this;
 212    }
 213    protected function getNewComments() {
 214      return $this->newComments;
 215    }
 216  
 217    public function setOldComments(array $old_comments) {
 218      foreach ($old_comments as $line_number => $comments) {
 219        assert_instances_of($comments, 'PhabricatorInlineCommentInterface');
 220      }
 221      $this->oldComments = $old_comments;
 222      return $this;
 223    }
 224    protected function getOldComments() {
 225      return $this->oldComments;
 226    }
 227  
 228    public function setNewLines(array $new_lines) {
 229      $this->newLines = $new_lines;
 230      return $this;
 231    }
 232    protected function getNewLines() {
 233      return $this->newLines;
 234    }
 235  
 236    public function setOldLines(array $old_lines) {
 237      $this->oldLines = $old_lines;
 238      return $this;
 239    }
 240    protected function getOldLines() {
 241      return $this->oldLines;
 242    }
 243  
 244    public function setHunkStartLines(array $hunk_start_lines) {
 245      $this->hunkStartLines = $hunk_start_lines;
 246      return $this;
 247    }
 248  
 249    protected function getHunkStartLines() {
 250      return $this->hunkStartLines;
 251    }
 252  
 253    public function setUser(PhabricatorUser $user) {
 254      $this->user = $user;
 255      return $this;
 256    }
 257    protected function getUser() {
 258      return $this->user;
 259    }
 260  
 261    public function setChangeset(DifferentialChangeset $changeset) {
 262      $this->changeset = $changeset;
 263      return $this;
 264    }
 265    protected function getChangeset() {
 266      return $this->changeset;
 267    }
 268  
 269    public function setRenderingReference($rendering_reference) {
 270      $this->renderingReference = $rendering_reference;
 271      return $this;
 272    }
 273    protected function getRenderingReference() {
 274      return $this->renderingReference;
 275    }
 276  
 277    public function setRenderPropertyChangeHeader($should_render) {
 278      $this->renderPropertyChangeHeader = $should_render;
 279      return $this;
 280    }
 281    private function shouldRenderPropertyChangeHeader() {
 282      return $this->renderPropertyChangeHeader;
 283    }
 284  
 285    public function setIsTopLevel($is) {
 286      $this->isTopLevel = $is;
 287      return $this;
 288    }
 289    private function getIsTopLevel() {
 290      return $this->isTopLevel;
 291    }
 292  
 293    final public function renderChangesetTable($content) {
 294      $props = null;
 295      if ($this->shouldRenderPropertyChangeHeader()) {
 296        $props = $this->renderPropertyChangeHeader();
 297      }
 298  
 299      $notice = null;
 300      if ($this->getIsTopLevel()) {
 301        $force = (!$content && !$props);
 302        $notice = $this->renderChangeTypeHeader($force);
 303      }
 304  
 305      $result = $notice.$props.$content;
 306  
 307      // TODO: Let the user customize their tab width / display style.
 308      // TODO: We should possibly post-process "\r" as well.
 309      // TODO: Both these steps should happen earlier.
 310      $result = str_replace("\t", '  ', $result);
 311  
 312      return phutil_safe_html($result);
 313    }
 314  
 315    abstract public function isOneUpRenderer();
 316    abstract public function renderTextChange(
 317      $range_start,
 318      $range_len,
 319      $rows);
 320    abstract public function renderFileChange(
 321      $old = null,
 322      $new = null,
 323      $id = 0,
 324      $vs = 0);
 325  
 326    abstract protected function renderChangeTypeHeader($force);
 327  
 328    protected function didRenderChangesetTableContents($contents) {
 329      return $contents;
 330    }
 331  
 332    /**
 333     * Render a "shield" over the diff, with a message like "This file is
 334     * generated and does not need to be reviewed." or "This file was completely
 335     * deleted." This UI element hides unimportant text so the reviewer doesn't
 336     * need to scroll past it.
 337     *
 338     * The shield includes a link to view the underlying content. This link
 339     * may force certain rendering modes when the link is clicked:
 340     *
 341     *    - `"default"`: Render the diff normally, as though it was not
 342     *      shielded. This is the default and appropriate if the underlying
 343     *      diff is a normal change, but was hidden for reasons of not being
 344     *      important (e.g., generated code).
 345     *    - `"text"`: Force the text to be shown. This is probably only relevant
 346     *      when a file is not changed.
 347     *    - `"whitespace"`: Force the text to be shown, and the diff to be
 348     *      rendered with all whitespace shown. This is probably only relevant
 349     *      when a file is changed only by altering whitespace.
 350     *    - `"none"`: Don't show the link (e.g., text not available).
 351     *
 352     * @param   string        Message explaining why the diff is hidden.
 353     * @param   string|null   Force mode, see above.
 354     * @return  string        Shield markup.
 355     */
 356    abstract public function renderShield($message, $force = 'default');
 357  
 358    abstract protected function renderPropertyChangeHeader();
 359  
 360    protected function buildPrimitives($range_start, $range_len) {
 361      $primitives = array();
 362  
 363      $hunk_starts = $this->getHunkStartLines();
 364  
 365      $mask = $this->getMask();
 366      $gaps = $this->getGaps();
 367  
 368      $old = $this->getOldLines();
 369      $new = $this->getNewLines();
 370      $old_render = $this->getOldRender();
 371      $new_render = $this->getNewRender();
 372      $old_comments = $this->getOldComments();
 373      $new_comments = $this->getNewComments();
 374  
 375      $size = count($old);
 376      for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
 377        if (empty($mask[$ii])) {
 378          list($top, $len) = array_pop($gaps);
 379          $primitives[] = array(
 380            'type' => 'context',
 381            'top' => $top,
 382            'len' => $len,
 383          );
 384  
 385          $ii += ($len - 1);
 386          continue;
 387        }
 388  
 389        $ospec = array(
 390          'type' => 'old',
 391          'htype' => null,
 392          'cursor' => $ii,
 393          'line' => null,
 394          'oline' => null,
 395          'render' => null,
 396        );
 397  
 398        $nspec = array(
 399          'type' => 'new',
 400          'htype' => null,
 401          'cursor' => $ii,
 402          'line' => null,
 403          'oline' => null,
 404          'render' => null,
 405          'copy' => null,
 406          'coverage' => null,
 407        );
 408  
 409        if (isset($old[$ii])) {
 410          $ospec['line'] = (int)$old[$ii]['line'];
 411          $nspec['oline'] = (int)$old[$ii]['line'];
 412          $ospec['htype'] = $old[$ii]['type'];
 413          if (isset($old_render[$ii])) {
 414            $ospec['render'] = $old_render[$ii];
 415          }
 416        }
 417  
 418        if (isset($new[$ii])) {
 419          $nspec['line'] = (int)$new[$ii]['line'];
 420          $ospec['oline'] = (int)$new[$ii]['line'];
 421          $nspec['htype'] = $new[$ii]['type'];
 422          if (isset($new_render[$ii])) {
 423            $nspec['render'] = $new_render[$ii];
 424          }
 425        }
 426  
 427        if (isset($hunk_starts[$ospec['line']])) {
 428          $primitives[] = array(
 429            'type' => 'no-context',
 430          );
 431        }
 432  
 433        $primitives[] = $ospec;
 434        $primitives[] = $nspec;
 435  
 436        if ($ospec['line'] !== null && isset($old_comments[$ospec['line']])) {
 437          foreach ($old_comments[$ospec['line']] as $comment) {
 438            $primitives[] = array(
 439              'type' => 'inline',
 440              'comment' => $comment,
 441              'right' => false,
 442            );
 443          }
 444        }
 445  
 446        if ($nspec['line'] !== null && isset($new_comments[$nspec['line']])) {
 447          foreach ($new_comments[$nspec['line']] as $comment) {
 448            $primitives[] = array(
 449              'type' => 'inline',
 450              'comment' => $comment,
 451              'right' => true,
 452            );
 453          }
 454        }
 455  
 456        if ($hunk_starts && ($ii == $size - 1)) {
 457          $primitives[] = array(
 458            'type' => 'no-context',
 459          );
 460        }
 461      }
 462  
 463      if ($this->isOneUpRenderer()) {
 464        $primitives = $this->processPrimitivesForOneUp($primitives);
 465      }
 466  
 467      return $primitives;
 468    }
 469  
 470    private function processPrimitivesForOneUp(array $primitives) {
 471      // Primitives come out of buildPrimitives() in two-up format, because it
 472      // is the most general, flexible format. To put them into one-up format,
 473      // we need to filter and reorder them. In particular:
 474      //
 475      //   - We discard unchanged lines in the old file; in one-up format, we
 476      //     render them only once.
 477      //   - We group contiguous blocks of old-modified and new-modified lines, so
 478      //     they render in "block of old, block of new" order instead of
 479      //     alternating old and new lines.
 480  
 481      $out = array();
 482  
 483      $old_buf = array();
 484      $new_buf = array();
 485      foreach ($primitives as $primitive) {
 486        $type = $primitive['type'];
 487  
 488        if ($type == 'old') {
 489          if (!$primitive['htype']) {
 490            // This is a line which appears in both the old file and the new
 491            // file, or the spacer corresponding to a line added in the new file.
 492            // Ignore it when rendering a one-up diff.
 493            continue;
 494          }
 495          if ($new_buf) {
 496            $out[] = $new_buf;
 497            $new_buf = array();
 498          }
 499          $old_buf[] = $primitive;
 500        } else if ($type == 'new') {
 501          if ($primitive['line'] === null) {
 502            // This is an empty spacer corresponding to a line removed from the
 503            // old file. Ignore it when rendering a one-up diff.
 504            continue;
 505          }
 506          if ($old_buf) {
 507            $out[] = $old_buf;
 508            $old_buf = array();
 509          }
 510          $new_buf[] = $primitive;
 511        } else if ($type == 'context' || $type == 'no-context') {
 512          $out[] = $old_buf;
 513          $out[] = $new_buf;
 514          $old_buf = array();
 515          $new_buf = array();
 516          $out[] = array($primitive);
 517        } else if ($type == 'inline') {
 518          $out[] = $old_buf;
 519          $out[] = $new_buf;
 520          $old_buf = array();
 521          $new_buf = array();
 522  
 523          $out[] = array($primitive);
 524        } else {
 525          throw new Exception("Unknown primitive type '{$primitive}'!");
 526        }
 527      }
 528  
 529      $out[] = $old_buf;
 530      $out[] = $new_buf;
 531      $out = array_mergev($out);
 532  
 533      return $out;
 534    }
 535  
 536    protected function getChangesetProperties($changeset) {
 537      $old = $changeset->getOldProperties();
 538      $new = $changeset->getNewProperties();
 539  
 540      // When adding files, don't show the uninteresting 644 filemode change.
 541      if ($changeset->getChangeType() == DifferentialChangeType::TYPE_ADD &&
 542          $new == array('unix:filemode' => '100644')) {
 543        unset($new['unix:filemode']);
 544      }
 545  
 546      // Likewise when removing files.
 547      if ($changeset->getChangeType() == DifferentialChangeType::TYPE_DELETE &&
 548          $old == array('unix:filemode' => '100644')) {
 549        unset($old['unix:filemode']);
 550      }
 551  
 552      if ($this->hasOldFile()) {
 553        $file = $this->getOldFile();
 554        if ($file->getImageWidth()) {
 555          $dimensions = $file->getImageWidth().'x'.$file->getImageHeight();
 556          $old['file:dimensions'] = $dimensions;
 557        }
 558        $old['file:mimetype'] = $file->getMimeType();
 559        $old['file:size'] = phutil_format_bytes($file->getByteSize());
 560      }
 561  
 562      if ($this->hasNewFile()) {
 563        $file = $this->getNewFile();
 564        if ($file->getImageWidth()) {
 565          $dimensions = $file->getImageWidth().'x'.$file->getImageHeight();
 566          $new['file:dimensions'] = $dimensions;
 567        }
 568        $new['file:mimetype'] = $file->getMimeType();
 569        $new['file:size'] = phutil_format_bytes($file->getByteSize());
 570      }
 571  
 572      return array($old, $new);
 573    }
 574  
 575  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1