@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #180 from r4nt/differential-unified-comments

Adds an option to allow sending unified diff contexts in differential mails

+309 -2
+7
conf/default.conf.php
··· 382 382 // patches are sent in. Valid options are 'unified' (like diff -u) or 'git'. 383 383 'metamta.differential.patch-format' => 'unified', 384 384 385 + // Enables a different format for comments in differential emails. 386 + // Differential will create unified diffs around the comment, which 387 + // will give enough context for people who are only viewing the 388 + // reviews in email to understand what is going on. The context will 389 + // be created based on the range of the comment. 390 + 'metamta.differential.unified-comment-context' => true, 391 + 385 392 // Prefix prepended to mail sent by Diffusion. 386 393 'metamta.diffusion.subject-prefix' => '[Diffusion]', 387 394
+2
src/__phutil_library_map__.php
··· 216 216 'DifferentialBranchFieldSpecification' => 'applications/differential/field/specification/DifferentialBranchFieldSpecification.php', 217 217 'DifferentialCCWelcomeMail' => 'applications/differential/mail/DifferentialCCWelcomeMail.php', 218 218 'DifferentialCCsFieldSpecification' => 'applications/differential/field/specification/DifferentialCCsFieldSpecification.php', 219 + 'DifferentialChangeSetTestCase' => 'applications/differential/storage/__tests__/DifferentialChangesetTestCase.php', 219 220 'DifferentialChangeType' => 'applications/differential/constants/DifferentialChangeType.php', 220 221 'DifferentialChangeset' => 'applications/differential/storage/DifferentialChangeset.php', 221 222 'DifferentialChangesetDetailView' => 'applications/differential/view/DifferentialChangesetDetailView.php', ··· 1385 1386 'DifferentialBranchFieldSpecification' => 'DifferentialFieldSpecification', 1386 1387 'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail', 1387 1388 'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification', 1389 + 'DifferentialChangeSetTestCase' => 'PhabricatorTestCase', 1388 1390 'DifferentialChangeset' => 'DifferentialDAO', 1389 1391 'DifferentialChangesetDetailView' => 'AphrontView', 1390 1392 'DifferentialChangesetListView' => 'AphrontView',
+21 -2
src/applications/differential/mail/DifferentialCommentMail.php
··· 152 152 if ($inlines) { 153 153 $body[] = 'INLINE COMMENTS'; 154 154 $changesets = $this->getChangesets(); 155 + 156 + if (PhabricatorEnv::getEnvConfig( 157 + 'metamta.differential.unified-comment-context', false)) { 158 + foreach ($changesets as $changeset) { 159 + $changeset->attachHunks($changeset->loadHunks()); 160 + } 161 + } 155 162 foreach ($inlines as $inline) { 156 163 $changeset = $changesets[$inline->getChangesetID()]; 157 164 if (!$changeset) { ··· 165 172 } else { 166 173 $range = $start; 167 174 } 168 - $content = $inline->getContent(); 169 - $body[] = $this->formatText("{$file}:{$range} {$content}"); 175 + 176 + if (!PhabricatorEnv::getEnvConfig( 177 + 'metamta.differential.unified-comment-context', false)) { 178 + $body[] = $this->formatText("{$file}:{$range} {$content}"); 179 + } else { 180 + $body[] = "================"; 181 + $body[] = "Comment at: " . $file . ":" . $range; 182 + $body[] = $changeset->makeContextDiff($inline, 1); 183 + $body[] = "----------------"; 184 + 185 + $content = $inline->getContent(); 186 + $body[] = $content; 187 + $body[] = null; 188 + } 170 189 } 171 190 $body[] = null; 172 191 }
+86
src/applications/differential/storage/DifferentialChangeset.php
··· 215 215 return false; 216 216 } 217 217 218 + public function makeContextDiff($inline, $add_context) { 219 + $context = array(); 220 + $debug = false; 221 + if ($debug) { 222 + $context[] = 'Inline: '.$inline->getIsNewFile().' '. 223 + $inline->getLineNumber().' '.$inline->getLineLength(); 224 + foreach ($this->getHunks() as $hunk) { 225 + $context[] = 'hunk: '.$hunk->getOldOffset().'-'. 226 + $hunk->getOldLen().'; '.$hunk->getNewOffset().'-'.$hunk->getNewLen(); 227 + $context[] = $hunk->getChanges(); 228 + } 229 + } 230 + 231 + if ($inline->getIsNewFile()) { 232 + $prefix = '+'; 233 + } else { 234 + $prefix = '-'; 235 + } 236 + foreach ($this->getHunks() as $hunk) { 237 + if ($inline->getIsNewFile()) { 238 + $offset = $hunk->getNewOffset(); 239 + $length = $hunk->getNewLen(); 240 + } else { 241 + $offset = $hunk->getOldOffset(); 242 + $length = $hunk->getOldLen(); 243 + } 244 + $start = $inline->getLineNumber() - $offset; 245 + $end = $start + $inline->getLineLength(); 246 + // We need to go in if $start == $length, because the last line 247 + // might be a "\No newline at end of file" marker, which we want 248 + // to show if the additional context is > 0. 249 + if ($start <= $length && $end >= 0) { 250 + $start = $start - $add_context; 251 + $end = $end + $add_context; 252 + $hunk_content = array(); 253 + $hunk_pos = array( "-" => 0, "+" => 0 ); 254 + $hunk_offset = array( "-" => NULL, "+" => NULL ); 255 + $hunk_last = array( "-" => NULL, "+" => NULL ); 256 + foreach (explode("\n", $hunk->getChanges()) as $line) { 257 + $in_common = strncmp($line, " ", 1) === 0; 258 + $in_old = strncmp($line, "-", 1) === 0 || $in_common; 259 + $in_new = strncmp($line, "+", 1) === 0 || $in_common; 260 + $in_selected = strncmp($line, $prefix, 1) === 0; 261 + $skip = !$in_selected && !$in_common; 262 + if ($hunk_pos[$prefix] <= $end) { 263 + if ($start <= $hunk_pos[$prefix]) { 264 + if (!$skip || ($hunk_pos[$prefix] != $start && 265 + $hunk_pos[$prefix] != $end)) { 266 + if ($in_old) { 267 + if ($hunk_offset["-"] === NULL) { 268 + $hunk_offset["-"] = $hunk_pos["-"]; 269 + } 270 + $hunk_last["-"] = $hunk_pos["-"]; 271 + } 272 + if ($in_new) { 273 + if ($hunk_offset["+"] === NULL) { 274 + $hunk_offset["+"] = $hunk_pos["+"]; 275 + } 276 + $hunk_last["+"] = $hunk_pos["+"]; 277 + } 278 + 279 + $hunk_content[] = $line; 280 + } 281 + } 282 + if ($in_old) { ++$hunk_pos["-"]; } 283 + if ($in_new) { ++$hunk_pos["+"]; } 284 + } 285 + } 286 + if ($hunk_offset["-"] !== NULL || $hunk_offset["+"] !== NULL) { 287 + $header = "@@"; 288 + if ($hunk_offset["-"] !== NULL) { 289 + $header .= " -" . ($hunk->getOldOffset() + $hunk_offset["-"]) . 290 + "," . ($hunk_last["-"]-$hunk_offset["-"]+1); 291 + } 292 + if ($hunk_offset["+"] !== NULL) { 293 + $header .= " +" . ($hunk->getNewOffset() + $hunk_offset["+"]) . 294 + "," . ($hunk_last["+"]-$hunk_offset["+"]+1); 295 + } 296 + $header .= " @@"; 297 + $context[] = $header; 298 + $context[] = implode("\n", $hunk_content); 299 + } 300 + } 301 + } 302 + return implode("\n", $context); 303 + } 218 304 }
+193
src/applications/differential/storage/__tests__/DifferentialChangesetTestCase.php
··· 1 + <?php 2 + 3 + final class DifferentialChangeSetTestCase extends PhabricatorTestCase { 4 + private function createComment() { 5 + $comment = new DifferentialInlineComment(); 6 + return $comment; 7 + } 8 + // $line: 1 based 9 + // $length: 0 based (0 meaning 1 line) 10 + private function createNewComment($line, $length) { 11 + $comment = $this->createComment(); 12 + $comment->setIsNewFile(True); 13 + $comment->setLineNumber($line); 14 + $comment->setLineLength($length); 15 + return $comment; 16 + } 17 + // $line: 1 based 18 + // $length: 0 based (0 meaning 1 line) 19 + private function createOldComment($line, $length) { 20 + $comment = $this->createComment(); 21 + $comment->setIsNewFile(False); 22 + $comment->setLineNumber($line); 23 + $comment->setLineLength($length); 24 + return $comment; 25 + } 26 + private function createHunk($oldOffset, $oldLen, $newOffset, $newLen, $changes) { 27 + $hunk = new DifferentialHunk(); 28 + $hunk->setOldOffset($oldOffset); 29 + $hunk->setOldLen($oldLen); 30 + $hunk->setNewOffset($newOffset); 31 + $hunk->setNewLen($newLen); 32 + $hunk->setChanges($changes); 33 + return $hunk; 34 + } 35 + private function createChange($hunks) { 36 + $change = new DifferentialChangeset(); 37 + $change->attachHunks($hunks); 38 + return $change; 39 + } 40 + // Returns a change that consists of a single hunk, starting at line 1. 41 + private function createSingleChange($old_lines, $new_lines, $changes) { 42 + return $this->createChange(array( 43 + 0 => $this->createHunk(1, $old_lines, 1, $new_lines, $changes), 44 + )); 45 + } 46 + 47 + public function testOneLineOldComment() { 48 + $change = $this->createSingleChange(1, 0, "-a"); 49 + $context = $change->makeContextDiff($this->createOldComment(1, 0), 0); 50 + $this->assertEqual("@@ -1,1 @@\n-a", $context); 51 + } 52 + 53 + public function testOneLineNewComment() { 54 + $change = $this->createSingleChange(0, 1, "+a"); 55 + $context = $change->makeContextDiff($this->createNewComment(1, 0), 0); 56 + $this->assertEqual("@@ +1,1 @@\n+a", $context); 57 + } 58 + 59 + public function testCannotFindContext() { 60 + $change = $this->createSingleChange(0, 1, "+a"); 61 + $context = $change->makeContextDiff($this->createNewComment(2, 0), 0); 62 + $this->assertEqual("", $context); 63 + } 64 + 65 + public function testOverlapFromStartOfHunk() { 66 + $change = $this->createChange(array( 67 + 0 => $this->createHunk(23, 2, 42, 2, " 1\n 2"), 68 + )); 69 + $context = $change->makeContextDiff($this->createNewComment(41, 1), 0); 70 + $this->assertEqual("@@ -23,1 +42,1 @@\n 1", $context); 71 + } 72 + 73 + public function testOverlapAfterEndOfHunk() { 74 + $change = $this->createChange(array( 75 + 0 => $this->createHunk(23, 2, 42, 2, " 1\n 2"), 76 + )); 77 + $context = $change->makeContextDiff($this->createNewComment(43, 1), 0); 78 + $this->assertEqual("@@ -24,1 +43,1 @@\n 2", $context); 79 + } 80 + 81 + public function testInclusionOfNewFileInOldCommentFromStart() { 82 + $change = $this->createSingleChange(2, 3, 83 + "+n1\n". 84 + " e1/2\n". 85 + "-o2\n". 86 + "+n3\n"); 87 + $context = $change->makeContextDiff($this->createOldComment(1, 1), 0); 88 + $this->assertEqual( 89 + "@@ -1,2 +2,1 @@\n". 90 + " e1/2\n". 91 + "-o2", $context); 92 + } 93 + 94 + public function testInclusionOfOldFileInNewCommentFromStart() { 95 + $change = $this->createSingleChange(2, 2, 96 + "-o1\n". 97 + " e2/1\n". 98 + "-o3\n". 99 + "+n2\n"); 100 + $context = $change->makeContextDiff($this->createNewComment(1, 1), 0); 101 + $this->assertEqual( 102 + "@@ -2,1 +1,2 @@\n". 103 + " e2/1\n". 104 + "+n2", $context); 105 + } 106 + 107 + public function testNoNewlineAtEndOfFile() { 108 + $change = $this->createSingleChange(0, 1, 109 + "+a\n". 110 + "\\No newline at end of file"); 111 + // Note that this only works with additional context. 112 + $context = $change->makeContextDiff($this->createNewComment(2, 0), 1); 113 + $this->assertEqual( 114 + "@@ +1,1 @@\n". 115 + "+a\n". 116 + "\\No newline at end of file", $context); 117 + } 118 + 119 + public function testMultiLineNewComment() { 120 + $change = $this->createSingleChange(7, 7, 121 + " e1\n". 122 + " e2\n". 123 + "-o3\n". 124 + "-o4\n". 125 + "+n3\n". 126 + " e5/4\n". 127 + " e6/5\n". 128 + "+n6\n". 129 + " e7\n"); 130 + $context = $change->makeContextDiff($this->createNewComment(2, 4), 0); 131 + $this->assertEqual( 132 + "@@ -2,5 +2,5 @@\n". 133 + " e2\n". 134 + "-o3\n". 135 + "-o4\n". 136 + "+n3\n". 137 + " e5/4\n". 138 + " e6/5\n". 139 + "+n6", $context); 140 + } 141 + 142 + public function testMultiLineOldComment() { 143 + $change = $this->createSingleChange(7, 7, 144 + " e1\n". 145 + " e2\n". 146 + "-o3\n". 147 + "-o4\n". 148 + "+n3\n". 149 + " e5/4\n". 150 + " e6/5\n". 151 + "+n6\n". 152 + " e7\n"); 153 + $context = $change->makeContextDiff($this->createOldComment(2, 4), 0); 154 + $this->assertEqual( 155 + "@@ -2,5 +2,4 @@\n". 156 + " e2\n". 157 + "-o3\n". 158 + "-o4\n". 159 + "+n3\n". 160 + " e5/4\n". 161 + " e6/5", $context); 162 + } 163 + 164 + public function testInclusionOfNewFileInOldCommentFromStartWithContext() { 165 + $change = $this->createSingleChange(2, 3, 166 + "+n1\n". 167 + " e1/2\n". 168 + "-o2\n". 169 + "+n3\n"); 170 + $context = $change->makeContextDiff($this->createOldComment(1, 1), 1); 171 + $this->assertEqual( 172 + "@@ -1,2 +1,2 @@\n". 173 + "+n1\n". 174 + " e1/2\n". 175 + "-o2", $context); 176 + } 177 + 178 + public function testInclusionOfOldFileInNewCommentFromStartWithContext() { 179 + $change = $this->createSingleChange(2, 2, 180 + "-o1\n". 181 + " e2/1\n". 182 + "-o3\n". 183 + "+n2\n"); 184 + $context = $change->makeContextDiff($this->createNewComment(1, 1), 1); 185 + $this->assertEqual( 186 + "@@ -1,3 +1,2 @@\n". 187 + "-o1\n". 188 + " e2/1\n". 189 + "-o3\n". 190 + "+n2", $context); 191 + } 192 + } 193 +