mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-27 01:02:42 +01:00
Merge pull request #180 from r4nt/differential-unified-comments
Adds an option to allow sending unified diff contexts in differential mails
This commit is contained in:
commit
0f076779b2
5 changed files with 309 additions and 2 deletions
|
@ -382,6 +382,13 @@ return array(
|
||||||
// patches are sent in. Valid options are 'unified' (like diff -u) or 'git'.
|
// patches are sent in. Valid options are 'unified' (like diff -u) or 'git'.
|
||||||
'metamta.differential.patch-format' => 'unified',
|
'metamta.differential.patch-format' => 'unified',
|
||||||
|
|
||||||
|
// Enables a different format for comments in differential emails.
|
||||||
|
// Differential will create unified diffs around the comment, which
|
||||||
|
// will give enough context for people who are only viewing the
|
||||||
|
// reviews in email to understand what is going on. The context will
|
||||||
|
// be created based on the range of the comment.
|
||||||
|
'metamta.differential.unified-comment-context' => true,
|
||||||
|
|
||||||
// Prefix prepended to mail sent by Diffusion.
|
// Prefix prepended to mail sent by Diffusion.
|
||||||
'metamta.diffusion.subject-prefix' => '[Diffusion]',
|
'metamta.diffusion.subject-prefix' => '[Diffusion]',
|
||||||
|
|
||||||
|
|
|
@ -216,6 +216,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialBranchFieldSpecification' => 'applications/differential/field/specification/DifferentialBranchFieldSpecification.php',
|
'DifferentialBranchFieldSpecification' => 'applications/differential/field/specification/DifferentialBranchFieldSpecification.php',
|
||||||
'DifferentialCCWelcomeMail' => 'applications/differential/mail/DifferentialCCWelcomeMail.php',
|
'DifferentialCCWelcomeMail' => 'applications/differential/mail/DifferentialCCWelcomeMail.php',
|
||||||
'DifferentialCCsFieldSpecification' => 'applications/differential/field/specification/DifferentialCCsFieldSpecification.php',
|
'DifferentialCCsFieldSpecification' => 'applications/differential/field/specification/DifferentialCCsFieldSpecification.php',
|
||||||
|
'DifferentialChangeSetTestCase' => 'applications/differential/storage/__tests__/DifferentialChangesetTestCase.php',
|
||||||
'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php',
|
'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php',
|
||||||
'DifferentialChangeset' => 'applications/differential/storage/DifferentialChangeset.php',
|
'DifferentialChangeset' => 'applications/differential/storage/DifferentialChangeset.php',
|
||||||
'DifferentialChangesetDetailView' => 'applications/differential/view/DifferentialChangesetDetailView.php',
|
'DifferentialChangesetDetailView' => 'applications/differential/view/DifferentialChangesetDetailView.php',
|
||||||
|
@ -1385,6 +1386,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialBranchFieldSpecification' => 'DifferentialFieldSpecification',
|
'DifferentialBranchFieldSpecification' => 'DifferentialFieldSpecification',
|
||||||
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
|
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
|
||||||
'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification',
|
'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification',
|
||||||
|
'DifferentialChangeSetTestCase' => 'PhabricatorTestCase',
|
||||||
'DifferentialChangeset' => 'DifferentialDAO',
|
'DifferentialChangeset' => 'DifferentialDAO',
|
||||||
'DifferentialChangesetDetailView' => 'AphrontView',
|
'DifferentialChangesetDetailView' => 'AphrontView',
|
||||||
'DifferentialChangesetListView' => 'AphrontView',
|
'DifferentialChangesetListView' => 'AphrontView',
|
||||||
|
|
|
@ -152,6 +152,13 @@ final class DifferentialCommentMail extends DifferentialMail {
|
||||||
if ($inlines) {
|
if ($inlines) {
|
||||||
$body[] = 'INLINE COMMENTS';
|
$body[] = 'INLINE COMMENTS';
|
||||||
$changesets = $this->getChangesets();
|
$changesets = $this->getChangesets();
|
||||||
|
|
||||||
|
if (PhabricatorEnv::getEnvConfig(
|
||||||
|
'metamta.differential.unified-comment-context', false)) {
|
||||||
|
foreach ($changesets as $changeset) {
|
||||||
|
$changeset->attachHunks($changeset->loadHunks());
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach ($inlines as $inline) {
|
foreach ($inlines as $inline) {
|
||||||
$changeset = $changesets[$inline->getChangesetID()];
|
$changeset = $changesets[$inline->getChangesetID()];
|
||||||
if (!$changeset) {
|
if (!$changeset) {
|
||||||
|
@ -165,8 +172,20 @@ final class DifferentialCommentMail extends DifferentialMail {
|
||||||
} else {
|
} else {
|
||||||
$range = $start;
|
$range = $start;
|
||||||
}
|
}
|
||||||
$content = $inline->getContent();
|
|
||||||
$body[] = $this->formatText("{$file}:{$range} {$content}");
|
if (!PhabricatorEnv::getEnvConfig(
|
||||||
|
'metamta.differential.unified-comment-context', false)) {
|
||||||
|
$body[] = $this->formatText("{$file}:{$range} {$content}");
|
||||||
|
} else {
|
||||||
|
$body[] = "================";
|
||||||
|
$body[] = "Comment at: " . $file . ":" . $range;
|
||||||
|
$body[] = $changeset->makeContextDiff($inline, 1);
|
||||||
|
$body[] = "----------------";
|
||||||
|
|
||||||
|
$content = $inline->getContent();
|
||||||
|
$body[] = $content;
|
||||||
|
$body[] = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$body[] = null;
|
$body[] = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,4 +215,90 @@ final class DifferentialChangeset extends DifferentialDAO {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function makeContextDiff($inline, $add_context) {
|
||||||
|
$context = array();
|
||||||
|
$debug = false;
|
||||||
|
if ($debug) {
|
||||||
|
$context[] = 'Inline: '.$inline->getIsNewFile().' '.
|
||||||
|
$inline->getLineNumber().' '.$inline->getLineLength();
|
||||||
|
foreach ($this->getHunks() as $hunk) {
|
||||||
|
$context[] = 'hunk: '.$hunk->getOldOffset().'-'.
|
||||||
|
$hunk->getOldLen().'; '.$hunk->getNewOffset().'-'.$hunk->getNewLen();
|
||||||
|
$context[] = $hunk->getChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inline->getIsNewFile()) {
|
||||||
|
$prefix = '+';
|
||||||
|
} else {
|
||||||
|
$prefix = '-';
|
||||||
|
}
|
||||||
|
foreach ($this->getHunks() as $hunk) {
|
||||||
|
if ($inline->getIsNewFile()) {
|
||||||
|
$offset = $hunk->getNewOffset();
|
||||||
|
$length = $hunk->getNewLen();
|
||||||
|
} else {
|
||||||
|
$offset = $hunk->getOldOffset();
|
||||||
|
$length = $hunk->getOldLen();
|
||||||
|
}
|
||||||
|
$start = $inline->getLineNumber() - $offset;
|
||||||
|
$end = $start + $inline->getLineLength();
|
||||||
|
// We need to go in if $start == $length, because the last line
|
||||||
|
// might be a "\No newline at end of file" marker, which we want
|
||||||
|
// to show if the additional context is > 0.
|
||||||
|
if ($start <= $length && $end >= 0) {
|
||||||
|
$start = $start - $add_context;
|
||||||
|
$end = $end + $add_context;
|
||||||
|
$hunk_content = array();
|
||||||
|
$hunk_pos = array( "-" => 0, "+" => 0 );
|
||||||
|
$hunk_offset = array( "-" => NULL, "+" => NULL );
|
||||||
|
$hunk_last = array( "-" => NULL, "+" => NULL );
|
||||||
|
foreach (explode("\n", $hunk->getChanges()) as $line) {
|
||||||
|
$in_common = strncmp($line, " ", 1) === 0;
|
||||||
|
$in_old = strncmp($line, "-", 1) === 0 || $in_common;
|
||||||
|
$in_new = strncmp($line, "+", 1) === 0 || $in_common;
|
||||||
|
$in_selected = strncmp($line, $prefix, 1) === 0;
|
||||||
|
$skip = !$in_selected && !$in_common;
|
||||||
|
if ($hunk_pos[$prefix] <= $end) {
|
||||||
|
if ($start <= $hunk_pos[$prefix]) {
|
||||||
|
if (!$skip || ($hunk_pos[$prefix] != $start &&
|
||||||
|
$hunk_pos[$prefix] != $end)) {
|
||||||
|
if ($in_old) {
|
||||||
|
if ($hunk_offset["-"] === NULL) {
|
||||||
|
$hunk_offset["-"] = $hunk_pos["-"];
|
||||||
|
}
|
||||||
|
$hunk_last["-"] = $hunk_pos["-"];
|
||||||
|
}
|
||||||
|
if ($in_new) {
|
||||||
|
if ($hunk_offset["+"] === NULL) {
|
||||||
|
$hunk_offset["+"] = $hunk_pos["+"];
|
||||||
|
}
|
||||||
|
$hunk_last["+"] = $hunk_pos["+"];
|
||||||
|
}
|
||||||
|
|
||||||
|
$hunk_content[] = $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($in_old) { ++$hunk_pos["-"]; }
|
||||||
|
if ($in_new) { ++$hunk_pos["+"]; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($hunk_offset["-"] !== NULL || $hunk_offset["+"] !== NULL) {
|
||||||
|
$header = "@@";
|
||||||
|
if ($hunk_offset["-"] !== NULL) {
|
||||||
|
$header .= " -" . ($hunk->getOldOffset() + $hunk_offset["-"]) .
|
||||||
|
"," . ($hunk_last["-"]-$hunk_offset["-"]+1);
|
||||||
|
}
|
||||||
|
if ($hunk_offset["+"] !== NULL) {
|
||||||
|
$header .= " +" . ($hunk->getNewOffset() + $hunk_offset["+"]) .
|
||||||
|
"," . ($hunk_last["+"]-$hunk_offset["+"]+1);
|
||||||
|
}
|
||||||
|
$header .= " @@";
|
||||||
|
$context[] = $header;
|
||||||
|
$context[] = implode("\n", $hunk_content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implode("\n", $context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,193 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DifferentialChangeSetTestCase extends PhabricatorTestCase {
|
||||||
|
private function createComment() {
|
||||||
|
$comment = new DifferentialInlineComment();
|
||||||
|
return $comment;
|
||||||
|
}
|
||||||
|
// $line: 1 based
|
||||||
|
// $length: 0 based (0 meaning 1 line)
|
||||||
|
private function createNewComment($line, $length) {
|
||||||
|
$comment = $this->createComment();
|
||||||
|
$comment->setIsNewFile(True);
|
||||||
|
$comment->setLineNumber($line);
|
||||||
|
$comment->setLineLength($length);
|
||||||
|
return $comment;
|
||||||
|
}
|
||||||
|
// $line: 1 based
|
||||||
|
// $length: 0 based (0 meaning 1 line)
|
||||||
|
private function createOldComment($line, $length) {
|
||||||
|
$comment = $this->createComment();
|
||||||
|
$comment->setIsNewFile(False);
|
||||||
|
$comment->setLineNumber($line);
|
||||||
|
$comment->setLineLength($length);
|
||||||
|
return $comment;
|
||||||
|
}
|
||||||
|
private function createHunk($oldOffset, $oldLen, $newOffset, $newLen, $changes) {
|
||||||
|
$hunk = new DifferentialHunk();
|
||||||
|
$hunk->setOldOffset($oldOffset);
|
||||||
|
$hunk->setOldLen($oldLen);
|
||||||
|
$hunk->setNewOffset($newOffset);
|
||||||
|
$hunk->setNewLen($newLen);
|
||||||
|
$hunk->setChanges($changes);
|
||||||
|
return $hunk;
|
||||||
|
}
|
||||||
|
private function createChange($hunks) {
|
||||||
|
$change = new DifferentialChangeset();
|
||||||
|
$change->attachHunks($hunks);
|
||||||
|
return $change;
|
||||||
|
}
|
||||||
|
// Returns a change that consists of a single hunk, starting at line 1.
|
||||||
|
private function createSingleChange($old_lines, $new_lines, $changes) {
|
||||||
|
return $this->createChange(array(
|
||||||
|
0 => $this->createHunk(1, $old_lines, 1, $new_lines, $changes),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOneLineOldComment() {
|
||||||
|
$change = $this->createSingleChange(1, 0, "-a");
|
||||||
|
$context = $change->makeContextDiff($this->createOldComment(1, 0), 0);
|
||||||
|
$this->assertEqual("@@ -1,1 @@\n-a", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOneLineNewComment() {
|
||||||
|
$change = $this->createSingleChange(0, 1, "+a");
|
||||||
|
$context = $change->makeContextDiff($this->createNewComment(1, 0), 0);
|
||||||
|
$this->assertEqual("@@ +1,1 @@\n+a", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCannotFindContext() {
|
||||||
|
$change = $this->createSingleChange(0, 1, "+a");
|
||||||
|
$context = $change->makeContextDiff($this->createNewComment(2, 0), 0);
|
||||||
|
$this->assertEqual("", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOverlapFromStartOfHunk() {
|
||||||
|
$change = $this->createChange(array(
|
||||||
|
0 => $this->createHunk(23, 2, 42, 2, " 1\n 2"),
|
||||||
|
));
|
||||||
|
$context = $change->makeContextDiff($this->createNewComment(41, 1), 0);
|
||||||
|
$this->assertEqual("@@ -23,1 +42,1 @@\n 1", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOverlapAfterEndOfHunk() {
|
||||||
|
$change = $this->createChange(array(
|
||||||
|
0 => $this->createHunk(23, 2, 42, 2, " 1\n 2"),
|
||||||
|
));
|
||||||
|
$context = $change->makeContextDiff($this->createNewComment(43, 1), 0);
|
||||||
|
$this->assertEqual("@@ -24,1 +43,1 @@\n 2", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInclusionOfNewFileInOldCommentFromStart() {
|
||||||
|
$change = $this->createSingleChange(2, 3,
|
||||||
|
"+n1\n".
|
||||||
|
" e1/2\n".
|
||||||
|
"-o2\n".
|
||||||
|
"+n3\n");
|
||||||
|
$context = $change->makeContextDiff($this->createOldComment(1, 1), 0);
|
||||||
|
$this->assertEqual(
|
||||||
|
"@@ -1,2 +2,1 @@\n".
|
||||||
|
" e1/2\n".
|
||||||
|
"-o2", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInclusionOfOldFileInNewCommentFromStart() {
|
||||||
|
$change = $this->createSingleChange(2, 2,
|
||||||
|
"-o1\n".
|
||||||
|
" e2/1\n".
|
||||||
|
"-o3\n".
|
||||||
|
"+n2\n");
|
||||||
|
$context = $change->makeContextDiff($this->createNewComment(1, 1), 0);
|
||||||
|
$this->assertEqual(
|
||||||
|
"@@ -2,1 +1,2 @@\n".
|
||||||
|
" e2/1\n".
|
||||||
|
"+n2", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoNewlineAtEndOfFile() {
|
||||||
|
$change = $this->createSingleChange(0, 1,
|
||||||
|
"+a\n".
|
||||||
|
"\\No newline at end of file");
|
||||||
|
// Note that this only works with additional context.
|
||||||
|
$context = $change->makeContextDiff($this->createNewComment(2, 0), 1);
|
||||||
|
$this->assertEqual(
|
||||||
|
"@@ +1,1 @@\n".
|
||||||
|
"+a\n".
|
||||||
|
"\\No newline at end of file", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultiLineNewComment() {
|
||||||
|
$change = $this->createSingleChange(7, 7,
|
||||||
|
" e1\n".
|
||||||
|
" e2\n".
|
||||||
|
"-o3\n".
|
||||||
|
"-o4\n".
|
||||||
|
"+n3\n".
|
||||||
|
" e5/4\n".
|
||||||
|
" e6/5\n".
|
||||||
|
"+n6\n".
|
||||||
|
" e7\n");
|
||||||
|
$context = $change->makeContextDiff($this->createNewComment(2, 4), 0);
|
||||||
|
$this->assertEqual(
|
||||||
|
"@@ -2,5 +2,5 @@\n".
|
||||||
|
" e2\n".
|
||||||
|
"-o3\n".
|
||||||
|
"-o4\n".
|
||||||
|
"+n3\n".
|
||||||
|
" e5/4\n".
|
||||||
|
" e6/5\n".
|
||||||
|
"+n6", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMultiLineOldComment() {
|
||||||
|
$change = $this->createSingleChange(7, 7,
|
||||||
|
" e1\n".
|
||||||
|
" e2\n".
|
||||||
|
"-o3\n".
|
||||||
|
"-o4\n".
|
||||||
|
"+n3\n".
|
||||||
|
" e5/4\n".
|
||||||
|
" e6/5\n".
|
||||||
|
"+n6\n".
|
||||||
|
" e7\n");
|
||||||
|
$context = $change->makeContextDiff($this->createOldComment(2, 4), 0);
|
||||||
|
$this->assertEqual(
|
||||||
|
"@@ -2,5 +2,4 @@\n".
|
||||||
|
" e2\n".
|
||||||
|
"-o3\n".
|
||||||
|
"-o4\n".
|
||||||
|
"+n3\n".
|
||||||
|
" e5/4\n".
|
||||||
|
" e6/5", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInclusionOfNewFileInOldCommentFromStartWithContext() {
|
||||||
|
$change = $this->createSingleChange(2, 3,
|
||||||
|
"+n1\n".
|
||||||
|
" e1/2\n".
|
||||||
|
"-o2\n".
|
||||||
|
"+n3\n");
|
||||||
|
$context = $change->makeContextDiff($this->createOldComment(1, 1), 1);
|
||||||
|
$this->assertEqual(
|
||||||
|
"@@ -1,2 +1,2 @@\n".
|
||||||
|
"+n1\n".
|
||||||
|
" e1/2\n".
|
||||||
|
"-o2", $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testInclusionOfOldFileInNewCommentFromStartWithContext() {
|
||||||
|
$change = $this->createSingleChange(2, 2,
|
||||||
|
"-o1\n".
|
||||||
|
" e2/1\n".
|
||||||
|
"-o3\n".
|
||||||
|
"+n2\n");
|
||||||
|
$context = $change->makeContextDiff($this->createNewComment(1, 1), 1);
|
||||||
|
$this->assertEqual(
|
||||||
|
"@@ -1,3 +1,2 @@\n".
|
||||||
|
"-o1\n".
|
||||||
|
" e2/1\n".
|
||||||
|
"-o3\n".
|
||||||
|
"+n2", $context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue