Path: blob/master/src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php
12242 views
<?php12final class PHUIDiffTableOfContentsListView extends AphrontView {34private $items = array();5private $authorityPackages;6private $header;7private $infoView;8private $background;9private $bare;1011private $components = array();1213public function addItem(PHUIDiffTableOfContentsItemView $item) {14$this->items[] = $item;15return $this;16}1718public function setAuthorityPackages(array $authority_packages) {19assert_instances_of($authority_packages, 'PhabricatorOwnersPackage');20$this->authorityPackages = $authority_packages;21return $this;22}2324public function getAuthorityPackages() {25return $this->authorityPackages;26}2728public function setBackground($background) {29$this->background = $background;30return $this;31}3233public function setHeader(PHUIHeaderView $header) {34$this->header = $header;35return $this;36}3738public function setInfoView(PHUIInfoView $infoview) {39$this->infoView = $infoview;40return $this;41}4243public function setBare($bare) {44$this->bare = $bare;45return $this;46}4748public function getBare() {49return $this->bare;50}5152public function render() {53$this->requireResource('differential-core-view-css');54$this->requireResource('differential-table-of-contents-css');5556Javelin::initBehavior('phabricator-tooltips');5758if ($this->getAuthorityPackages()) {59$authority = mpull($this->getAuthorityPackages(), null, 'getPHID');60} else {61$authority = array();62}6364$items = $this->items;65$viewer = $this->getViewer();6667$item_map = array();6869$vector_tree = new ArcanistDiffVectorTree();70foreach ($items as $item) {71$item->setViewer($viewer);7273$changeset = $item->getChangeset();7475$old_vector = $changeset->getOldStatePathVector();76$new_vector = $changeset->getNewStatePathVector();7778$tree_vector = $this->newTreeVector($old_vector, $new_vector);7980$item_map[implode("\n", $tree_vector)] = $item;8182$vector_tree->addVector($tree_vector);83}84$node_list = $vector_tree->newDisplayList();8586$node_map = array();87foreach ($node_list as $node) {88$path_vector = $node->getVector();89$path_vector = implode("\n", $path_vector);90$node_map[$path_vector] = $node;91}9293// Mark all nodes which contain at least one path which exists in the new94// state. Nodes we don't mark contain only deleted or moved files, so they95// can be rendered with a less-prominent style.9697foreach ($node_map as $node_key => $node) {98$item = idx($item_map, $node_key);99100if (!$item) {101continue;102}103104$changeset = $item->getChangeset();105if (!$changeset->getIsLowImportanceChangeset()) {106$node->setAncestralAttribute('important', true);107}108}109110$any_packages = false;111$any_coverage = false;112$any_context = false;113114$rows = array();115$rowc = array();116foreach ($node_map as $node_key => $node) {117$display_vector = $node->getDisplayVector();118$item = idx($item_map, $node_key);119120if ($item) {121$changeset = $item->getChangeset();122$icon = $changeset->newFileTreeIcon();123} else {124$changeset = null;125$icon = id(new PHUIIconView())126->setIcon('fa-folder-open-o grey');127}128129if ($node->getChildren()) {130$old_dir = true;131$new_dir = true;132} else {133// TODO: When properties are set on a directory in SVN directly, this134// might be incorrect.135$old_dir = false;136$new_dir = false;137}138139$display_view = $this->newComponentView(140$icon,141$display_vector,142$old_dir,143$new_dir,144$item);145146$depth = $node->getDisplayDepth();147148$style = sprintf('padding-left: %dpx;', $depth * 16);149150if ($item) {151$packages = $item->renderPackages();152} else {153$packages = null;154}155156if ($packages) {157$any_packages = true;158}159160if ($item) {161if ($item->getCoverage()) {162$any_coverage = true;163}164$coverage = $item->renderCoverage();165$modified_coverage = $item->renderModifiedCoverage();166} else {167$coverage = null;168$modified_coverage = null;169}170171if ($item) {172$context = $item->getContext();173if ($context) {174$any_context = true;175}176} else {177$context = null;178}179180if ($item) {181$lines = $item->renderChangesetLines();182} else {183$lines = null;184}185186$rows[] = array(187$context,188phutil_tag(189'div',190array(191'style' => $style,192),193$display_view),194$lines,195$coverage,196$modified_coverage,197$packages,198);199200$classes = array();201202$have_authority = false;203204if ($item) {205$packages = $item->getPackages();206if ($packages) {207if (array_intersect_key($packages, $authority)) {208$have_authority = true;209}210}211}212213if ($have_authority) {214$classes[] = 'highlighted';215}216217if (!$node->getAttribute('important')) {218$classes[] = 'diff-toc-low-importance-row';219}220221if ($changeset) {222$classes[] = 'diff-toc-changeset-row';223} else {224$classes[] = 'diff-toc-no-changeset-row';225}226227$rowc[] = implode(' ', $classes);228}229230$table = id(new AphrontTableView($rows))231->setRowClasses($rowc)232->setClassName('aphront-table-view-compact')233->setHeaders(234array(235null,236pht('Path'),237pht('Size'),238pht('Coverage (All)'),239pht('Coverage (Touched)'),240pht('Packages'),241))242->setColumnClasses(243array(244null,245'diff-toc-path wide',246'right',247'differential-toc-cov',248'differential-toc-cov',249null,250))251->setColumnVisibility(252array(253$any_context,254true,255true,256$any_coverage,257$any_coverage,258$any_packages,259))260->setDeviceVisibility(261array(262true,263true,264false,265false,266false,267true,268));269270$anchor = id(new PhabricatorAnchorView())271->setAnchorName('toc')272->setNavigationMarker(true);273274if ($this->bare) {275return $table;276}277278$header = id(new PHUIHeaderView())279->setHeader(pht('Table of Contents'));280281if ($this->header) {282$header = $this->header;283}284285$box = id(new PHUIObjectBoxView())286->setHeader($header)287->setBackground($this->background)288->setTable($table)289->appendChild($anchor);290291if ($this->infoView) {292$box->setInfoView($this->infoView);293}294295return $box;296}297298private function newTreeVector($old, $new) {299if ($old === null && $new === null) {300throw new Exception(pht('Changeset has no path vectors!'));301}302303$vector = null;304if ($old === null) {305$vector = $new;306} else if ($new === null) {307$vector = $old;308} else if ($old === $new) {309$vector = $new;310}311312if ($vector) {313foreach ($vector as $k => $v) {314$vector[$k] = $this->newScalarComponent($v);315}316return $vector;317}318319$matrix = id(new PhutilEditDistanceMatrix())320->setSequences($old, $new)321->setComputeString(true);322$edits = $matrix->getEditString();323324// If the edit sequence contains deletions followed by edits, move325// the deletions to the end to left-align the new path.326$edits = preg_replace('/(d+)(x+)/', '\2\1', $edits);327328$vector = array();329$length = strlen($edits);330331$old_cursor = 0;332$new_cursor = 0;333334for ($ii = 0; $ii < strlen($edits); $ii++) {335$c = $edits[$ii];336switch ($c) {337case 'i':338$vector[] = $this->newPairComponent(null, $new[$new_cursor]);339$new_cursor++;340break;341case 'd':342$vector[] = $this->newPairComponent($old[$old_cursor], null);343$old_cursor++;344break;345case 's':346case 'x':347case 't':348$vector[] = $this->newPairComponent(349$old[$old_cursor],350$new[$new_cursor]);351$old_cursor++;352$new_cursor++;353break;354default:355throw new Exception(pht('Unknown edit string "%s"!', $c));356}357}358359return $vector;360}361362private function newScalarComponent($v) {363$key = sprintf('path(%s)', $v);364365if (!isset($this->components[$key])) {366$this->components[$key] = $v;367}368369return $key;370}371372private function newPairComponent($u, $v) {373if ($u === $v) {374return $this->newScalarComponent($u);375}376377$key = sprintf('pair(%s > %s)', $u, $v);378379if (!isset($this->components[$key])) {380$this->components[$key] = array($u, $v);381}382383return $key;384}385386private function newComponentView(387$icon,388array $keys,389$old_dir,390$new_dir,391$item) {392393$is_simple = true;394395$items = array();396foreach ($keys as $key) {397$component = $this->components[$key];398399if (is_array($component)) {400$is_simple = false;401} else {402$component = array(403$component,404$component,405);406}407408$items[] = $component;409}410411$move_icon = id(new PHUIIconView())412->setIcon('fa-angle-double-right pink');413414$old_row = array(415phutil_tag('td', array(), $move_icon),416);417$new_row = array(418phutil_tag('td', array(), $icon),419);420421$last_old_key = null;422$last_new_key = null;423424foreach ($items as $key => $component) {425if (!is_array($component)) {426$last_old_key = $key;427$last_new_key = $key;428} else {429if ($component[0] !== null) {430$last_old_key = $key;431}432if ($component[1] !== null) {433$last_new_key = $key;434}435}436}437438foreach ($items as $key => $component) {439if (!is_array($component)) {440$old = $component;441$new = $component;442} else {443$old = $component[0];444$new = $component[1];445}446447$old_classes = array();448$new_classes = array();449450if ($old === $new) {451// Do nothing.452} else if ($old === null) {453$new_classes[] = 'diff-path-component-new';454} else if ($new === null) {455$old_classes[] = 'diff-path-component-old';456} else {457$old_classes[] = 'diff-path-component-old';458$new_classes[] = 'diff-path-component-new';459}460461if ($old !== null) {462if (($key === $last_old_key) && !$old_dir) {463// Do nothing.464} else {465$old = $old.'/';466}467}468469if ($new !== null) {470if (($key === $last_new_key) && $item) {471$new = $item->newLink();472} else if (($key === $last_new_key) && !$new_dir) {473// Do nothing.474} else {475$new = $new.'/';476}477}478479$old_row[] = phutil_tag(480'td',481array(),482phutil_tag(483'div',484array(485'class' => implode(' ', $old_classes),486),487$old));488$new_row[] = phutil_tag(489'td',490array(),491phutil_tag(492'div',493array(494'class' => implode(' ', $new_classes),495),496$new));497}498499$old_row = phutil_tag(500'tr',501array(502'class' => 'diff-path-old',503),504$old_row);505506$new_row = phutil_tag(507'tr',508array(509'class' => 'diff-path-new',510),511$new_row);512513$rows = array();514$rows[] = $new_row;515if (!$is_simple) {516$rows[] = $old_row;517}518519$body = phutil_tag('tbody', array(), $rows);520521$table = phutil_tag(522'table',523array(524),525$body);526527return $table;528}529530}531532533