2011-01-24 22:18:41 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class DifferentialDiffTableOfContentsView extends AphrontView {
|
|
|
|
|
|
|
|
private $changesets = array();
|
2012-03-15 18:45:35 +01:00
|
|
|
private $visibleChangesets = array();
|
2012-05-01 21:09:50 +02:00
|
|
|
private $references = array();
|
2012-03-01 08:16:45 +01:00
|
|
|
private $repository;
|
|
|
|
private $diff;
|
2011-04-15 23:25:23 +02:00
|
|
|
private $renderURI = '/differential/changeset/';
|
|
|
|
private $revisionID;
|
2011-04-28 06:16:35 +02:00
|
|
|
private $whitespace;
|
2012-03-13 01:06:55 +01:00
|
|
|
private $unitTestData;
|
2011-01-24 22:18:41 +01:00
|
|
|
|
|
|
|
public function setChangesets($changesets) {
|
|
|
|
$this->changesets = $changesets;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-03-15 18:45:35 +01:00
|
|
|
public function setVisibleChangesets($visible_changesets) {
|
|
|
|
$this->visibleChangesets = $visible_changesets;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-05-01 21:09:50 +02:00
|
|
|
public function setRenderingReferences(array $references) {
|
|
|
|
$this->references = $references;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-03-01 08:16:45 +01:00
|
|
|
public function setRepository(PhabricatorRepository $repository) {
|
|
|
|
$this->repository = $repository;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setDiff(DifferentialDiff $diff) {
|
|
|
|
$this->diff = $diff;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-03-13 01:06:55 +01:00
|
|
|
public function setUnitTestData($unit_test_data) {
|
|
|
|
$this->unitTestData = $unit_test_data;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-04-15 23:25:23 +02:00
|
|
|
public function setRevisionID($revision_id) {
|
|
|
|
$this->revisionID = $revision_id;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2011-04-28 06:16:35 +02:00
|
|
|
public function setWhitespace($whitespace) {
|
|
|
|
$this->whitespace = $whitespace;
|
|
|
|
return $this;
|
|
|
|
}
|
2011-04-15 23:25:23 +02:00
|
|
|
|
2011-01-24 22:18:41 +01:00
|
|
|
public function render() {
|
2011-01-27 23:55:52 +01:00
|
|
|
|
2014-01-02 20:59:35 +01:00
|
|
|
$this->requireResource('differential-core-view-css');
|
|
|
|
$this->requireResource('differential-table-of-contents-css');
|
2011-01-24 22:18:41 +01:00
|
|
|
|
|
|
|
$rows = array();
|
|
|
|
|
2012-03-13 01:06:55 +01:00
|
|
|
$coverage = array();
|
|
|
|
if ($this->unitTestData) {
|
|
|
|
$coverage_by_file = array();
|
|
|
|
foreach ($this->unitTestData as $result) {
|
|
|
|
$test_coverage = idx($result, 'coverage');
|
|
|
|
if (!$test_coverage) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
foreach ($test_coverage as $file => $results) {
|
|
|
|
$coverage_by_file[$file][] = $results;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach ($coverage_by_file as $file => $coverages) {
|
|
|
|
$coverage[$file] = ArcanistUnitTestResult::mergeCoverage($coverages);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-24 22:18:41 +01:00
|
|
|
$changesets = $this->changesets;
|
2012-03-01 08:16:45 +01:00
|
|
|
$paths = array();
|
2012-03-15 18:45:35 +01:00
|
|
|
foreach ($changesets as $id => $changeset) {
|
2011-01-24 22:18:41 +01:00
|
|
|
$type = $changeset->getChangeType();
|
|
|
|
$ftype = $changeset->getFileType();
|
2012-05-01 21:09:50 +02:00
|
|
|
$ref = idx($this->references, $id);
|
2013-04-16 20:02:04 +02:00
|
|
|
$display_file = $changeset->getDisplayFilename();
|
2012-01-16 07:43:54 +01:00
|
|
|
|
2013-04-16 20:02:04 +02:00
|
|
|
$meta = null;
|
2011-01-24 22:18:41 +01:00
|
|
|
if (DifferentialChangeType::isOldLocationChangeType($type)) {
|
|
|
|
$away = $changeset->getAwayPaths();
|
|
|
|
if (count($away) > 1) {
|
|
|
|
$meta = array();
|
|
|
|
if ($type == DifferentialChangeType::TYPE_MULTICOPY) {
|
2013-01-24 22:18:44 +01:00
|
|
|
$meta[] = pht('Deleted after being copied to multiple locations:');
|
2011-01-24 22:18:41 +01:00
|
|
|
} else {
|
2013-01-24 22:18:44 +01:00
|
|
|
$meta[] = pht('Copied to multiple locations:');
|
2011-01-24 22:18:41 +01:00
|
|
|
}
|
|
|
|
foreach ($away as $path) {
|
2013-02-13 23:50:15 +01:00
|
|
|
$meta[] = $path;
|
2011-01-24 22:18:41 +01:00
|
|
|
}
|
2013-02-13 23:50:15 +01:00
|
|
|
$meta = phutil_implode_html(phutil_tag('br'), $meta);
|
2011-01-24 22:18:41 +01:00
|
|
|
} else {
|
|
|
|
if ($type == DifferentialChangeType::TYPE_MOVE_AWAY) {
|
2013-04-16 20:02:04 +02:00
|
|
|
$display_file = $this->renderRename(
|
|
|
|
$display_file,
|
|
|
|
reset($away),
|
|
|
|
"\xE2\x86\x92");
|
2011-01-24 22:18:41 +01:00
|
|
|
} else {
|
2013-02-13 23:50:15 +01:00
|
|
|
$meta = pht('Copied to %s', reset($away));
|
2011-01-24 22:18:41 +01:00
|
|
|
}
|
|
|
|
}
|
2012-01-16 07:43:54 +01:00
|
|
|
} else if ($type == DifferentialChangeType::TYPE_MOVE_HERE) {
|
2013-04-16 20:02:04 +02:00
|
|
|
$old_file = $changeset->getOldFile();
|
|
|
|
$display_file = $this->renderRename(
|
|
|
|
$display_file,
|
|
|
|
$old_file,
|
|
|
|
"\xE2\x86\x90");
|
2012-01-16 07:43:54 +01:00
|
|
|
} else if ($type == DifferentialChangeType::TYPE_COPY_HERE) {
|
2013-02-13 23:50:15 +01:00
|
|
|
$meta = pht('Copied from %s', $changeset->getOldFile());
|
2011-01-24 22:18:41 +01:00
|
|
|
}
|
|
|
|
|
2013-04-16 20:02:04 +02:00
|
|
|
$link = $this->renderChangesetLink($changeset, $ref, $display_file);
|
|
|
|
|
2011-01-24 22:18:41 +01:00
|
|
|
$line_count = $changeset->getAffectedLineCount();
|
|
|
|
if ($line_count == 0) {
|
2014-04-03 06:49:28 +02:00
|
|
|
$lines = '';
|
2011-01-24 22:18:41 +01:00
|
|
|
} else {
|
2012-06-14 22:43:09 +02:00
|
|
|
$lines = ' '.pht('(%d line(s))', $line_count);
|
2011-01-24 22:18:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$char = DifferentialChangeType::getSummaryCharacterForChangeType($type);
|
|
|
|
$chartitle = DifferentialChangeType::getFullNameForChangeType($type);
|
|
|
|
$desc = DifferentialChangeType::getShortNameForFileType($ftype);
|
|
|
|
if ($desc) {
|
|
|
|
$desc = '('.$desc.')';
|
|
|
|
}
|
|
|
|
$pchar =
|
|
|
|
($changeset->getOldProperties() === $changeset->getNewProperties())
|
2014-04-03 06:49:28 +02:00
|
|
|
? ''
|
2014-09-08 15:08:56 +02:00
|
|
|
: phutil_tag(
|
|
|
|
'span',
|
|
|
|
array('title' => pht('Properties Changed')),
|
|
|
|
'M');
|
2011-01-24 22:18:41 +01:00
|
|
|
|
2012-03-13 01:06:55 +01:00
|
|
|
$fname = $changeset->getFilename();
|
|
|
|
$cov = $this->renderCoverage($coverage, $fname);
|
|
|
|
if ($cov === null) {
|
2013-02-13 23:50:15 +01:00
|
|
|
$mcov = $cov = phutil_tag('em', array(), '-');
|
2012-03-13 01:06:55 +01:00
|
|
|
} else {
|
2013-01-18 09:32:58 +01:00
|
|
|
$mcov = phutil_tag(
|
2012-03-13 01:06:55 +01:00
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'id' => 'differential-mcoverage-'.md5($fname),
|
|
|
|
'class' => 'differential-mcoverage-loading',
|
|
|
|
),
|
2014-04-03 06:49:28 +02:00
|
|
|
(isset($this->visibleChangesets[$id]) ?
|
|
|
|
pht('Loading...') : pht('?')));
|
2012-03-13 01:06:55 +01:00
|
|
|
}
|
|
|
|
|
2011-01-24 22:18:41 +01:00
|
|
|
if ($meta) {
|
2014-04-03 06:49:28 +02:00
|
|
|
$meta = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'differential-toc-meta'
|
|
|
|
),
|
|
|
|
$meta);
|
2011-01-24 22:18:41 +01:00
|
|
|
}
|
2014-04-03 06:49:28 +02:00
|
|
|
|
2012-03-01 08:16:45 +01:00
|
|
|
if ($this->diff && $this->repository) {
|
|
|
|
$paths[] =
|
2012-04-10 08:42:12 +02:00
|
|
|
$changeset->getAbsoluteRepositoryPath($this->repository, $this->diff);
|
2012-03-01 08:16:45 +01:00
|
|
|
}
|
2014-04-03 06:49:28 +02:00
|
|
|
|
|
|
|
$rows[] = array(
|
|
|
|
$char,
|
|
|
|
$pchar,
|
|
|
|
$desc,
|
|
|
|
array($link, $lines, $meta),
|
|
|
|
$cov,
|
|
|
|
$mcov
|
|
|
|
);
|
2012-03-01 08:16:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$editor_link = null;
|
|
|
|
if ($paths && $this->user) {
|
|
|
|
$editor_link = $this->user->loadEditorLink(
|
2012-12-04 00:51:18 +01:00
|
|
|
$paths,
|
2012-03-01 08:16:45 +01:00
|
|
|
1, // line number
|
2012-04-05 02:53:16 +02:00
|
|
|
$this->repository->getCallsign());
|
2012-03-01 08:16:45 +01:00
|
|
|
if ($editor_link) {
|
2012-12-13 06:00:35 +01:00
|
|
|
$editor_link =
|
2013-01-18 03:57:09 +01:00
|
|
|
phutil_tag(
|
2012-12-13 06:00:35 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => $editor_link,
|
|
|
|
'class' => 'button differential-toc-edit-all',
|
|
|
|
),
|
2013-01-24 22:18:44 +01:00
|
|
|
pht('Open All in Editor'));
|
2012-03-01 08:16:45 +01:00
|
|
|
}
|
2011-01-24 22:18:41 +01:00
|
|
|
}
|
|
|
|
|
2013-01-25 21:57:17 +01:00
|
|
|
$reveal_link = javelin_tag(
|
2012-12-13 06:00:35 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'sigil' => 'differential-reveal-all',
|
|
|
|
'mustcapture' => true,
|
|
|
|
'class' => 'button differential-toc-reveal-all',
|
|
|
|
),
|
2013-01-25 21:57:17 +01:00
|
|
|
pht('Show All Context'));
|
2012-12-13 06:00:35 +01:00
|
|
|
|
2014-04-03 06:49:28 +02:00
|
|
|
$buttons = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'differential-toc-buttons grouped'
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
$editor_link,
|
|
|
|
$reveal_link
|
|
|
|
));
|
|
|
|
|
|
|
|
$table = id(new AphrontTableView($rows));
|
|
|
|
$table->setHeaders(
|
|
|
|
array(
|
|
|
|
'',
|
|
|
|
'',
|
|
|
|
'',
|
|
|
|
pht('Path'),
|
|
|
|
pht('Coverage (All)'),
|
|
|
|
pht('Coverage (Touched)'),
|
|
|
|
));
|
|
|
|
$table->setColumnClasses(
|
|
|
|
array(
|
|
|
|
'differential-toc-char center',
|
|
|
|
'differential-toc-prop center',
|
|
|
|
'differential-toc-ftype center',
|
|
|
|
'differential-toc-file wide',
|
|
|
|
'differential-toc-cov',
|
|
|
|
'differential-toc-cov',
|
|
|
|
));
|
|
|
|
$table->setDeviceVisibility(
|
|
|
|
array(
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
true,
|
|
|
|
false,
|
|
|
|
false,
|
|
|
|
));
|
|
|
|
$anchor = id(new PhabricatorAnchorView())
|
2013-02-13 23:50:15 +01:00
|
|
|
->setAnchorName('toc')
|
2014-04-03 06:49:28 +02:00
|
|
|
->setNavigationMarker(true);
|
2013-09-29 00:55:38 +02:00
|
|
|
|
|
|
|
return id(new PHUIObjectBoxView())
|
|
|
|
->setHeaderText(pht('Table of Contents'))
|
2014-04-03 06:49:28 +02:00
|
|
|
->appendChild($anchor)
|
|
|
|
->appendChild($table)
|
|
|
|
->appendChild($buttons);
|
2011-01-24 22:18:41 +01:00
|
|
|
}
|
2012-01-24 09:37:50 +01:00
|
|
|
|
2013-04-16 20:02:04 +02:00
|
|
|
private function renderRename($display_file, $other_file, $arrow) {
|
|
|
|
$old = explode('/', $display_file);
|
|
|
|
$new = explode('/', $other_file);
|
|
|
|
|
|
|
|
$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);
|
|
|
|
}
|
|
|
|
|
2012-03-13 01:06:55 +01:00
|
|
|
private function renderCoverage(array $coverage, $file) {
|
|
|
|
$info = idx($coverage, $file);
|
|
|
|
if (!$info) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$not_covered = substr_count($info, 'U');
|
|
|
|
$covered = substr_count($info, 'C');
|
|
|
|
|
|
|
|
if (!$not_covered && !$covered) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return sprintf('%d%%', 100 * ($covered / ($covered + $not_covered)));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-16 20:02:04 +02:00
|
|
|
private function renderChangesetLink(
|
|
|
|
DifferentialChangeset $changeset,
|
|
|
|
$ref,
|
|
|
|
$display_file) {
|
2012-01-24 09:37:50 +01:00
|
|
|
|
2013-01-25 21:57:17 +01:00
|
|
|
return javelin_tag(
|
2012-01-24 09:37:50 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '#'.$changeset->getAnchorName(),
|
Consolidate changeset rendering logic
Summary:
Ref T5179. Currently, all the changeset rendering logic is in the "populate" behavior, and a lot of it comes in via configuration and is hard to get at.
Instead, surface an object which can control it, and which other behaviors can access more easily.
In particular, this allows us to add a "Load/Reload" item to the view options menu, which would previously have been very challenging.
Load/Reload isn't useful on its own, but is a step away from "Show whitespace as...", "Highlight as...", "Show tabtops as...", "View Unified", "View Side-By-Side", etc.
Test Plan:
- Viewed Differential.
- Viewed Diffusion.
- Viewed large changesets, clicked "Load".
- Used "Load" and "Reload" from view options menu.
- Loaded all changes in a large diff, verified "Load" and TOC clicks take precedence over other content loads.
- Played with content stability stuff.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T5179
Differential Revision: https://secure.phabricator.com/D9286
2014-05-25 16:13:22 +02:00
|
|
|
'sigil' => 'differential-load',
|
2012-05-01 21:09:50 +02:00
|
|
|
'meta' => array(
|
|
|
|
'id' => 'diff-'.$changeset->getAnchorName(),
|
|
|
|
),
|
2012-01-24 09:37:50 +01:00
|
|
|
),
|
2013-01-25 21:57:17 +01:00
|
|
|
$display_file);
|
2012-01-24 09:37:50 +01:00
|
|
|
}
|
2012-03-30 19:13:08 +02:00
|
|
|
|
2011-01-24 22:18:41 +01:00
|
|
|
}
|