From cb912d17352c147d11c0efe71b567f03844be578 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 15 Aug 2015 11:43:12 -0700 Subject: [PATCH] Convert DifferentialRevision view to new PHUIDiffTableOfContentsListView Summary: Ref T2183. Introduces a new View which can (in theory) unify the Revision, Diff and Commit table of contents views. This has the same behavior as before, but accepts slightly more general primitives and parameters and has somewhat cleaner code. I've made one intentinoal behavior change: removing the "Open All in Editor" button. I suspect this is essentially unused, and is a pain to keep around. We can look at restoring it if anyone notices. Test Plan: Looked at a bunch of revisions, no changes from before. Reviewers: chad Reviewed By: chad Maniphest Tasks: T2183 Differential Revision: https://secure.phabricator.com/D13908 --- src/__phutil_library_map__.php | 4 + .../DifferentialRevisionViewController.php | 43 ++- .../view/PHUIDiffTableOfContentsItemView.php | 295 ++++++++++++++++++ .../view/PHUIDiffTableOfContentsListView.php | 80 +++++ 4 files changed, 412 insertions(+), 10 deletions(-) create mode 100644 src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php create mode 100644 src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 95678635b3..e88762b70b 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1362,6 +1362,8 @@ phutil_register_library_map(array( 'PHUIDiffInlineCommentView' => 'infrastructure/diff/view/PHUIDiffInlineCommentView.php', 'PHUIDiffOneUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php', 'PHUIDiffRevealIconView' => 'infrastructure/diff/view/PHUIDiffRevealIconView.php', + 'PHUIDiffTableOfContentsItemView' => 'infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php', + 'PHUIDiffTableOfContentsListView' => 'infrastructure/diff/view/PHUIDiffTableOfContentsListView.php', 'PHUIDiffTwoUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php', 'PHUIDocumentExample' => 'applications/uiexample/examples/PHUIDocumentExample.php', 'PHUIDocumentView' => 'view/phui/PHUIDocumentView.php', @@ -5157,6 +5159,8 @@ phutil_register_library_map(array( 'PHUIDiffInlineCommentView' => 'AphrontView', 'PHUIDiffOneUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold', 'PHUIDiffRevealIconView' => 'AphrontView', + 'PHUIDiffTableOfContentsItemView' => 'AphrontView', + 'PHUIDiffTableOfContentsListView' => 'AphrontView', 'PHUIDiffTwoUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold', 'PHUIDocumentExample' => 'PhabricatorUIExample', 'PHUIDocumentView' => 'AphrontTagView', diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php index aa8daa76d6..c853cc278c 100644 --- a/src/applications/differential/controller/DifferentialRevisionViewController.php +++ b/src/applications/differential/controller/DifferentialRevisionViewController.php @@ -349,16 +349,10 @@ final class DifferentialRevisionViewController extends DifferentialController { $other_view = $this->renderOtherRevisions($other_revisions); } - $toc_view = new DifferentialDiffTableOfContentsView(); - $toc_view->setChangesets($changesets); - $toc_view->setVisibleChangesets($visible_changesets); - $toc_view->setRenderingReferences($rendering_references); - $toc_view->setCoverageMap($target->loadCoverageMap($user)); - if ($repository) { - $toc_view->setRepository($repository); - } - $toc_view->setDiff($target); - $toc_view->setUser($user); + $toc_view = $this->buildTableOfContents( + $changesets, + $visible_changesets, + $target->loadCoverageMap($user)); $comment_form = null; if (!$viewer_is_anonymous) { @@ -1042,5 +1036,34 @@ final class DifferentialRevisionViewController extends DifferentialController { return $view; } + private function buildTableOfContents( + array $changesets, + array $visible_changesets, + array $coverage) { + $viewer = $this->getViewer(); + + $toc_view = id(new PHUIDiffTableOfContentsListView()) + ->setUser($viewer); + + foreach ($changesets as $changeset_id => $changeset) { + $is_visible = isset($visible_changesets[$changeset_id]); + $anchor = $changeset->getAnchorName(); + + $filename = $changeset->getFilename(); + $coverage_id = 'differential-mcoverage-'.md5($filename); + + $item = id(new PHUIDiffTableOfContentsItemView()) + ->setChangeset($changeset) + ->setIsVisible($is_visible) + ->setAnchor($anchor) + ->setCoverage(idx($coverage, $filename)) + ->setCoverageID($coverage_id); + + $toc_view->addItem($item); + } + + return $toc_view; + } + } diff --git a/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php b/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php new file mode 100644 index 0000000000..638a9c21eb --- /dev/null +++ b/src/infrastructure/diff/view/PHUIDiffTableOfContentsItemView.php @@ -0,0 +1,295 @@ +changeset = $changeset; + return $this; + } + + public function getChangeset() { + return $this->changeset; + } + + public function setIsVisible($is_visible) { + $this->isVisible = $is_visible; + return $this; + } + + public function getIsVisible() { + return $this->isVisible; + } + + public function setAnchor($anchor) { + $this->anchor = $anchor; + return $this; + } + + public function getAnchor() { + return $this->anchor; + } + + public function setCoverage($coverage) { + $this->coverage = $coverage; + return $this; + } + + public function getCoverage() { + return $this->coverage; + } + + public function setCoverageID($coverage_id) { + $this->coverageID = $coverage_id; + return $this; + } + + public function getCoverageID() { + return $this->coverageID; + } + + public function render() { + $changeset = $this->getChangeset(); + + $cells = array(); + + $cells[] = $this->renderPathChangeCharacter(); + $cells[] = $this->renderPropertyChangeCharacter(); + $cells[] = $this->renderPropertyChangeDescription(); + + $link = $this->renderChangesetLink(); + $lines = $this->renderChangesetLines(); + $meta = $this->renderChangesetMetadata(); + + $cells[] = array( + $link, + $lines, + $meta, + ); + + $cells[] = $this->renderCoverage(); + $cells[] = $this->renderModifiedCoverage(); + + return $cells; + } + + private function renderPathChangeCharacter() { + $changeset = $this->getChangeset(); + $type = $changeset->getChangeType(); + + $color = DifferentialChangeType::getSummaryColorForChangeType($type); + $char = DifferentialChangeType::getSummaryCharacterForChangeType($type); + $title = DifferentialChangeType::getFullNameForChangeType($type); + + return javelin_tag( + 'span', + array( + 'sigil' => 'has-tip', + 'meta' => array( + 'tip' => $title, + 'align' => 'E', + ), + 'class' => 'phui-text-'.$color, + ), + $char); + } + + private function renderPropertyChangeCharacter() { + $changeset = $this->getChangeset(); + + $old = $changeset->getOldProperties(); + $new = $changeset->getNewProperties(); + + if ($old === $new) { + return null; + } + + return javelin_tag( + 'span', + array( + 'sigil' => 'has-tip', + 'meta' => array( + 'tip' => pht('Properties Modified'), + 'align' => 'E', + ), + ), + 'M'); + } + + private function renderPropertyChangeDescription() { + $changeset = $this->getChangeset(); + + $file_type = $changeset->getFileType(); + + $desc = DifferentialChangeType::getShortNameForFileType($file_type); + if ($desc === null) { + return null; + } + + return pht('(%s)', $desc); + } + + private function renderChangesetLink() { + $anchor = $this->getAnchor(); + + $changeset = $this->getChangeset(); + $name = $changeset->getDisplayFilename(); + + $change_type = $changeset->getChangeType(); + if (DifferentialChangeType::isOldLocationChangeType($change_type)) { + $away = $changeset->getAwayPaths(); + if (count($away) == 1) { + if ($change_type == DifferentialChangeType::TYPE_MOVE_AWAY) { + $right_arrow = "\xE2\x86\x92"; + $name = $this->renderRename($name, head($away), $right_arrow); + } + } + } else if ($change_type == DifferentialChangeType::TYPE_MOVE_HERE) { + $left_arrow = "\xE2\x86\x90"; + $name = $this->renderRename($name, $changeset->getOldFile(), $left_arrow); + } + + return javelin_tag( + 'a', + array( + 'href' => '#'.$anchor, + 'sigil' => 'differential-load', + 'meta' => array( + 'id' => 'diff-'.$anchor, + ), + ), + $name); + } + + private function renderChangesetLines() { + $changeset = $this->getChangeset(); + + $line_count = $changeset->getAffectedLineCount(); + if (!$line_count) { + return null; + } + + return ' '.pht('(%d line(s))', $line_count); + } + + private function renderCoverage() { + $not_applicable = '-'; + + $coverage = $this->getCoverage(); + if (!strlen($coverage)) { + return $not_applicable; + } + + $covered = substr_count($coverage, 'C'); + $not_covered = substr_count($coverage, 'U'); + + if (!$not_covered && !$covered) { + return $not_applicable; + } + + return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered))); + } + + private function renderModifiedCoverage() { + $not_applicable = '-'; + + $coverage = $this->getCoverage(); + if (!strlen($coverage)) { + return $not_applicable; + } + + if ($this->getIsVisible()) { + $label = pht('Loading...'); + } else { + $label = pht('?'); + } + + return phutil_tag( + 'div', + array( + 'id' => $this->getCoverageID(), + 'class' => 'differential-mcoverage-loading', + ), + $label); + } + + private function renderChangesetMetadata() { + $changeset = $this->getChangeset(); + $type = $changeset->getChangeType(); + + $meta = array(); + if (DifferentialChangeType::isOldLocationChangeType($type)) { + $away = $changeset->getAwayPaths(); + if (count($away) > 1) { + if ($type == DifferentialChangeType::TYPE_MULTICOPY) { + $meta[] = pht('Deleted after being copied to multiple locations:'); + } else { + $meta[] = pht('Copied to multiple locations:'); + } + foreach ($away as $path) { + $meta[] = $path; + } + } else { + if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) { + // This case is handled when we render the path. + } else { + $meta[] = pht('Copied to %s', head($away)); + } + } + } else if ($type == DifferentialChangeType::TYPE_COPY_HERE) { + $meta = pht('Copied from %s', $changeset->getOldFile()); + } + + if (!$meta) { + return null; + } + + $meta = phutil_implode_html(phutil_tag('br'), $meta); + + return phutil_tag( + 'div', + array( + 'class' => 'differential-toc-meta', + ), + $meta); + } + + private function renderRename($self, $other, $arrow) { + $old = explode('/', $self); + $new = explode('/', $other); + + $start = count($old); + foreach ($old as $index => $part) { + if (!isset($new[$index]) || $part != $new[$index]) { + $start = $index; + break; + } + } + + $end = count($old); + foreach (array_reverse($old) as $from_end => $part) { + $index = count($new) - $from_end - 1; + if (!isset($new[$index]) || $part != $new[$index]) { + $end = $from_end; + break; + } + } + + $rename = + '{'. + implode('/', array_slice($old, $start, count($old) - $end - $start)). + ' '.$arrow.' '. + implode('/', array_slice($new, $start, count($new) - $end - $start)). + '}'; + + array_splice($new, $start, count($new) - $end - $start, $rename); + + return implode('/', $new); + } + +} diff --git a/src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php b/src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php new file mode 100644 index 0000000000..6ed2e8343e --- /dev/null +++ b/src/infrastructure/diff/view/PHUIDiffTableOfContentsListView.php @@ -0,0 +1,80 @@ +items[] = $item; + return $this; + } + + public function render() { + $this->requireResource('differential-core-view-css'); + $this->requireResource('differential-table-of-contents-css'); + $this->requireResource('phui-text-css'); + + $items = $this->items; + + $rows = array(); + foreach ($items as $item) { + $rows[] = $item->render(); + } + + $reveal_link = javelin_tag( + 'a', + array( + 'sigil' => 'differential-reveal-all', + 'mustcapture' => true, + 'class' => 'button differential-toc-reveal-all', + ), + pht('Show All Context')); + + $buttons = phutil_tag( + 'div', + array( + 'class' => 'differential-toc-buttons grouped', + ), + $reveal_link); + + $table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + '', + '', + '', + pht('Path'), + pht('Coverage (All)'), + pht('Coverage (Touched)'), + )) + ->setColumnClasses( + array( + 'differential-toc-char center', + 'differential-toc-prop center', + 'differential-toc-ftype center', + 'differential-toc-file wide', + 'differential-toc-cov', + 'differential-toc-cov', + )) + ->setDeviceVisibility( + array( + true, + true, + true, + true, + false, + false, + )); + + $anchor = id(new PhabricatorAnchorView()) + ->setAnchorName('toc') + ->setNavigationMarker(true); + + return id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Table of Contents')) + ->setTable($table) + ->appendChild($anchor) + ->appendChild($buttons); + } + +}