1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-21 13:00:56 +01:00

Add color to diffusion blame and improve plain view

Summary:
query the database to get the epoch info for the commits, then
calculate the color depending on the epoch (the newer the commit, the
dark its color). Also improved the plain blame view for git, as the
git-blame doesn't produce a good display by default. Now we format the
output it from the data we fetches from the database.

Test Plan:
verify both git and svn browsing page work for 'plain',
'plainblame', 'highlighted' and 'highlightedblame' view.

Reviewed By: epriestley
Reviewers: epriestley
CC: epriestley, jungejason
Differential Revision: 93
This commit is contained in:
jungejason 2011-03-31 18:28:24 -07:00
parent 3369147cab
commit 64cd4f969d
6 changed files with 142 additions and 116 deletions

View file

@ -82,35 +82,18 @@ class DiffusionBrowseFileController extends DiffusionController {
} }
public static function renderRevision(
DiffusionRequest $drequest,
$revision) {
$callsign = $drequest->getCallsign();
$name = 'r'.$callsign.$revision;
return phutil_render_tag(
'a',
array(
'href' => '/'.$name,
),
$name
);
}
private function buildCorpus($selected) { private function buildCorpus($selected) {
$blame = ($selected == 'blame' || $selected == 'plainblame'); $needs_blame = ($selected == 'blame' || $selected == 'plainblame');
$file_query = DiffusionFileContentQuery::newFromDiffusionRequest( $file_query = DiffusionFileContentQuery::newFromDiffusionRequest(
$this->diffusionRequest); $this->diffusionRequest);
$file_query->setNeedsBlame($blame); $file_query->setNeedsBlame($needs_blame);
$file_query->loadFileContent();
// TODO: image // TODO: image
// TODO: blame of blame. // TODO: blame of blame.
switch ($selected) { switch ($selected) {
case 'plain': case 'plain':
case 'plainblame':
$style = $style =
"margin: 1em 2em; width: 90%; height: 80em; font-family: monospace"; "margin: 1em 2em; width: 90%; height: 80em; font-family: monospace";
$corpus = phutil_render_tag( $corpus = phutil_render_tag(
@ -119,6 +102,30 @@ class DiffusionBrowseFileController extends DiffusionController {
'style' => $style, 'style' => $style,
), ),
phutil_escape_html($file_query->getRawData())); phutil_escape_html($file_query->getRawData()));
break;
case 'plainblame':
$style =
"margin: 1em 2em; width: 90%; height: 80em; font-family: monospace";
list($text_list, $rev_list, $blame_dict) =
$file_query->getBlameData();
$rows = array();
foreach ($text_list as $k => $line) {
$rev = $rev_list[$k];
$author = $blame_dict[$rev]['author'];
$rows[] =
sprintf("%-10s %-15s %s", substr($rev, 0, 7), $author, $line);
}
$corpus = phutil_render_tag(
'textarea',
array(
'style' => $style,
),
phutil_escape_html(implode("\n", $rows)));
break; break;
case 'highlighted': case 'highlighted':
@ -127,17 +134,17 @@ class DiffusionBrowseFileController extends DiffusionController {
require_celerity_resource('syntax-highlighting-css'); require_celerity_resource('syntax-highlighting-css');
require_celerity_resource('diffusion-source-css'); require_celerity_resource('diffusion-source-css');
list($data, $blamedata, $revs) = $file_query->getTokenizedData(); list($text_list, $rev_list, $blame_dict) = $file_query->getBlameData();
$drequest = $this->getDiffusionRequest(); $drequest = $this->getDiffusionRequest();
$path = $drequest->getPath(); $path = $drequest->getPath();
$highlightEngine = new PhutilDefaultSyntaxHighlighterEngine(); $highlightEngine = new PhutilDefaultSyntaxHighlighterEngine();
$data = $highlightEngine->highlightSource($path, $data);
$data = explode("\n", rtrim($data)); $text_list = explode("\n", $highlightEngine->highlightSource($path,
implode("\n", $text_list)));
$rows = $this->buildDisplayRows($data, $blame, $blamedata, $drequest, $rows = $this->buildDisplayRows($text_list, $rev_list, $blame_dict,
$revs); $needs_blame, $drequest);
$corpus_table = phutil_render_tag( $corpus_table = phutil_render_tag(
'table', 'table',
@ -159,49 +166,51 @@ class DiffusionBrowseFileController extends DiffusionController {
} }
private static function buildDisplayRows($data, $blame, $blamedata, $drequest, private static function buildDisplayRows($text_list, $rev_list, $blame_dict,
$revs) { $needs_blame, DiffusionRequest $drequest) {
$last = null; $last_rev = null;
$color = null; $color = null;
$rows = array(); $rows = array();
$n = 1; $n = 1;
foreach ($data as $k => $line) {
if ($blame) { $epoch_list = ipull($blame_dict, 'epoch');
if ($last == $blamedata[$k][0]) { $max = max($epoch_list);
$blameinfo = $min = min($epoch_list);
$range = $max - $min + 1;
foreach ($text_list as $k => $line) {
if ($needs_blame) {
// If the line's rev is same as the line above, show empty content
// with same color; otherwise generate blame info. The newer a change
// is, the darker the color.
$rev = $rev_list[$k];
if ($last_rev == $rev) {
$blame_info =
'<th style="background: '.$color.'; width: 9em;"></th>'. '<th style="background: '.$color.'; width: 9em;"></th>'.
'<th style="background: '.$color.'"></th>'; '<th style="background: '.$color.'"></th>';
} else { } else {
switch ($drequest->getRepository()->getVersionControlSystem()) {
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $color_number = (int)(0xEE -
// TODO: better color for git. 0xEE * ($blame_dict[$rev]['epoch'] - $min) / $range);
$color = '#dddddd'; $color = sprintf('#%02xee%02x', $color_number, $color_number);
break;
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
$color = sprintf(
'#%02xee%02x',
$revs[$blamedata[$k][0]],
$revs[$blamedata[$k][0]]);
break;
default:
throw new Exception('repository type not supported');
}
$revision_link = self::renderRevision( $revision_link = self::renderRevision(
$drequest, $drequest,
$blamedata[$k][0]); substr($rev, 0, 7));
$author_link = $blamedata[$k][1]; $author_link = $blame_dict[$rev]['author'];
$blameinfo = $blame_info =
'<th style="background: '.$color. '<th style="background: '.$color.
'; width: 9em;">'.$revision_link.'</th>'. '; width: 9em;">'.$revision_link.'</th>'.
'<th style="background: '.$color. '<th style="background: '.$color.
'; font-weight: normal; color: #333;">'.$author_link.'</th>'; '; font-weight: normal; color: #333;">'.$author_link.'</th>';
$last = $blamedata[$k][0]; $last_rev = $rev;
} }
} else { } else {
$blameinfo = null; $blame_info = null;
} }
// Highlight the line of interest if needed.
if ($n == $drequest->getLine()) { if ($n == $drequest->getLine()) {
$tr = '<tr style="background: #ffff00;">'; $tr = '<tr style="background: #ffff00;">';
$targ = '<a id="scroll_target"></a>'; $targ = '<a id="scroll_target"></a>';
@ -212,6 +221,7 @@ class DiffusionBrowseFileController extends DiffusionController {
$targ = null; $targ = null;
} }
// Create the row display.
$uri_path = $drequest->getUriPath(); $uri_path = $drequest->getUriPath();
$uri_rev = $drequest->getCommit(); $uri_rev = $drequest->getCommit();
@ -222,10 +232,27 @@ class DiffusionBrowseFileController extends DiffusionController {
), ),
$n); $n);
$rows[] = $tr.$blameinfo.'<th>'.$l.'</th><td>'.$targ.$line.'</td></tr>'; $rows[] = $tr.$blame_info.'<th>'.$l.'</th><td>'.$targ.$line.'</td></tr>';
++$n; ++$n;
} }
return $rows; return $rows;
} }
private static function renderRevision(DiffusionRequest $drequest,
$revision) {
$callsign = $drequest->getCallsign();
$name = 'r'.$callsign.$revision;
return phutil_render_tag(
'a',
array(
'href' => '/'.$name,
),
$name
);
}
} }

View file

@ -8,13 +8,13 @@
phutil_require_module('phabricator', 'applications/diffusion/controller/base'); phutil_require_module('phabricator', 'applications/diffusion/controller/base');
phutil_require_module('phabricator', 'applications/diffusion/query/filecontent/base'); phutil_require_module('phabricator', 'applications/diffusion/query/filecontent/base');
phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
phutil_require_module('phabricator', 'infrastructure/celerity/api'); phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/api'); phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'view/layout/panel'); phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'markup'); phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'markup/syntax/engine/default'); phutil_require_module('phutil', 'markup/syntax/engine/default');
phutil_require_module('phutil', 'utils');
phutil_require_source('DiffusionBrowseFileController.php'); phutil_require_source('DiffusionBrowseFileController.php');

View file

@ -20,6 +20,7 @@ abstract class DiffusionFileContentQuery {
private $request; private $request;
private $needsBlame; private $needsBlame;
private $fileContent;
final private function __construct() { final private function __construct() {
// <private> // <private>
@ -54,20 +55,53 @@ abstract class DiffusionFileContentQuery {
} }
final public function loadFileContent() { final public function loadFileContent() {
return $this->executeQuery(); $this->fileContent = $this->executeQuery();
} }
abstract protected function executeQuery(); abstract protected function executeQuery();
final public function getRawData() { final public function getRawData() {
return $this->loadFileContent()->getCorpus(); return $this->fileContent->getCorpus();
} }
final public function getTokenizedData() { final public function getBlameData() {
return $this->tokenizeData($this->getRawData()); $raw_data = $this->getRawData();
$text_list = array();
$rev_list = array();
$blame_dict = array();
if (!$this->getNeedsBlame()) {
$text_list = explode("\n", rtrim($raw_data));
} else {
foreach (explode("\n", rtrim($raw_data)) as $k => $line) {
list($rev_id, $author, $text) = $this->tokenizeLine($line);
$text_list[$k] = $text;
$rev_list[$k] = $rev_id;
if (!isset($blame_dict[$rev_id]) &&
!isset($blame_dict[$rev_id]['author'] )) {
$blame_dict[$rev_id]['author'] = $author;
}
} }
abstract protected function tokenizeData($data); $repository = $this->getRequest()->getRepository();
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
'repositoryID = %d AND commitIdentifier IN (%Ls)', $repository->getID(),
array_unique($rev_list));
foreach ($commits as $commit) {
$commitIdentifier = $commit->getCommitIdentifier();
$epoch = $commit->getEpoch();
$blame_dict[$commitIdentifier]['epoch'] = $epoch;
}
}
return array($text_list, $rev_list, $blame_dict);
}
abstract protected function tokenizeLine($line);
public function setNeedsBlame($needs_blame) public function setNeedsBlame($needs_blame)
{ {

View file

@ -7,8 +7,10 @@
phutil_require_module('phabricator', 'applications/repository/constants/repositorytype'); phutil_require_module('phabricator', 'applications/repository/constants/repositorytype');
phutil_require_module('phabricator', 'applications/repository/storage/commit');
phutil_require_module('phutil', 'symbols'); phutil_require_module('phutil', 'symbols');
phutil_require_module('phutil', 'utils');
phutil_require_source('DiffusionFileContentQuery.php'); phutil_require_source('DiffusionFileContentQuery.php');

View file

@ -28,7 +28,7 @@ final class DiffusionGitFileContentQuery extends DiffusionFileContentQuery {
$local_path = $repository->getDetail('local-path'); $local_path = $repository->getDetail('local-path');
if ($this->getNeedsBlame()) { if ($this->getNeedsBlame()) {
list($corpus) = execx( list($corpus) = execx(
'(cd %s && git --no-pager blame -c --date short %s -- %s)', '(cd %s && git --no-pager blame -c -l --date short %s -- %s)',
$local_path, $local_path,
$commit, $commit,
$path); $path);
@ -46,35 +46,17 @@ final class DiffusionGitFileContentQuery extends DiffusionFileContentQuery {
return $file_content; return $file_content;
} }
protected function tokenizeData($data) {
protected function tokenizeLine($line) {
$m = array(); $m = array();
$blamedata = array();
$revs = array();
if ($this->getNeedsBlame()) {
$data = explode("\n", rtrim($data));
foreach ($data as $k => $line) {
// sample line: // sample line:
// d1b4fcdd ( hzhao 2009-05-01 202)function print(); // d1b4fcdd2a7c8c0f8cbdd01ca839d992135424dc
preg_match('/^\s*?(\S+?)\s*\(\s*(\S+)\s+\S+\s+\d+\)(.*)?$/', // ( hzhao 2009-05-01 202)function print();
$line, $m); preg_match('/^\s*?(\S+?)\s*\(\s*(\S+)\s+\S+\s+\d+\)(.*)?$/', $line, $m);
$data[$k] = idx($m, 3); $rev_id = $m[1];
$blamedata[$k] = array($m[1], $m[2]); $author = $m[2];
$text = idx($m, 3);
$revs[$m[1]] = true; return array($rev_id, $author, $text);
}
$data = implode("\n", $data);
krsort($revs);
$ii = 0;
$len = count($revs);
foreach ($revs as $rev => $ignored) {
$revs[$rev] = (int)(0xEE * ($ii / $len));
++$ii;
}
}
return array($data, $blamedata, $revs);
} }
} }

View file

@ -56,35 +56,16 @@ final class DiffusionSvnFileContentQuery extends DiffusionFileContentQuery {
return $file_content; return $file_content;
} }
protected function tokenizeData($data) protected function tokenizeLine($line) {
{
$m = array();
$blamedata = array();
$revs = array();
if ($this->getNeedsBlame()) {
$data = explode("\n", rtrim($data));
foreach ($data as $k => $line) {
// sample line: // sample line:
// 347498 yliu function print(); // 347498 yliu function print();
$m = array();
preg_match('/^\s*(\d+)\s+(\S+)(?: (.*))?$/', $line, $m); preg_match('/^\s*(\d+)\s+(\S+)(?: (.*))?$/', $line, $m);
$data[$k] = idx($m, 3); $rev_id = $m[1];
$blamedata[$k] = array($m[1], $m[2]); $author = $m[2];
$text = idx($m, 3);
$revs[$m[1]] = true; return array($rev_id, $author, $text);
}
$data = implode("\n", $data);
krsort($revs);
$ii = 0;
$len = count($revs);
foreach ($revs as $rev => $ignored) {
$revs[$rev] = (int)(0xEE * ($ii / $len));
++$ii;
}
}
return array($data, $blamedata, $revs);
} }
} }