private function tryCacheStuff()
 {
     $whitespace_mode = $this->whitespaceMode;
     switch ($whitespace_mode) {
         case self::WHITESPACE_SHOW_ALL:
         case self::WHITESPACE_IGNORE_TRAILING:
             break;
         default:
             $whitespace_mode = self::WHITESPACE_IGNORE_ALL;
             break;
     }
     $skip_cache = $whitespace_mode != self::WHITESPACE_IGNORE_ALL;
     $this->whitespaceMode = $whitespace_mode;
     $changeset = $this->changeset;
     if ($changeset->getFileType() == DifferentialChangeType::FILE_TEXT || $changeset->getFileType() == DifferentialChangeType::FILE_SYMLINK) {
         if ($skip_cache || !$this->loadCache()) {
             $ignore_all = $this->whitespaceMode == self::WHITESPACE_IGNORE_ALL;
             if ($ignore_all && $changeset->getWhitespaceMatters()) {
                 $ignore_all = false;
             }
             // The "ignore all whitespace" algorithm depends on rediffing the
             // files, and we currently need complete representations of both
             // files to do anything reasonable. If we only have parts of the files,
             // don't use the "ignore all" algorithm.
             if ($ignore_all) {
                 $hunks = $changeset->getHunks();
                 if (count($hunks) !== 1) {
                     $ignore_all = false;
                 } else {
                     $first_hunk = reset($hunks);
                     if ($first_hunk->getOldOffset() != 1 || $first_hunk->getNewOffset() != 1) {
                         $ignore_all = false;
                     }
                 }
             }
             if ($ignore_all) {
                 $old_file = $changeset->makeOldFile();
                 $new_file = $changeset->makeNewFile();
                 if ($old_file == $new_file) {
                     // If the old and new files are exactly identical, the synthetic
                     // diff below will give us nonsense and whitespace modes are
                     // irrelevant anyway. This occurs when you, e.g., copy a file onto
                     // itself in Subversion (see T271).
                     $ignore_all = false;
                 }
             }
             if ($ignore_all) {
                 // Huge mess. Generate a "-bw" (ignore all whitespace changes) diff,
                 // parse it out, and then play a shell game with the parsed format
                 // in process() so we highlight only changed lines but render
                 // whitespace differences. If we don't do this, we either fail to
                 // render whitespace changes (which is incredibly confusing,
                 // especially for python) or often produce a much larger set of
                 // differences than necessary.
                 $engine = new PhabricatorDifferenceEngine();
                 $engine->setIgnoreWhitespace(true);
                 $no_whitespace_changeset = $engine->generateChangesetFromFileContent($old_file, $new_file);
                 // subparser takes over the current non-whitespace-ignoring changeset
                 $subparser = new DifferentialChangesetParser();
                 $subparser->isSubparser = true;
                 $subparser->setChangeset($changeset);
                 foreach ($changeset->getHunks() as $hunk) {
                     $subparser->parseHunk($hunk);
                 }
                 // We need to call process() so that the subparser's values for
                 // metadata (like 'unchanged') is correct.
                 $subparser->process();
                 $this->subparser = $subparser;
                 // While we aren't updating $this->changeset (since it has a bunch
                 // of metadata we need to preserve, so that headers like "this file
                 // was moved" render correctly), we're overwriting the local
                 // $changeset so that the block below will choose the synthetic
                 // hunks we've built instead of the original hunks.
                 $changeset = $no_whitespace_changeset;
             }
             // This either uses the real hunks, or synthetic hunks we built above.
             foreach ($changeset->getHunks() as $hunk) {
                 $this->parseHunk($hunk);
             }
             $this->process();
             if (!$skip_cache) {
                 $this->saveCache();
             }
         }
     }
 }
 private function process()
 {
     $whitespace_mode = $this->whitespaceMode;
     $changeset = $this->changeset;
     $ignore_all = $whitespace_mode == self::WHITESPACE_IGNORE_MOST || $whitespace_mode == self::WHITESPACE_IGNORE_ALL;
     $force_ignore = $whitespace_mode == self::WHITESPACE_IGNORE_ALL;
     if (!$force_ignore) {
         if ($ignore_all && $changeset->getWhitespaceMatters()) {
             $ignore_all = false;
         }
     }
     // The "ignore all whitespace" algorithm depends on rediffing the
     // files, and we currently need complete representations of both
     // files to do anything reasonable. If we only have parts of the files,
     // don't use the "ignore all" algorithm.
     if ($ignore_all) {
         $hunks = $changeset->getHunks();
         if (count($hunks) !== 1) {
             $ignore_all = false;
         } else {
             $first_hunk = reset($hunks);
             if ($first_hunk->getOldOffset() != 1 || $first_hunk->getNewOffset() != 1) {
                 $ignore_all = false;
             }
         }
     }
     if ($ignore_all) {
         $old_file = $changeset->makeOldFile();
         $new_file = $changeset->makeNewFile();
         if ($old_file == $new_file) {
             // If the old and new files are exactly identical, the synthetic
             // diff below will give us nonsense and whitespace modes are
             // irrelevant anyway. This occurs when you, e.g., copy a file onto
             // itself in Subversion (see T271).
             $ignore_all = false;
         }
     }
     $hunk_parser = new DifferentialHunkParser();
     $hunk_parser->setWhitespaceMode($whitespace_mode);
     $hunk_parser->parseHunksForLineData($changeset->getHunks());
     // Depending on the whitespace mode, we may need to compute a different
     // set of changes than the set of changes in the hunk data (specificaly,
     // we might want to consider changed lines which have only whitespace
     // changes as unchanged).
     if ($ignore_all) {
         $engine = new PhabricatorDifferenceEngine();
         $engine->setIgnoreWhitespace(true);
         $no_whitespace_changeset = $engine->generateChangesetFromFileContent($old_file, $new_file);
         $type_parser = new DifferentialHunkParser();
         $type_parser->parseHunksForLineData($no_whitespace_changeset->getHunks());
         $hunk_parser->setOldLineTypeMap($type_parser->getOldLineTypeMap());
         $hunk_parser->setNewLineTypeMap($type_parser->getNewLineTypeMap());
     }
     $hunk_parser->reparseHunksForSpecialAttributes();
     $unchanged = false;
     if (!$hunk_parser->getHasAnyChanges()) {
         $filetype = $this->changeset->getFileType();
         if ($filetype == DifferentialChangeType::FILE_TEXT || $filetype == DifferentialChangeType::FILE_SYMLINK) {
             $unchanged = true;
         }
     }
     $moveaway = false;
     $changetype = $this->changeset->getChangeType();
     if ($changetype == DifferentialChangeType::TYPE_MOVE_AWAY) {
         $moveaway = true;
     }
     $this->setSpecialAttributes(array(self::ATTR_UNCHANGED => $unchanged, self::ATTR_DELETED => $hunk_parser->getIsDeleted(), self::ATTR_WHITELINES => !$hunk_parser->getHasTextChanges(), self::ATTR_MOVEAWAY => $moveaway));
     $lines_context = $this->getLinesOfContext();
     $hunk_parser->generateIntraLineDiffs();
     $hunk_parser->generateVisibileLinesMask($lines_context);
     $this->setOldLines($hunk_parser->getOldLines());
     $this->setNewLines($hunk_parser->getNewLines());
     $this->setIntraLineDiffs($hunk_parser->getIntraLineDiffs());
     $this->setVisibileLinesMask($hunk_parser->getVisibleLinesMask());
     $this->hunkStartLines = $hunk_parser->getHunkStartLines($changeset->getHunks());
     $new_corpus = $hunk_parser->getNewCorpus();
     $new_corpus_block = implode('', $new_corpus);
     $this->markGenerated($new_corpus_block);
     if ($this->isTopLevel && !$this->comments && ($this->isGenerated() || $this->isUnchanged() || $this->isDeleted())) {
         return;
     }
     $old_corpus = $hunk_parser->getOldCorpus();
     $old_corpus_block = implode('', $old_corpus);
     $old_future = $this->getHighlightFuture($old_corpus_block);
     $new_future = $this->getHighlightFuture($new_corpus_block);
     $futures = array('old' => $old_future, 'new' => $new_future);
     $corpus_blocks = array('old' => $old_corpus_block, 'new' => $new_corpus_block);
     $this->highlightErrors = false;
     foreach (new FutureIterator($futures) as $key => $future) {
         try {
             try {
                 $highlighted = $future->resolve();
             } catch (PhutilSyntaxHighlighterException $ex) {
                 $this->highlightErrors = true;
                 $highlighted = id(new PhutilDefaultSyntaxHighlighter())->getHighlightFuture($corpus_blocks[$key])->resolve();
             }
             switch ($key) {
                 case 'old':
                     $this->oldRender = $this->processHighlightedSource($this->old, $highlighted);
                     break;
                 case 'new':
                     $this->newRender = $this->processHighlightedSource($this->new, $highlighted);
                     break;
             }
         } catch (Exception $ex) {
             phlog($ex);
             throw $ex;
         }
     }
     $this->applyIntraline($this->oldRender, ipull($this->intra, 0), $old_corpus);
     $this->applyIntraline($this->newRender, ipull($this->intra, 1), $new_corpus);
 }