public function render()
 {
     require_celerity_resource('differential-changeset-view-css');
     $changesets = $this->changesets;
     $output = array();
     $mapping = array();
     foreach ($changesets as $key => $changeset) {
         $file = $changeset->getFilename();
         $class = 'differential-changeset';
         if (!$this->editable) {
             $class .= ' differential-changeset-noneditable';
         }
         $ref = $this->references[$key];
         $detail_button = null;
         if ($this->standaloneViews) {
             $detail_uri = new PhutilURI($this->renderURI);
             $detail_uri->setQueryParams(array('ref' => $ref, 'whitespace' => $this->whitespace));
             $detail_button = phutil_render_tag('a', array('class' => 'button small grey', 'href' => $detail_uri, 'target' => '_blank'), 'View Standalone / Raw');
         }
         $uniq_id = celerity_generate_unique_node_id();
         $detail = new DifferentialChangesetDetailView();
         $detail->setChangeset($changeset);
         $detail->addButton($detail_button);
         $detail->appendChild(phutil_render_tag('div', array('id' => $uniq_id), '<div class="differential-loading">Loading...</div>'));
         $output[] = $detail->render();
         $mapping[$uniq_id] = $ref;
     }
     Javelin::initBehavior('differential-populate', array('registry' => $mapping, 'whitespace' => $this->whitespace, 'uri' => $this->renderURI));
     Javelin::initBehavior('differential-show-more', array('uri' => $this->renderURI, 'whitespace' => $this->whitespace));
     Javelin::initBehavior('differential-comment-jump', array());
     if ($this->editable) {
         $undo_templates = $this->renderUndoTemplates();
         $revision = $this->revision;
         Javelin::initBehavior('differential-edit-inline-comments', array('uri' => '/differential/comment/inline/edit/' . $revision->getID() . '/', 'undo_templates' => $undo_templates));
     }
     return '<div class="differential-review-stage" id="differential-review-stage">' . implode("\n", $output) . '</div>';
 }
 private function renderViewOptionsDropdown(DifferentialChangesetDetailView $detail, $ref, DifferentialChangeset $changeset)
 {
     $meta = array();
     $qparams = array('ref' => $ref, 'whitespace' => $this->whitespace);
     if ($this->standaloneURI) {
         $uri = new PhutilURI($this->standaloneURI);
         $uri->setQueryParams($uri->getQueryParams() + $qparams);
         $meta['standaloneURI'] = (string) $uri;
     }
     $repository = $this->repository;
     if ($repository) {
         try {
             $meta['diffusionURI'] = (string) $repository->getDiffusionBrowseURIForPath($this->user, $changeset->getAbsoluteRepositoryPath($repository, $this->diff), idx($changeset->getMetadata(), 'line:first'), $this->getBranch());
         } catch (DiffusionSetupException $e) {
             // Ignore
         }
     }
     $change = $changeset->getChangeType();
     if ($this->leftRawFileURI) {
         if ($change != DifferentialChangeType::TYPE_ADD) {
             $uri = new PhutilURI($this->leftRawFileURI);
             $uri->setQueryParams($uri->getQueryParams() + $qparams);
             $meta['leftURI'] = (string) $uri;
         }
     }
     if ($this->rightRawFileURI) {
         if ($change != DifferentialChangeType::TYPE_DELETE && $change != DifferentialChangeType::TYPE_MULTICOPY) {
             $uri = new PhutilURI($this->rightRawFileURI);
             $uri->setQueryParams($uri->getQueryParams() + $qparams);
             $meta['rightURI'] = (string) $uri;
         }
     }
     $user = $this->user;
     if ($user && $repository) {
         $path = ltrim($changeset->getAbsoluteRepositoryPath($repository, $this->diff), '/');
         $line = idx($changeset->getMetadata(), 'line:first', 1);
         $callsign = $repository->getCallsign();
         $editor_link = $user->loadEditorLink($path, $line, $callsign);
         if ($editor_link) {
             $meta['editor'] = $editor_link;
         } else {
             $meta['editorConfigure'] = '/settings/panel/display/';
         }
     }
     $meta['containerID'] = $detail->getID();
     $caret = phutil_tag('span', array('class' => 'caret'), '');
     return javelin_tag('a', array('class' => 'button grey small dropdown', 'meta' => $meta, 'href' => idx($meta, 'detailURI', '#'), 'target' => '_blank', 'sigil' => 'differential-view-options'), array(pht('View Options'), $caret));
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $author_phid = $request->getUser()->getPHID();
     $rendering_reference = $request->getStr('ref');
     $parts = explode('/', $rendering_reference);
     if (count($parts) == 2) {
         list($id, $vs) = $parts;
     } else {
         $id = $parts[0];
         $vs = 0;
     }
     $id = (int) $id;
     $vs = (int) $vs;
     $changeset = id(new DifferentialChangeset())->load($id);
     if (!$changeset) {
         return new Aphront404Response();
     }
     $view = $request->getStr('view');
     if ($view) {
         $changeset->attachHunks($changeset->loadHunks());
         switch ($view) {
             case 'new':
                 return $this->buildRawFileResponse($changeset->makeNewFile());
             case 'old':
                 return $this->buildRawFileResponse($changeset->makeOldFile());
             default:
                 return new Aphront400Response();
         }
     }
     if ($vs && $vs != -1) {
         $vs_changeset = id(new DifferentialChangeset())->load($vs);
         if (!$vs_changeset) {
             return new Aphront404Response();
         }
     }
     if (!$vs) {
         $right = $changeset;
         $left = null;
         $right_source = $right->getID();
         $right_new = true;
         $left_source = $right->getID();
         $left_new = false;
         $render_cache_key = $right->getID();
     } else {
         if ($vs == -1) {
             $right = null;
             $left = $changeset;
             $right_source = $left->getID();
             $right_new = false;
             $left_source = $left->getID();
             $left_new = true;
             $render_cache_key = null;
         } else {
             $right = $changeset;
             $left = $vs_changeset;
             $right_source = $right->getID();
             $right_new = true;
             $left_source = $left->getID();
             $left_new = true;
             $render_cache_key = null;
         }
     }
     if ($left) {
         $left->attachHunks($left->loadHunks());
     }
     if ($right) {
         $right->attachHunks($right->loadHunks());
     }
     if ($left) {
         $left_data = $left->makeNewFile();
         if ($right) {
             $right_data = $right->makeNewFile();
         } else {
             $right_data = $left->makeOldFile();
         }
         $engine = new PhabricatorDifferenceEngine();
         $synthetic = $engine->generateChangesetFromFileContent($left_data, $right_data);
         $choice = nonempty($left, $right);
         $choice->attachHunks($synthetic->getHunks());
         $changeset = $choice;
         $changeset->setID(null);
     }
     $spec = $request->getStr('range');
     list($range_s, $range_e, $mask) = DifferentialChangesetParser::parseRangeSpecification($spec);
     $parser = new DifferentialChangesetParser();
     $parser->setChangeset($changeset);
     $parser->setRenderingReference($rendering_reference);
     $parser->setRenderCacheKey($render_cache_key);
     $parser->setRightSideCommentMapping($right_source, $right_new);
     $parser->setLeftSideCommentMapping($left_source, $left_new);
     $parser->setWhitespaceMode($request->getStr('whitespace'));
     // Load both left-side and right-side inline comments.
     $inlines = $this->loadInlineComments(array($left_source, $right_source), $author_phid);
     $phids = array();
     foreach ($inlines as $inline) {
         $parser->parseInlineComment($inline);
         $phids[$inline->getAuthorPHID()] = true;
     }
     $phids = array_keys($phids);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $parser->setHandles($handles);
     $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
     $parser->setMarkupEngine($engine);
     if ($request->isAjax()) {
         // TODO: This is sort of lazy, the effect is just to not render "Edit"
         // links on the "standalone view".
         $parser->setUser($request->getUser());
     }
     $output = $parser->render($range_s, $range_e, $mask);
     if ($request->isAjax()) {
         return id(new AphrontAjaxResponse())->setContent($output);
     }
     Javelin::initBehavior('differential-show-more', array('uri' => '/differential/changeset/', 'whitespace' => $request->getStr('whitespace')));
     Javelin::initBehavior('differential-comment-jump', array());
     $detail = new DifferentialChangesetDetailView();
     $detail->setChangeset($changeset);
     $detail->appendChild($output);
     if (!$vs) {
         $detail->addButton(phutil_render_tag('a', array('href' => $request->getRequestURI()->alter('view', 'old'), 'class' => 'grey button small'), 'View Raw File (Old Version)'));
         $detail->addButton(phutil_render_tag('a', array('href' => $request->getRequestURI()->alter('view', 'new'), 'class' => 'grey button small'), 'View Raw File (New Version)'));
     }
     $detail->setRevisionID($request->getInt('revision_id'));
     $output = id(new DifferentialPrimaryPaneView())->setLineWidthFromChangesets(array($changeset))->appendChild('<div class="differential-review-stage" ' . 'id="differential-review-stage">' . $detail->render() . '</div>');
     return $this->buildStandardPageResponse(array($output), array('title' => 'Changeset View'));
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $author_phid = $request->getUser()->getPHID();
     $rendering_reference = $request->getStr('ref');
     $parts = explode('/', $rendering_reference);
     if (count($parts) == 2) {
         list($id, $vs) = $parts;
     } else {
         $id = $parts[0];
         $vs = 0;
     }
     $id = (int) $id;
     $vs = (int) $vs;
     $changeset = id(new DifferentialChangeset())->load($id);
     if (!$changeset) {
         return new Aphront404Response();
     }
     $view = $request->getStr('view');
     if ($view) {
         $changeset->attachHunks($changeset->loadHunks());
         $phid = idx($changeset->getMetadata(), "{$view}:binary-phid");
         if ($phid) {
             return id(new AphrontRedirectResponse())->setURI("/file/info/{$phid}/");
         }
         switch ($view) {
             case 'new':
                 return $this->buildRawFileResponse($changeset, $is_new = true);
             case 'old':
                 if ($vs && $vs != -1) {
                     $vs_changeset = id(new DifferentialChangeset())->load($vs);
                     if ($vs_changeset) {
                         $vs_changeset->attachHunks($vs_changeset->loadHunks());
                         return $this->buildRawFileResponse($vs_changeset, $is_new = true);
                     }
                 }
                 return $this->buildRawFileResponse($changeset, $is_new = false);
             default:
                 return new Aphront400Response();
         }
     }
     if ($vs && $vs != -1) {
         $vs_changeset = id(new DifferentialChangeset())->load($vs);
         if (!$vs_changeset) {
             return new Aphront404Response();
         }
     }
     if (!$vs) {
         $right = $changeset;
         $left = null;
         $right_source = $right->getID();
         $right_new = true;
         $left_source = $right->getID();
         $left_new = false;
         $render_cache_key = $right->getID();
     } else {
         if ($vs == -1) {
             $right = null;
             $left = $changeset;
             $right_source = $left->getID();
             $right_new = false;
             $left_source = $left->getID();
             $left_new = true;
             $render_cache_key = null;
         } else {
             $right = $changeset;
             $left = $vs_changeset;
             $right_source = $right->getID();
             $right_new = true;
             $left_source = $left->getID();
             $left_new = true;
             $render_cache_key = null;
         }
     }
     if ($left) {
         $left->attachHunks($left->loadHunks());
     }
     if ($right) {
         $right->attachHunks($right->loadHunks());
     }
     if ($left) {
         $left_data = $left->makeNewFile();
         if ($right) {
             $right_data = $right->makeNewFile();
         } else {
             $right_data = $left->makeOldFile();
         }
         $engine = new PhabricatorDifferenceEngine();
         $synthetic = $engine->generateChangesetFromFileContent($left_data, $right_data);
         $choice = clone nonempty($left, $right);
         $choice->attachHunks($synthetic->getHunks());
         $changeset = $choice;
     }
     $coverage = null;
     if ($right && $right->getDiffID()) {
         $unit = id(new DifferentialDiffProperty())->loadOneWhere('diffID = %d AND name = %s', $right->getDiffID(), 'arc:unit');
         if ($unit) {
             $coverage = array();
             foreach ($unit->getData() as $result) {
                 $result_coverage = idx($result, 'coverage');
                 if (!$result_coverage) {
                     continue;
                 }
                 $file_coverage = idx($result_coverage, $right->getFileName());
                 if (!$file_coverage) {
                     continue;
                 }
                 $coverage[] = $file_coverage;
             }
             $coverage = ArcanistUnitTestResult::mergeCoverage($coverage);
         }
     }
     $spec = $request->getStr('range');
     list($range_s, $range_e, $mask) = DifferentialChangesetParser::parseRangeSpecification($spec);
     $parser = new DifferentialChangesetParser();
     $parser->setCoverage($coverage);
     $parser->setChangeset($changeset);
     $parser->setRenderingReference($rendering_reference);
     $parser->setRenderCacheKey($render_cache_key);
     $parser->setRightSideCommentMapping($right_source, $right_new);
     $parser->setLeftSideCommentMapping($left_source, $left_new);
     $parser->setWhitespaceMode($request->getStr('whitespace'));
     if ($left && $right) {
         $parser->setOriginals($left, $right);
     }
     // Load both left-side and right-side inline comments.
     $inlines = $this->loadInlineComments(array($left_source, $right_source), $author_phid);
     if ($left_new) {
         $inlines = array_merge($inlines, $this->buildLintInlineComments($left));
     }
     if ($right_new) {
         $inlines = array_merge($inlines, $this->buildLintInlineComments($right));
     }
     $phids = array();
     foreach ($inlines as $inline) {
         $parser->parseInlineComment($inline);
         if ($inline->getAuthorPHID()) {
             $phids[$inline->getAuthorPHID()] = true;
         }
     }
     $phids = array_keys($phids);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $parser->setHandles($handles);
     $engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
     $parser->setMarkupEngine($engine);
     if ($request->isAjax()) {
         // TODO: This is sort of lazy, the effect is just to not render "Edit"
         // and "Reply" links on the "standalone view".
         $parser->setUser($request->getUser());
     }
     $output = $parser->render($range_s, $range_e, $mask);
     $mcov = $parser->renderModifiedCoverage();
     if ($request->isAjax()) {
         $coverage = array('differential-mcoverage-' . md5($changeset->getFilename()) => $mcov);
         return id(new PhabricatorChangesetResponse())->setRenderedChangeset($output)->setCoverage($coverage);
     }
     Javelin::initBehavior('differential-show-more', array('uri' => '/differential/changeset/', 'whitespace' => $request->getStr('whitespace')));
     Javelin::initBehavior('differential-comment-jump', array());
     $detail = new DifferentialChangesetDetailView();
     $detail->setChangeset($changeset);
     $detail->appendChild($output);
     $detail->setVsChangesetID($left_source);
     $output = id(new DifferentialPrimaryPaneView())->setLineWidthFromChangesets(array($changeset))->appendChild('<div class="differential-review-stage" ' . 'id="differential-review-stage">' . $detail->render() . '</div>');
     return $this->buildStandardPageResponse(array($output), array('title' => 'Changeset View'));
 }
 public function render()
 {
     require_celerity_resource('differential-changeset-view-css');
     $changesets = $this->changesets;
     if ($this->standaloneViews) {
         Javelin::initBehavior('differential-dropdown-menus', array());
     }
     $output = array();
     $mapping = array();
     $repository = $this->repository;
     foreach ($changesets as $key => $changeset) {
         $file = $changeset->getFilename();
         $class = 'differential-changeset';
         if (!$this->editable) {
             $class .= ' differential-changeset-noneditable';
         }
         $ref = $this->references[$key];
         $detail = new DifferentialChangesetDetailView();
         $detail_button = null;
         if ($this->standaloneViews) {
             $detail_uri = new PhutilURI($this->renderURI);
             $detail_uri->setQueryParams(array('ref' => $ref, 'whitespace' => $this->whitespace));
             $diffusion_uri = null;
             if ($repository) {
                 $diffusion_uri = $repository->getDiffusionBrowseURIForPath($changeset->getAbsoluteRepositoryPath($this->diff, $repository));
             }
             $detail_button = javelin_render_tag('a', array('class' => 'button small grey', 'meta' => array('detailURI' => (string) $detail_uri, 'leftURI' => (string) $detail_uri->alter('view', 'old'), 'rightURI' => (string) $detail_uri->alter('view', 'new'), 'diffusionURI' => $diffusion_uri, 'containerID' => $detail->getID()), 'href' => $detail_uri, 'target' => '_blank', 'sigil' => 'differential-view-options'), "View Options ▼");
         }
         $detail->setChangeset($changeset);
         $detail->addButton($detail_button);
         $detail->setSymbolIndex(idx($this->symbolIndexes, $key));
         $uniq_id = celerity_generate_unique_node_id();
         $detail->appendChild(phutil_render_tag('div', array('id' => $uniq_id), '<div class="differential-loading">Loading...</div>'));
         $output[] = $detail->render();
         $mapping[$uniq_id] = $ref;
     }
     Javelin::initBehavior('differential-populate', array('registry' => $mapping, 'whitespace' => $this->whitespace, 'uri' => $this->renderURI));
     Javelin::initBehavior('differential-show-more', array('uri' => $this->renderURI, 'whitespace' => $this->whitespace));
     Javelin::initBehavior('differential-comment-jump', array());
     if ($this->editable) {
         $undo_templates = $this->renderUndoTemplates();
         $revision = $this->revision;
         Javelin::initBehavior('differential-edit-inline-comments', array('uri' => '/differential/comment/inline/edit/' . $revision->getID() . '/', 'undo_templates' => $undo_templates));
     }
     return '<div class="differential-review-stage" id="differential-review-stage">' . implode("\n", $output) . '</div>';
 }
 private function renderViewOptionsDropdown(DifferentialChangesetDetailView $detail, $ref, DifferentialChangeset $changeset)
 {
     $meta = array();
     $qparams = array('ref' => $ref, 'whitespace' => $this->whitespace);
     if ($this->standaloneURI) {
         $uri = new PhutilURI($this->standaloneURI);
         $uri->setQueryParams($uri->getQueryParams() + $qparams);
         $meta['standaloneURI'] = (string) $uri;
     }
     $repository = $this->repository;
     if ($repository) {
         $meta['diffusionURI'] = (string) $repository->getDiffusionBrowseURIForPath($changeset->getAbsoluteRepositoryPath($repository, $this->diff));
     }
     $change = $changeset->getChangeType();
     if ($this->leftRawFileURI) {
         if ($change != DifferentialChangeType::TYPE_ADD) {
             $uri = new PhutilURI($this->leftRawFileURI);
             $uri->setQueryParams($uri->getQueryParams() + $qparams);
             $meta['leftURI'] = (string) $uri;
         }
     }
     if ($this->rightRawFileURI) {
         if ($change != DifferentialChangeType::TYPE_DELETE && $change != DifferentialChangeType::TYPE_MULTICOPY) {
             $uri = new PhutilURI($this->rightRawFileURI);
             $uri->setQueryParams($uri->getQueryParams() + $qparams);
             $meta['rightURI'] = (string) $uri;
         }
     }
     $user = $this->user;
     if ($user && $repository) {
         $path = ltrim($changeset->getAbsoluteRepositoryPath($repository, $this->diff), '/');
         $line = 1;
         // TODO: get first changed line
         $callsign = $repository->getCallsign();
         $editor_link = $user->loadEditorLink($path, $line, $callsign);
         if ($editor_link) {
             $meta['editor'] = $editor_link;
         } else {
             $meta['editorConfigure'] = '/settings/page/preferences/';
         }
     }
     $meta['containerID'] = $detail->getID();
     Javelin::initBehavior('differential-dropdown-menus', array());
     return javelin_render_tag('a', array('class' => 'button small grey', 'meta' => $meta, 'href' => idx($meta, 'detailURI', '#'), 'target' => '_blank', 'sigil' => 'differential-view-options'), "View Options ▼");
 }
 private function renderViewOptionsDropdown(DifferentialChangesetDetailView $detail, $ref, DifferentialChangeset $changeset)
 {
     $viewer = $this->getViewer();
     $meta = array();
     $qparams = array('ref' => $ref, 'whitespace' => $this->whitespace);
     if ($this->standaloneURI) {
         $uri = new PhutilURI($this->standaloneURI);
         $uri->setQueryParams($uri->getQueryParams() + $qparams);
         $meta['standaloneURI'] = (string) $uri;
     }
     $repository = $this->repository;
     if ($repository) {
         try {
             $meta['diffusionURI'] = (string) $repository->getDiffusionBrowseURIForPath($viewer, $changeset->getAbsoluteRepositoryPath($repository, $this->diff), idx($changeset->getMetadata(), 'line:first'), $this->getBranch());
         } catch (DiffusionSetupException $e) {
             // Ignore
         }
     }
     $change = $changeset->getChangeType();
     if ($this->leftRawFileURI) {
         if ($change != DifferentialChangeType::TYPE_ADD) {
             $uri = new PhutilURI($this->leftRawFileURI);
             $uri->setQueryParams($uri->getQueryParams() + $qparams);
             $meta['leftURI'] = (string) $uri;
         }
     }
     if ($this->rightRawFileURI) {
         if ($change != DifferentialChangeType::TYPE_DELETE && $change != DifferentialChangeType::TYPE_MULTICOPY) {
             $uri = new PhutilURI($this->rightRawFileURI);
             $uri->setQueryParams($uri->getQueryParams() + $qparams);
             $meta['rightURI'] = (string) $uri;
         }
     }
     if ($viewer && $repository) {
         $path = ltrim($changeset->getAbsoluteRepositoryPath($repository, $this->diff), '/');
         $line = idx($changeset->getMetadata(), 'line:first', 1);
         $editor_link = $viewer->loadEditorLink($path, $line, $repository);
         if ($editor_link) {
             $meta['editor'] = $editor_link;
         } else {
             $meta['editorConfigure'] = '/settings/panel/display/';
         }
     }
     $meta['containerID'] = $detail->getID();
     return id(new PHUIButtonView())->setTag('a')->setText(pht('View Options'))->setIcon('fa-bars')->setColor(PHUIButtonView::GREY)->setHref(idx($meta, 'detailURI', '#'))->setMetadata($meta)->addSigil('differential-view-options');
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $author_phid = $request->getUser()->getPHID();
     $rendering_reference = $request->getStr('ref');
     $parts = explode('/', $rendering_reference);
     if (count($parts) == 2) {
         list($id, $vs) = $parts;
     } else {
         $id = $parts[0];
         $vs = 0;
     }
     $id = (int) $id;
     $vs = (int) $vs;
     $load_ids = array($id);
     if ($vs && $vs != -1) {
         $load_ids[] = $vs;
     }
     $changesets = id(new DifferentialChangesetQuery())->setViewer($request->getUser())->withIDs($load_ids)->needHunks(true)->execute();
     $changesets = mpull($changesets, null, 'getID');
     $changeset = idx($changesets, $id);
     if (!$changeset) {
         return new Aphront404Response();
     }
     $vs_changeset = null;
     if ($vs && $vs != -1) {
         $vs_changeset = idx($changesets, $vs);
         if (!$vs_changeset) {
             return new Aphront404Response();
         }
     }
     $view = $request->getStr('view');
     if ($view) {
         $phid = idx($changeset->getMetadata(), "{$view}:binary-phid");
         if ($phid) {
             return id(new AphrontRedirectResponse())->setURI("/file/info/{$phid}/");
         }
         switch ($view) {
             case 'new':
                 return $this->buildRawFileResponse($changeset, $is_new = true);
             case 'old':
                 if ($vs_changeset) {
                     return $this->buildRawFileResponse($vs_changeset, $is_new = true);
                 }
                 return $this->buildRawFileResponse($changeset, $is_new = false);
             default:
                 return new Aphront400Response();
         }
     }
     if (!$vs) {
         $right = $changeset;
         $left = null;
         $right_source = $right->getID();
         $right_new = true;
         $left_source = $right->getID();
         $left_new = false;
         $render_cache_key = $right->getID();
     } else {
         if ($vs == -1) {
             $right = null;
             $left = $changeset;
             $right_source = $left->getID();
             $right_new = false;
             $left_source = $left->getID();
             $left_new = true;
             $render_cache_key = null;
         } else {
             $right = $changeset;
             $left = $vs_changeset;
             $right_source = $right->getID();
             $right_new = true;
             $left_source = $left->getID();
             $left_new = true;
             $render_cache_key = null;
         }
     }
     if ($left) {
         $left_data = $left->makeNewFile();
         if ($right) {
             $right_data = $right->makeNewFile();
         } else {
             $right_data = $left->makeOldFile();
         }
         $engine = new PhabricatorDifferenceEngine();
         $synthetic = $engine->generateChangesetFromFileContent($left_data, $right_data);
         $choice = clone nonempty($left, $right);
         $choice->attachHunks($synthetic->getHunks());
         $changeset = $choice;
     }
     $coverage = null;
     if ($right && $right->getDiffID()) {
         $unit = id(new DifferentialDiffProperty())->loadOneWhere('diffID = %d AND name = %s', $right->getDiffID(), 'arc:unit');
         if ($unit) {
             $coverage = array();
             foreach ($unit->getData() as $result) {
                 $result_coverage = idx($result, 'coverage');
                 if (!$result_coverage) {
                     continue;
                 }
                 $file_coverage = idx($result_coverage, $right->getFileName());
                 if (!$file_coverage) {
                     continue;
                 }
                 $coverage[] = $file_coverage;
             }
             $coverage = ArcanistUnitTestResult::mergeCoverage($coverage);
         }
     }
     $spec = $request->getStr('range');
     list($range_s, $range_e, $mask) = DifferentialChangesetParser::parseRangeSpecification($spec);
     $parser = new DifferentialChangesetParser();
     $parser->setCoverage($coverage);
     $parser->setChangeset($changeset);
     $parser->setRenderingReference($rendering_reference);
     $parser->setRenderCacheKey($render_cache_key);
     $parser->setRightSideCommentMapping($right_source, $right_new);
     $parser->setLeftSideCommentMapping($left_source, $left_new);
     $parser->setWhitespaceMode($request->getStr('whitespace'));
     $parser->setCharacterEncoding($request->getStr('encoding'));
     $parser->setHighlightAs($request->getStr('highlight'));
     if ($request->getStr('renderer') == '1up') {
         $parser->setRenderer(new DifferentialChangesetOneUpRenderer());
     }
     if ($left && $right) {
         $parser->setOriginals($left, $right);
     }
     // Load both left-side and right-side inline comments.
     $inlines = $this->loadInlineComments(array($left_source, $right_source), $author_phid);
     if ($left_new) {
         $inlines = array_merge($inlines, $this->buildLintInlineComments($left));
     }
     if ($right_new) {
         $inlines = array_merge($inlines, $this->buildLintInlineComments($right));
     }
     $phids = array();
     foreach ($inlines as $inline) {
         $parser->parseInlineComment($inline);
         if ($inline->getAuthorPHID()) {
             $phids[$inline->getAuthorPHID()] = true;
         }
     }
     $phids = array_keys($phids);
     $handles = $this->loadViewerHandles($phids);
     $parser->setHandles($handles);
     $engine = new PhabricatorMarkupEngine();
     $engine->setViewer($request->getUser());
     foreach ($inlines as $inline) {
         $engine->addObject($inline, PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
     }
     $engine->process();
     $parser->setMarkupEngine($engine);
     if ($request->isAjax()) {
         // TODO: This is sort of lazy, the effect is just to not render "Edit"
         // and "Reply" links on the "standalone view".
         $parser->setUser($request->getUser());
     }
     $output = $parser->render($range_s, $range_e, $mask);
     $mcov = $parser->renderModifiedCoverage();
     if ($request->isAjax()) {
         $coverage = array('differential-mcoverage-' . md5($changeset->getFilename()) => $mcov);
         return id(new PhabricatorChangesetResponse())->setRenderedChangeset($output)->setCoverage($coverage);
     }
     Javelin::initBehavior('differential-show-more', array('uri' => '/differential/changeset/', 'whitespace' => $request->getStr('whitespace')));
     Javelin::initBehavior('differential-comment-jump', array());
     // TODO: [HTML] Clean up DifferentialChangesetParser output, but it's
     // undergoing like six kinds of refactoring anyway.
     $output = phutil_safe_html($output);
     $detail = new DifferentialChangesetDetailView();
     $detail->setChangeset($changeset);
     $detail->appendChild($output);
     $detail->setVsChangesetID($left_source);
     $panel = new DifferentialPrimaryPaneView();
     $panel->appendChild(phutil_tag('div', array('class' => 'differential-review-stage', 'id' => 'differential-review-stage'), $detail->render()));
     $crumbs = $this->buildApplicationCrumbs();
     $revision_id = $changeset->getDiff()->getRevisionID();
     if ($revision_id) {
         $crumbs->addTextCrumb('D' . $revision_id, '/D' . $revision_id);
     }
     $diff_id = $changeset->getDiff()->getID();
     if ($diff_id) {
         $crumbs->addTextCrumb(pht('Diff %d', $diff_id), $this->getApplicationURI('diff/' . $diff_id));
     }
     $crumbs->addTextCrumb($changeset->getDisplayFilename());
     $box = id(new PHUIObjectBoxView())->setHeaderText(pht('Standalone View'))->appendChild($panel);
     return $this->buildApplicationPage(array($crumbs, $box), array('title' => pht('Changeset View'), 'device' => false));
 }