Path: blob/master/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
12256 views
<?php12final class DifferentialChangesetTwoUpRenderer3extends DifferentialChangesetHTMLRenderer {45private $newOffsetMap;67public function isOneUpRenderer() {8return false;9}1011protected function getRendererTableClass() {12return 'diff-2up';13}1415public function getRendererKey() {16return '2up';17}1819protected function renderColgroup() {20return phutil_tag('colgroup', array(), array(21phutil_tag('col', array('class' => 'num')),22phutil_tag('col', array('class' => 'left')),23phutil_tag('col', array('class' => 'num')),24phutil_tag('col', array('class' => 'copy')),25phutil_tag('col', array('class' => 'right')),26phutil_tag('col', array('class' => 'cov')),27));28}2930public function renderTextChange(31$range_start,32$range_len,33$rows) {3435$hunk_starts = $this->getHunkStartLines();3637$context_not_available = null;38if ($hunk_starts) {39$context_not_available = javelin_tag(40'tr',41array(42'sigil' => 'context-target',43),44phutil_tag(45'td',46array(47'colspan' => 6,48'class' => 'show-more',49),50pht('Context not available.')));51}5253$html = array();5455$old_lines = $this->getOldLines();56$new_lines = $this->getNewLines();57$gaps = $this->getGaps();58$reference = $this->getRenderingReference();5960list($left_prefix, $right_prefix) = $this->getLineIDPrefixes();6162$changeset = $this->getChangeset();63$copy_lines = idx($changeset->getMetadata(), 'copy:lines', array());64$highlight_old = $this->getHighlightOld();65$highlight_new = $this->getHighlightNew();66$old_render = $this->getOldRender();67$new_render = $this->getNewRender();68$original_left = $this->getOriginalOld();69$original_right = $this->getOriginalNew();70$mask = $this->getMask();7172$scope_engine = $this->getScopeEngine();73$offset_map = null;74$depth_only = $this->getDepthOnlyLines();7576for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {77if (empty($mask[$ii])) {78// If we aren't going to show this line, we've just entered a gap.79// Pop information about the next gap off the $gaps stack and render80// an appropriate "Show more context" element. This branch eventually81// increments $ii by the entire size of the gap and then continues82// the loop.83$gap = array_pop($gaps);84$top = $gap[0];85$len = $gap[1];8687$contents = $this->renderShowContextLinks($top, $len, $rows);8889$is_last_block = false;90if ($ii + $len >= $rows) {91$is_last_block = true;92}9394$context_text = null;95$context_line = null;96if (!$is_last_block && $scope_engine) {97$target_line = $new_lines[$ii + $len]['line'];98$context_line = $scope_engine->getScopeStart($target_line);99if ($context_line !== null) {100// The scope engine returns a line number in the file. We need101// to map that back to a display offset in the diff.102if (!$offset_map) {103$offset_map = $this->getNewLineToOffsetMap();104}105$offset = $offset_map[$context_line];106$context_text = $new_render[$offset];107}108}109110$container = javelin_tag(111'tr',112array(113'sigil' => 'context-target',114),115array(116phutil_tag(117'td',118array(119'class' => 'show-context-line n left-context',120)),121phutil_tag(122'td',123array(124'class' => 'show-more',125),126$contents),127phutil_tag(128'td',129array(130'class' => 'show-context-line n',131'data-n' => $context_line,132)),133phutil_tag(134'td',135array(136'colspan' => 3,137'class' => 'show-context',138),139// TODO: [HTML] Escaping model here isn't ideal.140phutil_safe_html($context_text)),141));142143$html[] = $container;144145$ii += ($len - 1);146continue;147}148149$o_num = null;150$o_classes = '';151$o_text = null;152if (isset($old_lines[$ii])) {153$o_num = $old_lines[$ii]['line'];154$o_text = isset($old_render[$ii]) ? $old_render[$ii] : null;155if ($old_lines[$ii]['type']) {156if ($old_lines[$ii]['type'] == '\\') {157$o_text = $old_lines[$ii]['text'];158$o_class = 'comment';159} else if ($original_left && !isset($highlight_old[$o_num])) {160$o_class = 'old-rebase';161} else if (empty($new_lines[$ii])) {162$o_class = 'old old-full';163} else {164if (isset($depth_only[$ii])) {165if ($depth_only[$ii] == '>') {166// When a line has depth-only change, we only highlight the167// left side of the diff if the depth is decreasing. When the168// depth is increasing, the ">>" marker on the right hand side169// of the diff generally provides enough visibility on its own.170171$o_class = '';172} else {173$o_class = 'old';174}175} else {176$o_class = 'old';177}178}179$o_classes = $o_class;180}181}182183$n_copy = hsprintf('<td class="copy" />');184$n_cov = null;185$n_colspan = 2;186$n_classes = '';187$n_num = null;188$n_text = null;189190if (isset($new_lines[$ii])) {191$n_num = $new_lines[$ii]['line'];192$n_text = isset($new_render[$ii]) ? $new_render[$ii] : null;193$coverage = $this->getCodeCoverage();194195if ($coverage !== null) {196if (empty($coverage[$n_num - 1])) {197$cov_class = 'N';198} else {199$cov_class = $coverage[$n_num - 1];200}201$cov_class = 'cov-'.$cov_class;202$n_cov = phutil_tag('td', array('class' => "cov {$cov_class}"));203$n_colspan--;204}205206if ($new_lines[$ii]['type']) {207if ($new_lines[$ii]['type'] == '\\') {208$n_text = $new_lines[$ii]['text'];209$n_class = 'comment';210} else if ($original_right && !isset($highlight_new[$n_num])) {211$n_class = 'new-rebase';212} else if (empty($old_lines[$ii])) {213$n_class = 'new new-full';214} else {215// When a line has a depth-only change, never highlight it on216// the right side. The ">>" marker generally provides enough217// visibility on its own for indent depth increases, and the left218// side is still highlighted for indent depth decreases.219220if (isset($depth_only[$ii])) {221$n_class = '';222} else {223$n_class = 'new';224}225}226$n_classes = $n_class;227228$not_copied =229// If this line only changed depth, copy markers are pointless.230(!isset($copy_lines[$n_num])) ||231(isset($depth_only[$ii])) ||232($new_lines[$ii]['type'] == '\\');233234if ($not_copied) {235$n_copy = phutil_tag('td', array('class' => 'copy'));236} else {237list($orig_file, $orig_line, $orig_type) = $copy_lines[$n_num];238$title = ($orig_type == '-' ? 'Moved' : 'Copied').' from ';239if ($orig_file == '') {240$title .= "line {$orig_line}";241} else {242$title .=243basename($orig_file).244":{$orig_line} in dir ".245dirname('/'.$orig_file);246}247$class = ($orig_type == '-' ? 'new-move' : 'new-copy');248$n_copy = javelin_tag(249'td',250array(251'meta' => array(252'msg' => $title,253),254'class' => 'copy '.$class,255));256}257}258}259260if (isset($hunk_starts[$o_num])) {261$html[] = $context_not_available;262}263264if ($o_num && $left_prefix) {265$o_id = $left_prefix.$o_num;266} else {267$o_id = null;268}269270if ($n_num && $right_prefix) {271$n_id = $right_prefix.$n_num;272} else {273$n_id = null;274}275276$old_comments = $this->getOldComments();277$new_comments = $this->getNewComments();278$scaffolds = array();279280if ($o_num && isset($old_comments[$o_num])) {281foreach ($old_comments[$o_num] as $comment) {282$inline = $this->buildInlineComment(283$comment,284$on_right = false);285$scaffold = $this->getRowScaffoldForInline($inline);286287if ($n_num && isset($new_comments[$n_num])) {288foreach ($new_comments[$n_num] as $key => $new_comment) {289if ($comment->isCompatible($new_comment)) {290$companion = $this->buildInlineComment(291$new_comment,292$on_right = true);293294$scaffold->addInlineView($companion);295unset($new_comments[$n_num][$key]);296break;297}298}299}300301302$scaffolds[] = $scaffold;303}304}305306if ($n_num && isset($new_comments[$n_num])) {307foreach ($new_comments[$n_num] as $comment) {308$inline = $this->buildInlineComment(309$comment,310$on_right = true);311312$scaffolds[] = $this->getRowScaffoldForInline($inline);313}314}315316$old_number = phutil_tag(317'td',318array(319'id' => $o_id,320'class' => $o_classes.' n',321'data-n' => $o_num,322));323324$new_number = phutil_tag(325'td',326array(327'id' => $n_id,328'class' => $n_classes.' n',329'data-n' => $n_num,330));331332$html[] = phutil_tag('tr', array(), array(333$old_number,334phutil_tag(335'td',336array(337'class' => $o_classes,338'data-copy-mode' => 'copy-l',339),340$o_text),341$new_number,342$n_copy,343phutil_tag(344'td',345array(346'class' => $n_classes,347'colspan' => $n_colspan,348'data-copy-mode' => 'copy-r',349),350$n_text),351$n_cov,352));353354if ($context_not_available && ($ii == $rows - 1)) {355$html[] = $context_not_available;356}357358foreach ($scaffolds as $scaffold) {359$html[] = $scaffold;360}361}362363return $this->wrapChangeInTable(phutil_implode_html('', $html));364}365366public function renderDocumentEngineBlocks(367PhabricatorDocumentEngineBlocks $block_list,368$old_changeset_key,369$new_changeset_key) {370371$engine = $this->getDocumentEngine();372373$old_ref = null;374$new_ref = null;375$refs = $block_list->getDocumentRefs();376if ($refs) {377list($old_ref, $new_ref) = $refs;378}379380$old_comments = $this->getOldComments();381$new_comments = $this->getNewComments();382383$rows = array();384$gap = array();385$in_gap = false;386387// NOTE: The generated layout is affected by range constraints, and may388// represent only a slice of the document.389390$layout = $block_list->newTwoUpLayout();391$available_count = $block_list->getLayoutAvailableRowCount();392393foreach ($layout as $idx => $row) {394list($old, $new) = $row;395396if ($old) {397$old_key = $old->getBlockKey();398$is_visible = $old->getIsVisible();399} else {400$old_key = null;401}402403if ($new) {404$new_key = $new->getBlockKey();405$is_visible = $new->getIsVisible();406} else {407$new_key = null;408}409410if (!$is_visible) {411if (!$in_gap) {412$in_gap = true;413}414$gap[$idx] = $row;415continue;416}417418if ($in_gap) {419$in_gap = false;420$rows[] = $this->renderDocumentEngineGap(421$gap,422$available_count);423$gap = array();424}425426if ($old) {427$is_rem = ($old->getDifferenceType() === '-');428} else {429$is_rem = false;430}431432if ($new) {433$is_add = ($new->getDifferenceType() === '+');434} else {435$is_add = false;436}437438if ($is_rem && $is_add) {439$block_diff = $engine->newBlockDiffViews(440$old_ref,441$old,442$new_ref,443$new);444445$old_content = $block_diff->getOldContent();446$new_content = $block_diff->getNewContent();447448$old_classes = $block_diff->getOldClasses();449$new_classes = $block_diff->getNewClasses();450} else {451$old_classes = array();452$new_classes = array();453454if ($old) {455$old_content = $engine->newBlockContentView(456$old_ref,457$old);458459if ($is_rem) {460$old_classes[] = 'old';461$old_classes[] = 'old-full';462}463} else {464$old_content = null;465}466467if ($new) {468$new_content = $engine->newBlockContentView(469$new_ref,470$new);471472if ($is_add) {473$new_classes[] = 'new';474$new_classes[] = 'new-full';475}476} else {477$new_content = null;478}479}480481$old_classes[] = 'diff-flush';482$old_classes = implode(' ', $old_classes);483484$new_classes[] = 'diff-flush';485$new_classes = implode(' ', $new_classes);486487$old_inline_rows = array();488if ($old_key !== null) {489$old_inlines = idx($old_comments, $old_key, array());490foreach ($old_inlines as $inline) {491$inline = $this->buildInlineComment(492$inline,493$on_right = false);494$old_inline_rows[] = $this->getRowScaffoldForInline($inline);495}496}497498$new_inline_rows = array();499if ($new_key !== null) {500$new_inlines = idx($new_comments, $new_key, array());501foreach ($new_inlines as $inline) {502$inline = $this->buildInlineComment(503$inline,504$on_right = true);505$new_inline_rows[] = $this->getRowScaffoldForInline($inline);506}507}508509if ($old_content === null) {510$old_id = null;511} else {512$old_id = "C{$old_changeset_key}OL{$old_key}";513}514515$old_line_cell = phutil_tag(516'td',517array(518'id' => $old_id,519'data-n' => $old_key,520'class' => 'n',521));522523$old_content_cell = phutil_tag(524'td',525array(526'class' => $old_classes,527'data-copy-mode' => 'copy-l',528),529$old_content);530531if ($new_content === null) {532$new_id = null;533} else {534$new_id = "C{$new_changeset_key}NL{$new_key}";535}536537$new_line_cell = phutil_tag(538'td',539array(540'id' => $new_id,541'data-n' => $new_key,542'class' => 'n',543));544545$copy_gutter = phutil_tag(546'td',547array(548'class' => 'copy',549));550551$new_content_cell = phutil_tag(552'td',553array(554'class' => $new_classes,555'colspan' => '2',556'data-copy-mode' => 'copy-r',557),558$new_content);559560$row_view = phutil_tag(561'tr',562array(),563array(564$old_line_cell,565$old_content_cell,566$new_line_cell,567$copy_gutter,568$new_content_cell,569));570571$rows[] = array(572$row_view,573$old_inline_rows,574$new_inline_rows,575);576}577578if ($in_gap) {579$rows[] = $this->renderDocumentEngineGap(580$gap,581$available_count);582}583584$output = $this->wrapChangeInTable($rows);585586return $this->renderChangesetTable($output);587}588589public function getRowScaffoldForInline(PHUIDiffInlineCommentView $view) {590return id(new PHUIDiffTwoUpInlineCommentRowScaffold())591->addInlineView($view);592}593594private function getNewLineToOffsetMap() {595if ($this->newOffsetMap === null) {596$new = $this->getNewLines();597598$map = array();599foreach ($new as $offset => $new_line) {600if ($new_line === null) {601continue;602}603604if ($new_line['line'] === null) {605continue;606}607608$map[$new_line['line']] = $offset;609}610611$this->newOffsetMap = $map;612}613614return $this->newOffsetMap;615}616617protected function getTableSigils() {618return array(619'intercept-copy',620);621}622623private function renderDocumentEngineGap(array $gap, $available_count) {624$content = $this->renderShowContextLinks(625head_key($gap),626count($gap),627$available_count,628$is_blocks = true);629630return javelin_tag(631'tr',632array(633'sigil' => 'context-target',634),635phutil_tag(636'td',637array(638'colspan' => 6,639'class' => 'show-more',640),641$content));642}643644}645646647