@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.

Implement basic one-up and test renderers

Summary:
This is a half-step toward one-up and test renderers. This is mostly a structural change, and has no user-facing impact. It splits the rendering hierarchy like this:

- Renderer (more methods are abstract than before)
- HTML Renderer (most HTML stuff has moved down from the base to here)
- HTML 1-up (placeholder only -- not yet a functional implementation)
- HTML 2-up (minimal changes, uses mostly old code)
- Test Renderer (unit-testable renderer base, implements text versions of the HTML stuff)
- Test 1-up (selects 1-up mode for test rendering)
- Test 2-up (selects 2-up mode for test rendering)

Broadly, I'm trying to share as much code as possible by splitting rendering into more, smaller stages. Specifically, we do this:

- Combine the various sorts of inputs (changes, context, inlines, etc.) into a single, relatively homogenous list of "primitives". This happens in the base class.
- The primitive types are: old (diff left side), new (diff right side), context ("show more context"), no-context ("context not available") and inline (inline comment).
- Possibly, apply a filtering/reordering step to the primitives to get them ready for 1-up rendering. This mostly removes information, and does a small amount of reordering. This also happens in the base class.
- Pass the primitives to the actual renderer, to convert them into HTML, text, or whatever else. This happens in the leaf class.

The primitive implementation is not yet complete (it doesn't attach as much information to the primitives as it should -- stuff like coverage and copies), but covers the basics.

The existing HTMLTwoUp renderer does not use the primitive path; instead, it still goes down the old path.

Test Plan: Ran unit tests, looked at a bunch of diffs.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2009

Differential Revision: https://secure.phabricator.com/D4421

+842 -372
+14 -2
src/__phutil_library_map__.php
··· 225 225 'DifferentialChangeset' => 'applications/differential/storage/DifferentialChangeset.php', 226 226 'DifferentialChangesetDetailView' => 'applications/differential/view/DifferentialChangesetDetailView.php', 227 227 'DifferentialChangesetFileTreeSideNavBuilder' => 'applications/differential/view/DifferentialChangesetFileTreeSideNavBuilder.php', 228 + 'DifferentialChangesetHTMLRenderer' => 'applications/differential/render/DifferentialChangesetHTMLRenderer.php', 228 229 'DifferentialChangesetListView' => 'applications/differential/view/DifferentialChangesetListView.php', 230 + 'DifferentialChangesetOneUpRenderer' => 'applications/differential/render/DifferentialChangesetOneUpRenderer.php', 231 + 'DifferentialChangesetOneUpTestRenderer' => 'applications/differential/render/DifferentialChangesetOneUpTestRenderer.php', 229 232 'DifferentialChangesetParser' => 'applications/differential/parser/DifferentialChangesetParser.php', 230 233 'DifferentialChangesetParserTestCase' => 'applications/differential/parser/__tests__/DifferentialChangesetParserTestCase.php', 231 234 'DifferentialChangesetRenderer' => 'applications/differential/render/DifferentialChangesetRenderer.php', 235 + 'DifferentialChangesetTestRenderer' => 'applications/differential/render/DifferentialChangesetTestRenderer.php', 232 236 'DifferentialChangesetTwoUpRenderer' => 'applications/differential/render/DifferentialChangesetTwoUpRenderer.php', 237 + 'DifferentialChangesetTwoUpTestRenderer' => 'applications/differential/render/DifferentialChangesetTwoUpTestRenderer.php', 233 238 'DifferentialChangesetViewController' => 'applications/differential/controller/DifferentialChangesetViewController.php', 234 239 'DifferentialComment' => 'applications/differential/storage/DifferentialComment.php', 235 240 'DifferentialCommentEditor' => 'applications/differential/editor/DifferentialCommentEditor.php', ··· 281 286 'DifferentialMailPhase' => 'applications/differential/constants/DifferentialMailPhase.php', 282 287 'DifferentialManiphestTasksFieldSpecification' => 'applications/differential/field/specification/DifferentialManiphestTasksFieldSpecification.php', 283 288 'DifferentialNewDiffMail' => 'applications/differential/mail/DifferentialNewDiffMail.php', 289 + 'DifferentialParseRenderTestCase' => 'applications/differential/__tests__/DifferentialParseRenderTestCase.php', 284 290 'DifferentialPathFieldSpecification' => 'applications/differential/field/specification/DifferentialPathFieldSpecification.php', 285 291 'DifferentialPrimaryPaneView' => 'applications/differential/view/DifferentialPrimaryPaneView.php', 286 292 'DifferentialReplyHandler' => 'applications/differential/DifferentialReplyHandler.php', ··· 1624 1630 'DifferentialCCsFieldSpecification' => 'DifferentialFieldSpecification', 1625 1631 'DifferentialChangeset' => 'DifferentialDAO', 1626 1632 'DifferentialChangesetDetailView' => 'AphrontView', 1633 + 'DifferentialChangesetHTMLRenderer' => 'DifferentialChangesetRenderer', 1627 1634 'DifferentialChangesetListView' => 'AphrontView', 1628 - 'DifferentialChangesetParserTestCase' => 'ArcanistPhutilTestCase', 1629 - 'DifferentialChangesetTwoUpRenderer' => 'DifferentialChangesetRenderer', 1635 + 'DifferentialChangesetOneUpRenderer' => 'DifferentialChangesetHTMLRenderer', 1636 + 'DifferentialChangesetOneUpTestRenderer' => 'DifferentialChangesetTestRenderer', 1637 + 'DifferentialChangesetParserTestCase' => 'PhabricatorTestCase', 1638 + 'DifferentialChangesetTestRenderer' => 'DifferentialChangesetRenderer', 1639 + 'DifferentialChangesetTwoUpRenderer' => 'DifferentialChangesetHTMLRenderer', 1640 + 'DifferentialChangesetTwoUpTestRenderer' => 'DifferentialChangesetTestRenderer', 1630 1641 'DifferentialChangesetViewController' => 'DifferentialController', 1631 1642 'DifferentialComment' => 1632 1643 array( ··· 1680 1691 'DifferentialLocalCommitsView' => 'AphrontView', 1681 1692 'DifferentialManiphestTasksFieldSpecification' => 'DifferentialFieldSpecification', 1682 1693 'DifferentialNewDiffMail' => 'DifferentialReviewRequestMail', 1694 + 'DifferentialParseRenderTestCase' => 'PhabricatorTestCase', 1683 1695 'DifferentialPathFieldSpecification' => 'DifferentialFieldSpecification', 1684 1696 'DifferentialPrimaryPaneView' => 'AphrontView', 1685 1697 'DifferentialReplyHandler' => 'PhabricatorMailReplyHandler',
+63
src/applications/differential/__tests__/DifferentialParseRenderTestCase.php
··· 1 + <?php 2 + 3 + final class DifferentialParseRenderTestCase extends PhabricatorTestCase { 4 + 5 + public function testParseRender() { 6 + $dir = dirname(__FILE__).'/data/'; 7 + foreach (Filesystem::listDirectory($dir, $show_hidden = false) as $file) { 8 + if (!preg_match('/\.diff$/', $file)) { 9 + continue; 10 + } 11 + $data = Filesystem::readFile($dir.$file); 12 + 13 + $opt_file = $dir.$file.'.options'; 14 + if (Filesystem::pathExists($opt_file)) { 15 + $options = Filesystem::readFile($opt_file); 16 + $options = json_decode($options, true); 17 + if (!is_array($options)) { 18 + throw new Exception("Invalid options file: {$opt_file}."); 19 + } 20 + } else { 21 + $options = array(); 22 + } 23 + 24 + foreach (array('one', 'two') as $type) { 25 + $parser = $this->buildChangesetParser($type, $data, $file); 26 + $actual = $parser->render(null, null, array()); 27 + 28 + $expect = Filesystem::readFile($dir.$file.'.'.$type.'.expect'); 29 + $this->assertEqual($expect, $actual, $file.'.'.$type); 30 + } 31 + } 32 + } 33 + 34 + private function buildChangesetParser($type, $data, $file) { 35 + $parser = new ArcanistDiffParser(); 36 + $changes = $parser->parseDiff($data); 37 + 38 + $diff = DifferentialDiff::newFromRawChanges($changes); 39 + if (count($diff->getChangesets()) !== 1) { 40 + throw new Exception("Expected one changeset: {$file}"); 41 + } 42 + 43 + $changeset = head($diff->getChangesets()); 44 + 45 + $engine = new PhabricatorMarkupEngine(); 46 + 47 + $cparser = new DifferentialChangesetParser(); 48 + $cparser->setDisableCache(true); 49 + $cparser->setChangeset($changeset); 50 + $cparser->setMarkupEngine($engine); 51 + 52 + if ($type == 'one') { 53 + $cparser->setRenderer(new DifferentialChangesetOneUpTestRenderer()); 54 + } else if ($type == 'two') { 55 + $cparser->setRenderer(new DifferentialChangesetTwoUpTestRenderer()); 56 + } else { 57 + throw new Exception("Unknown renderer type '{$type}'!"); 58 + } 59 + 60 + return $cparser; 61 + } 62 + 63 + }
+15
src/applications/differential/__tests__/data/fruit.diff
··· 1 + diff --git a/fruit b/fruit 2 + new file mode 100644 3 + index 0000000..a1f7255 4 + --- /dev/null 5 + +++ b/fruit 6 + @@ -0,0 +1,9 @@ 7 + +apple 8 + +banana 9 + +cherry 10 + +date 11 + +elderberry 12 + +fig 13 + +grape 14 + +honeydew 15 + +
+12
src/applications/differential/__tests__/data/fruit.diff.one.expect
··· 1 + CTYPE 1 1 (unforced) 2 + - 3 + - 4 + N 1 + apple~ 5 + N 2 + banana~ 6 + N 3 + cherry~ 7 + N 4 + date~ 8 + N 5 + elderberry~ 9 + N 6 + fig~ 10 + N 7 + grape~ 11 + N 8 + honeydew~ 12 + N 9 + ~
+21
src/applications/differential/__tests__/data/fruit.diff.two.expect
··· 1 + CTYPE 1 1 (unforced) 2 + - 3 + - 4 + O - . ~ 5 + N 1 + apple~ 6 + O - . ~ 7 + N 2 + banana~ 8 + O - . ~ 9 + N 3 + cherry~ 10 + O - . ~ 11 + N 4 + date~ 12 + O - . ~ 13 + N 5 + elderberry~ 14 + O - . ~ 15 + N 6 + fig~ 16 + O - . ~ 17 + N 7 + grape~ 18 + O - . ~ 19 + N 8 + honeydew~ 20 + O - . ~ 21 + N 9 + ~
+28 -1
src/applications/differential/parser/DifferentialChangesetParser.php
··· 39 39 private $coverage; 40 40 private $markupEngine; 41 41 private $highlightErrors; 42 + private $disableCache; 43 + private $renderer; 44 + 45 + public function setRenderer($renderer) { 46 + $this->renderer = $renderer; 47 + return $this; 48 + } 49 + 50 + public function getRenderer() { 51 + if (!$this->renderer) { 52 + return new DifferentialChangesetTwoUpRenderer(); 53 + } 54 + return $this->renderer; 55 + } 56 + 57 + public function setDisableCache($disable_cache) { 58 + $this->disableCache = $disable_cache; 59 + return $this; 60 + } 61 + 62 + public function getDisableCache() { 63 + return $this->disableCache; 64 + } 42 65 43 66 const CACHE_VERSION = 10; 44 67 const CACHE_MAX_SIZE = 8e6; ··· 434 457 } 435 458 436 459 $skip_cache = ($whitespace_mode != self::WHITESPACE_IGNORE_ALL); 460 + if ($this->disableCache) { 461 + $skip_cache = true; 462 + } 463 + 437 464 $this->whitespaceMode = $whitespace_mode; 438 465 439 466 $changeset = $this->changeset; ··· 664 691 count($this->old), 665 692 count($this->new)); 666 693 667 - $renderer = id(new DifferentialChangesetTwoUpRenderer()) 694 + $renderer = $this->getRenderer() 668 695 ->setChangeset($this->changeset) 669 696 ->setRenderPropertyChangeHeader($render_pch) 670 697 ->setLineCount($rows)
+1 -1
src/applications/differential/parser/__tests__/DifferentialChangesetParserTestCase.php
··· 1 1 <?php 2 2 3 - final class DifferentialChangesetParserTestCase extends ArcanistPhutilTestCase { 3 + final class DifferentialChangesetParserTestCase extends PhabricatorTestCase { 4 4 5 5 public function testDiffChangesets() { 6 6 $hunk = new DifferentialHunk();
+362
src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
··· 1 + <?php 2 + 3 + abstract class DifferentialChangesetHTMLRenderer 4 + extends DifferentialChangesetRenderer { 5 + 6 + protected function renderChangeTypeHeader($force) { 7 + $changeset = $this->getChangeset(); 8 + 9 + $change = $changeset->getChangeType(); 10 + $file = $changeset->getFileType(); 11 + 12 + $message = null; 13 + if ($change == DifferentialChangeType::TYPE_CHANGE && 14 + $file == DifferentialChangeType::FILE_TEXT) { 15 + if ($force) { 16 + // We have to force something to render because there were no changes 17 + // of other kinds. 18 + $message = pht('This file was not modified.'); 19 + } else { 20 + // Default case of changes to a text file, no metadata. 21 + return null; 22 + } 23 + } else { 24 + switch ($change) { 25 + 26 + case DifferentialChangeType::TYPE_ADD: 27 + switch ($file) { 28 + case DifferentialChangeType::FILE_TEXT: 29 + $message = pht('This file was <strong>added</strong>.'); 30 + break; 31 + case DifferentialChangeType::FILE_IMAGE: 32 + $message = pht('This image was <strong>added</strong>.'); 33 + break; 34 + case DifferentialChangeType::FILE_DIRECTORY: 35 + $message = pht('This directory was <strong>added</strong>.'); 36 + break; 37 + case DifferentialChangeType::FILE_BINARY: 38 + $message = pht('This binary file was <strong>added</strong>.'); 39 + break; 40 + case DifferentialChangeType::FILE_SYMLINK: 41 + $message = pht('This symlink was <strong>added</strong>.'); 42 + break; 43 + case DifferentialChangeType::FILE_SUBMODULE: 44 + $message = pht('This submodule was <strong>added</strong>.'); 45 + break; 46 + } 47 + break; 48 + 49 + case DifferentialChangeType::TYPE_DELETE: 50 + switch ($file) { 51 + case DifferentialChangeType::FILE_TEXT: 52 + $message = pht('This file was <strong>deleted</strong>.'); 53 + break; 54 + case DifferentialChangeType::FILE_IMAGE: 55 + $message = pht('This image was <strong>deleted</strong>.'); 56 + break; 57 + case DifferentialChangeType::FILE_DIRECTORY: 58 + $message = pht('This directory was <strong>deleted</strong>.'); 59 + break; 60 + case DifferentialChangeType::FILE_BINARY: 61 + $message = pht('This binary file was <strong>deleted</strong>.'); 62 + break; 63 + case DifferentialChangeType::FILE_SYMLINK: 64 + $message = pht('This symlink was <strong>deleted</strong>.'); 65 + break; 66 + case DifferentialChangeType::FILE_SUBMODULE: 67 + $message = pht('This submodule was <strong>deleted</strong>.'); 68 + break; 69 + } 70 + break; 71 + 72 + case DifferentialChangeType::TYPE_MOVE_HERE: 73 + $from = 74 + "<strong>". 75 + phutil_escape_html($changeset->getOldFile()). 76 + "</strong>"; 77 + switch ($file) { 78 + case DifferentialChangeType::FILE_TEXT: 79 + $message = pht('This file was moved from %s.', $from); 80 + break; 81 + case DifferentialChangeType::FILE_IMAGE: 82 + $message = pht('This image was moved from %s.', $from); 83 + break; 84 + case DifferentialChangeType::FILE_DIRECTORY: 85 + $message = pht('This directory was moved from %s.', $from); 86 + break; 87 + case DifferentialChangeType::FILE_BINARY: 88 + $message = pht('This binary file was moved from %s.', $from); 89 + break; 90 + case DifferentialChangeType::FILE_SYMLINK: 91 + $message = pht('This symlink was moved from %s.', $from); 92 + break; 93 + case DifferentialChangeType::FILE_SUBMODULE: 94 + $message = pht('This submodule was moved from %s.', $from); 95 + break; 96 + } 97 + break; 98 + 99 + case DifferentialChangeType::TYPE_COPY_HERE: 100 + $from = 101 + "<strong>". 102 + phutil_escape_html($changeset->getOldFile()). 103 + "</strong>"; 104 + switch ($file) { 105 + case DifferentialChangeType::FILE_TEXT: 106 + $message = pht('This file was copied from %s.', $from); 107 + break; 108 + case DifferentialChangeType::FILE_IMAGE: 109 + $message = pht('This image was copied from %s.', $from); 110 + break; 111 + case DifferentialChangeType::FILE_DIRECTORY: 112 + $message = pht('This directory was copied from %s.', $from); 113 + break; 114 + case DifferentialChangeType::FILE_BINARY: 115 + $message = pht('This binary file was copied from %s.', $from); 116 + break; 117 + case DifferentialChangeType::FILE_SYMLINK: 118 + $message = pht('This symlink was copied from %s.', $from); 119 + break; 120 + case DifferentialChangeType::FILE_SUBMODULE: 121 + $message = pht('This submodule was copied from %s.', $from); 122 + break; 123 + } 124 + break; 125 + 126 + case DifferentialChangeType::TYPE_MOVE_AWAY: 127 + $paths = 128 + "<strong>". 129 + phutil_escape_html(implode(', ', $changeset->getAwayPaths())). 130 + "</strong>"; 131 + switch ($file) { 132 + case DifferentialChangeType::FILE_TEXT: 133 + $message = pht('This file was moved to %s.', $paths); 134 + break; 135 + case DifferentialChangeType::FILE_IMAGE: 136 + $message = pht('This image was moved to %s.', $paths); 137 + break; 138 + case DifferentialChangeType::FILE_DIRECTORY: 139 + $message = pht('This directory was moved to %s.', $paths); 140 + break; 141 + case DifferentialChangeType::FILE_BINARY: 142 + $message = pht('This binary file was moved to %s.', $paths); 143 + break; 144 + case DifferentialChangeType::FILE_SYMLINK: 145 + $message = pht('This symlink was moved to %s.', $paths); 146 + break; 147 + case DifferentialChangeType::FILE_SUBMODULE: 148 + $message = pht('This submodule was moved to %s.', $paths); 149 + break; 150 + } 151 + break; 152 + 153 + case DifferentialChangeType::TYPE_COPY_AWAY: 154 + $paths = 155 + "<strong>". 156 + phutil_escape_html(implode(', ', $changeset->getAwayPaths())). 157 + "</strong>"; 158 + switch ($file) { 159 + case DifferentialChangeType::FILE_TEXT: 160 + $message = pht('This file was copied to %s.', $paths); 161 + break; 162 + case DifferentialChangeType::FILE_IMAGE: 163 + $message = pht('This image was copied to %s.', $paths); 164 + break; 165 + case DifferentialChangeType::FILE_DIRECTORY: 166 + $message = pht('This directory was copied to %s.', $paths); 167 + break; 168 + case DifferentialChangeType::FILE_BINARY: 169 + $message = pht('This binary file was copied to %s.', $paths); 170 + break; 171 + case DifferentialChangeType::FILE_SYMLINK: 172 + $message = pht('This symlink was copied to %s.', $paths); 173 + break; 174 + case DifferentialChangeType::FILE_SUBMODULE: 175 + $message = pht('This submodule was copied to %s.', $paths); 176 + break; 177 + } 178 + break; 179 + 180 + case DifferentialChangeType::TYPE_MULTICOPY: 181 + $paths = 182 + "<strong>". 183 + phutil_escape_html(implode(', ', $changeset->getAwayPaths())). 184 + "</strong>"; 185 + switch ($file) { 186 + case DifferentialChangeType::FILE_TEXT: 187 + $message = pht( 188 + 'This file was deleted after being copied to %s.', 189 + $paths); 190 + break; 191 + case DifferentialChangeType::FILE_IMAGE: 192 + $message = pht( 193 + 'This image was deleted after being copied to %s.', 194 + $paths); 195 + break; 196 + case DifferentialChangeType::FILE_DIRECTORY: 197 + $message = pht( 198 + 'This directory was deleted after being copied to %s.', 199 + $paths); 200 + break; 201 + case DifferentialChangeType::FILE_BINARY: 202 + $message = pht( 203 + 'This binary file was deleted after being copied to %s.', 204 + $paths); 205 + break; 206 + case DifferentialChangeType::FILE_SYMLINK: 207 + $message = pht( 208 + 'This symlink was deleted after being copied to %s.', 209 + $paths); 210 + break; 211 + case DifferentialChangeType::FILE_SUBMODULE: 212 + $message = pht( 213 + 'This submodule was deleted after being copied to %s.', 214 + $paths); 215 + break; 216 + } 217 + break; 218 + 219 + default: 220 + switch ($file) { 221 + case DifferentialChangeType::FILE_TEXT: 222 + $message = pht('This is a file.'); 223 + break; 224 + case DifferentialChangeType::FILE_IMAGE: 225 + $message = pht('This is an image.'); 226 + break; 227 + case DifferentialChangeType::FILE_DIRECTORY: 228 + $message = pht('This is a directory.'); 229 + break; 230 + case DifferentialChangeType::FILE_BINARY: 231 + $message = pht('This is a binary file.'); 232 + break; 233 + case DifferentialChangeType::FILE_SYMLINK: 234 + $message = pht('This is a symlink.'); 235 + break; 236 + case DifferentialChangeType::FILE_SUBMODULE: 237 + $message = pht('This is a submodule.'); 238 + break; 239 + } 240 + break; 241 + } 242 + } 243 + 244 + return 245 + '<div class="differential-meta-notice">'. 246 + $message. 247 + '</div>'; 248 + } 249 + 250 + protected function renderPropertyChangeHeader() { 251 + $changeset = $this->getChangeset(); 252 + 253 + $old = $changeset->getOldProperties(); 254 + $new = $changeset->getNewProperties(); 255 + 256 + $keys = array_keys($old + $new); 257 + sort($keys); 258 + 259 + $rows = array(); 260 + foreach ($keys as $key) { 261 + $oval = idx($old, $key); 262 + $nval = idx($new, $key); 263 + if ($oval !== $nval) { 264 + if ($oval === null) { 265 + $oval = '<em>null</em>'; 266 + } else { 267 + $oval = nl2br(phutil_escape_html($oval)); 268 + } 269 + 270 + if ($nval === null) { 271 + $nval = '<em>null</em>'; 272 + } else { 273 + $nval = nl2br(phutil_escape_html($nval)); 274 + } 275 + 276 + $rows[] = 277 + '<tr>'. 278 + '<th>'.phutil_escape_html($key).'</th>'. 279 + '<td class="oval">'.$oval.'</td>'. 280 + '<td class="nval">'.$nval.'</td>'. 281 + '</tr>'; 282 + } 283 + } 284 + 285 + return 286 + '<table class="differential-property-table">'. 287 + '<tr class="property-table-header">'. 288 + '<th>Property Changes</th>'. 289 + '<td class="oval">Old Value</td>'. 290 + '<td class="nval">New Value</td>'. 291 + '</tr>'. 292 + implode('', $rows). 293 + '</table>'; 294 + } 295 + 296 + protected function renderShield($message, $force = 'default') { 297 + $end = count($this->getOldLines()); 298 + $reference = $this->getRenderingReference(); 299 + 300 + if ($force !== 'text' && 301 + $force !== 'whitespace' && 302 + $force !== 'none' && 303 + $force !== 'default') { 304 + throw new Exception("Invalid 'force' parameter '{$force}'!"); 305 + } 306 + 307 + $range = "0-{$end}"; 308 + if ($force == 'text') { 309 + // If we're forcing text, force the whole file to be rendered. 310 + $range = "{$range}/0-{$end}"; 311 + } 312 + 313 + $meta = array( 314 + 'ref' => $reference, 315 + 'range' => $range, 316 + ); 317 + 318 + if ($force == 'whitespace') { 319 + $meta['whitespace'] = DifferentialChangesetParser::WHITESPACE_SHOW_ALL; 320 + } 321 + 322 + $more = null; 323 + if ($force !== 'none') { 324 + $more = ' '.javelin_render_tag( 325 + 'a', 326 + array( 327 + 'mustcapture' => true, 328 + 'sigil' => 'show-more', 329 + 'class' => 'complete', 330 + 'href' => '#', 331 + 'meta' => $meta, 332 + ), 333 + 'Show File Contents'); 334 + } 335 + 336 + return javelin_render_tag( 337 + 'tr', 338 + array( 339 + 'sigil' => 'context-target', 340 + ), 341 + '<td class="differential-shield" colspan="6">'. 342 + phutil_escape_html($message). 343 + $more. 344 + '</td>'); 345 + } 346 + 347 + protected function wrapChangeInTable($content) { 348 + if (!$content) { 349 + return null; 350 + } 351 + 352 + return javelin_render_tag( 353 + 'table', 354 + array( 355 + 'class' => 'differential-diff remarkup-code PhabricatorMonospaced', 356 + 'sigil' => 'differential-diff', 357 + ), 358 + $content); 359 + } 360 + 361 + 362 + }
+28
src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
··· 1 + <?php 2 + 3 + final class DifferentialChangesetOneUpRenderer 4 + extends DifferentialChangesetHTMLRenderer { 5 + 6 + public function isOneUpRenderer() { 7 + return true; 8 + } 9 + 10 + public function renderChangesetTable($contents) { 11 + throw new Exception("Not implemented!"); 12 + } 13 + 14 + public function renderTextChange( 15 + $range_start, 16 + $range_len, 17 + $rows) { 18 + throw new Exception("Not implemented!"); 19 + } 20 + 21 + public function renderFileChange($old_file = null, 22 + $new_file = null, 23 + $id = 0, 24 + $vs = 0) { 25 + throw new Exception("Not implemented!"); 26 + } 27 + 28 + }
+10
src/applications/differential/render/DifferentialChangesetOneUpTestRenderer.php
··· 1 + <?php 2 + 3 + final class DifferentialChangesetOneUpTestRenderer 4 + extends DifferentialChangesetTestRenderer { 5 + 6 + public function isOneUpRenderer() { 7 + return true; 8 + } 9 + 10 + }
+187 -331
src/applications/differential/render/DifferentialChangesetRenderer.php
··· 245 245 return $this->renderPropertyChangeHeader; 246 246 } 247 247 248 - abstract public function renderChangesetTable($contents); 248 + final public function renderChangesetTable($content) { 249 + $props = null; 250 + if ($this->shouldRenderPropertyChangeHeader()) { 251 + $props = $this->renderPropertyChangeHeader(); 252 + } 253 + 254 + $force = (!$content && !$props); 255 + $notice = $this->renderChangeTypeHeader($force); 256 + 257 + $result = $notice.$props.$content; 258 + 259 + // TODO: Let the user customize their tab width / display style. 260 + // TODO: We should possibly post-process "\r" as well. 261 + // TODO: Both these steps should happen earlier. 262 + $result = str_replace("\t", ' ', $result); 263 + 264 + return $result; 265 + } 266 + 267 + abstract public function isOneUpRenderer(); 249 268 abstract public function renderTextChange( 250 269 $range_start, 251 270 $range_len, ··· 257 276 $id = 0, 258 277 $vs = 0 259 278 ); 279 + 280 + abstract protected function renderChangeTypeHeader($force); 281 + 282 + protected function didRenderChangesetTableContents($contents) { 283 + return $contents; 284 + } 260 285 261 286 /** 262 287 * Render a "shield" over the diff, with a message like "This file is ··· 282 307 * @param string|null Force mode, see above. 283 308 * @return string Shield markup. 284 309 */ 285 - public function renderShield($message, $force = 'default') { 310 + abstract protected function renderShield($message, $force = 'default'); 286 311 287 - $end = $this->getLineCount(); 288 - $reference = $this->getRenderingReference(); 312 + abstract protected function renderPropertyChangeHeader(); 289 313 290 - if ($force !== 'text' && 291 - $force !== 'whitespace' && 292 - $force !== 'none' && 293 - $force !== 'default') { 294 - throw new Exception("Invalid 'force' parameter '{$force}'!"); 295 - } 314 + protected function renderInlineComment( 315 + PhabricatorInlineCommentInterface $comment, 316 + $on_right = false) { 296 317 297 - $range = "0-{$end}"; 298 - if ($force == 'text') { 299 - // If we're forcing text, force the whole file to be rendered. 300 - $range = "{$range}/0-{$end}"; 301 - } 318 + $user = $this->getUser(); 319 + $edit = $user && 320 + ($comment->getAuthorPHID() == $user->getPHID()) && 321 + ($comment->isDraft()); 322 + $allow_reply = (bool)$user; 302 323 303 - $meta = array( 304 - 'ref' => $reference, 305 - 'range' => $range, 306 - ); 324 + return id(new DifferentialInlineCommentView()) 325 + ->setInlineComment($comment) 326 + ->setOnRight($on_right) 327 + ->setHandles($this->getHandles()) 328 + ->setMarkupEngine($this->getMarkupEngine()) 329 + ->setEditable($edit) 330 + ->setAllowReply($allow_reply) 331 + ->render(); 332 + } 307 333 308 - if ($force == 'whitespace') { 309 - $meta['whitespace'] = DifferentialChangesetParser::WHITESPACE_SHOW_ALL; 310 - } 334 + protected function buildPrimitives($range_start, $range_len) { 335 + $primitives = array(); 336 + 337 + $hunk_starts = $this->getHunkStartLines(); 311 338 312 - $more = null; 313 - if ($force !== 'none') { 314 - $more = ' '.javelin_render_tag( 315 - 'a', 316 - array( 317 - 'mustcapture' => true, 318 - 'sigil' => 'show-more', 319 - 'class' => 'complete', 320 - 'href' => '#', 321 - 'meta' => $meta, 322 - ), 323 - 'Show File Contents'); 324 - } 339 + $mask = $this->getMask(); 340 + $gaps = $this->getGaps(); 325 341 326 - return javelin_render_tag( 327 - 'tr', 328 - array( 329 - 'sigil' => 'context-target', 330 - ), 331 - '<td class="differential-shield" colspan="6">'. 332 - phutil_escape_html($message). 333 - $more. 334 - '</td>'); 335 - } 342 + $old = $this->getOldLines(); 343 + $new = $this->getNewLines(); 344 + $old_render = $this->getOldRender(); 345 + $new_render = $this->getNewRender(); 346 + $old_comments = $this->getOldComments(); 347 + $new_comments = $this->getNewComments(); 336 348 349 + $size = count($old); 350 + for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) { 351 + if (empty($mask[$ii])) { 352 + list($top, $len) = array_pop($gaps); 353 + $primitives[] = array( 354 + 'type' => 'context', 355 + 'top' => $top, 356 + 'len' => $len, 357 + ); 337 358 338 - protected function renderPropertyChangeHeader($changeset) { 339 - if (!$this->shouldRenderPropertyChangeHeader()) { 340 - return null; 341 - } 359 + $ii += ($len - 1); 360 + continue; 361 + } 342 362 343 - $old = $changeset->getOldProperties(); 344 - $new = $changeset->getNewProperties(); 363 + $ospec = array( 364 + 'type' => 'old', 365 + 'htype' => null, 366 + 'cursor' => $ii, 367 + 'line' => null, 368 + 'render' => null, 369 + ); 345 370 346 - $keys = array_keys($old + $new); 347 - sort($keys); 371 + $nspec = array( 372 + 'type' => 'new', 373 + 'htype' => null, 374 + 'cursor' => $ii, 375 + 'line' => null, 376 + 'render' => null, 377 + 'copy' => null, 378 + 'coverage' => null, 379 + ); 348 380 349 - $rows = array(); 350 - foreach ($keys as $key) { 351 - $oval = idx($old, $key); 352 - $nval = idx($new, $key); 353 - if ($oval !== $nval) { 354 - if ($oval === null) { 355 - $oval = '<em>null</em>'; 356 - } else { 357 - $oval = nl2br(phutil_escape_html($oval)); 381 + if (isset($old[$ii])) { 382 + $ospec['line'] = $old[$ii]['line']; 383 + $ospec['htype'] = $old[$ii]['type']; 384 + if (isset($old_render[$ii])) { 385 + $ospec['render'] = $old_render[$ii]; 358 386 } 387 + } 359 388 360 - if ($nval === null) { 361 - $nval = '<em>null</em>'; 362 - } else { 363 - $nval = nl2br(phutil_escape_html($nval)); 389 + if (isset($new[$ii])) { 390 + $nspec['line'] = $new[$ii]['line']; 391 + $nspec['htype'] = $new[$ii]['type']; 392 + if (isset($new_render[$ii])) { 393 + $nspec['render'] = $new_render[$ii]; 364 394 } 395 + } 365 396 366 - $rows[] = 367 - '<tr>'. 368 - '<th>'.phutil_escape_html($key).'</th>'. 369 - '<td class="oval">'.$oval.'</td>'. 370 - '<td class="nval">'.$nval.'</td>'. 371 - '</tr>'; 397 + if (isset($hunk_starts[$ospec['line']])) { 398 + $primitives[] = array( 399 + 'type' => 'no-context', 400 + ); 372 401 } 373 - } 374 402 375 - return 376 - '<table class="differential-property-table">'. 377 - '<tr class="property-table-header">'. 378 - '<th>Property Changes</th>'. 379 - '<td class="oval">Old Value</td>'. 380 - '<td class="nval">New Value</td>'. 381 - '</tr>'. 382 - implode('', $rows). 383 - '</table>'; 384 - } 403 + $primitives[] = $ospec; 404 + $primitives[] = $nspec; 385 405 386 - protected function renderChangeTypeHeader($changeset, $force) { 387 - $change = $changeset->getChangeType(); 388 - $file = $changeset->getFileType(); 389 - 390 - $message = null; 391 - if ($change == DifferentialChangeType::TYPE_CHANGE && 392 - $file == DifferentialChangeType::FILE_TEXT) { 393 - if ($force) { 394 - // We have to force something to render because there were no changes 395 - // of other kinds. 396 - $message = pht('This file was not modified.'); 397 - } else { 398 - // Default case of changes to a text file, no metadata. 399 - return null; 406 + if ($ospec['line'] !== null && isset($old_comments[$ospec['line']])) { 407 + foreach ($old_comments[$ospec['line']] as $comment) { 408 + $primitives[] = array( 409 + 'type' => 'inline', 410 + 'comment' => $comment, 411 + 'right' => false, 412 + ); 413 + } 400 414 } 401 - } else { 402 - switch ($change) { 403 415 404 - case DifferentialChangeType::TYPE_ADD: 405 - switch ($file) { 406 - case DifferentialChangeType::FILE_TEXT: 407 - $message = pht('This file was <strong>added</strong>.'); 408 - break; 409 - case DifferentialChangeType::FILE_IMAGE: 410 - $message = pht('This image was <strong>added</strong>.'); 411 - break; 412 - case DifferentialChangeType::FILE_DIRECTORY: 413 - $message = pht('This directory was <strong>added</strong>.'); 414 - break; 415 - case DifferentialChangeType::FILE_BINARY: 416 - $message = pht('This binary file was <strong>added</strong>.'); 417 - break; 418 - case DifferentialChangeType::FILE_SYMLINK: 419 - $message = pht('This symlink was <strong>added</strong>.'); 420 - break; 421 - case DifferentialChangeType::FILE_SUBMODULE: 422 - $message = pht('This submodule was <strong>added</strong>.'); 423 - break; 424 - } 425 - break; 426 - 427 - case DifferentialChangeType::TYPE_DELETE: 428 - switch ($file) { 429 - case DifferentialChangeType::FILE_TEXT: 430 - $message = pht('This file was <strong>deleted</strong>.'); 431 - break; 432 - case DifferentialChangeType::FILE_IMAGE: 433 - $message = pht('This image was <strong>deleted</strong>.'); 434 - break; 435 - case DifferentialChangeType::FILE_DIRECTORY: 436 - $message = pht('This directory was <strong>deleted</strong>.'); 437 - break; 438 - case DifferentialChangeType::FILE_BINARY: 439 - $message = pht('This binary file was <strong>deleted</strong>.'); 440 - break; 441 - case DifferentialChangeType::FILE_SYMLINK: 442 - $message = pht('This symlink was <strong>deleted</strong>.'); 443 - break; 444 - case DifferentialChangeType::FILE_SUBMODULE: 445 - $message = pht('This submodule was <strong>deleted</strong>.'); 446 - break; 447 - } 448 - break; 416 + if ($nspec['line'] !== null && isset($new_comments[$nspec['line']])) { 417 + foreach ($new_comments[$nspec['line']] as $comment) { 418 + $primitives[] = array( 419 + 'type' => 'inline', 420 + 'comment' => 'comment', 421 + 'right' => true, 422 + ); 423 + } 424 + } 449 425 450 - case DifferentialChangeType::TYPE_MOVE_HERE: 451 - $from = 452 - "<strong>". 453 - phutil_escape_html($changeset->getOldFile()). 454 - "</strong>"; 455 - switch ($file) { 456 - case DifferentialChangeType::FILE_TEXT: 457 - $message = pht('This file was moved from %s.', $from); 458 - break; 459 - case DifferentialChangeType::FILE_IMAGE: 460 - $message = pht('This image was moved from %s.', $from); 461 - break; 462 - case DifferentialChangeType::FILE_DIRECTORY: 463 - $message = pht('This directory was moved from %s.', $from); 464 - break; 465 - case DifferentialChangeType::FILE_BINARY: 466 - $message = pht('This binary file was moved from %s.', $from); 467 - break; 468 - case DifferentialChangeType::FILE_SYMLINK: 469 - $message = pht('This symlink was moved from %s.', $from); 470 - break; 471 - case DifferentialChangeType::FILE_SUBMODULE: 472 - $message = pht('This submodule was moved from %s.', $from); 473 - break; 474 - } 475 - break; 426 + if ($hunk_starts && ($ii == $size - 1)) { 427 + $primitives[] = array( 428 + 'type' => 'no-context', 429 + ); 430 + } 431 + } 476 432 477 - case DifferentialChangeType::TYPE_COPY_HERE: 478 - $from = 479 - "<strong>". 480 - phutil_escape_html($changeset->getOldFile()). 481 - "</strong>"; 482 - switch ($file) { 483 - case DifferentialChangeType::FILE_TEXT: 484 - $message = pht('This file was copied from %s.', $from); 485 - break; 486 - case DifferentialChangeType::FILE_IMAGE: 487 - $message = pht('This image was copied from %s.', $from); 488 - break; 489 - case DifferentialChangeType::FILE_DIRECTORY: 490 - $message = pht('This directory was copied from %s.', $from); 491 - break; 492 - case DifferentialChangeType::FILE_BINARY: 493 - $message = pht('This binary file was copied from %s.', $from); 494 - break; 495 - case DifferentialChangeType::FILE_SYMLINK: 496 - $message = pht('This symlink was copied from %s.', $from); 497 - break; 498 - case DifferentialChangeType::FILE_SUBMODULE: 499 - $message = pht('This submodule was copied from %s.', $from); 500 - break; 501 - } 502 - break; 433 + if ($this->isOneUpRenderer()) { 434 + $primitives = $this->processPrimitivesForOneUp($primitives); 435 + } 503 436 504 - case DifferentialChangeType::TYPE_MOVE_AWAY: 505 - $paths = 506 - "<strong>". 507 - phutil_escape_html(implode(', ', $changeset->getAwayPaths())). 508 - "</strong>"; 509 - switch ($file) { 510 - case DifferentialChangeType::FILE_TEXT: 511 - $message = pht('This file was moved to %s.', $paths); 512 - break; 513 - case DifferentialChangeType::FILE_IMAGE: 514 - $message = pht('This image was moved to %s.', $paths); 515 - break; 516 - case DifferentialChangeType::FILE_DIRECTORY: 517 - $message = pht('This directory was moved to %s.', $paths); 518 - break; 519 - case DifferentialChangeType::FILE_BINARY: 520 - $message = pht('This binary file was moved to %s.', $paths); 521 - break; 522 - case DifferentialChangeType::FILE_SYMLINK: 523 - $message = pht('This symlink was moved to %s.', $paths); 524 - break; 525 - case DifferentialChangeType::FILE_SUBMODULE: 526 - $message = pht('This submodule was moved to %s.', $paths); 527 - break; 528 - } 529 - break; 437 + return $primitives; 438 + } 530 439 531 - case DifferentialChangeType::TYPE_COPY_AWAY: 532 - $paths = 533 - "<strong>". 534 - phutil_escape_html(implode(', ', $changeset->getAwayPaths())). 535 - "</strong>"; 536 - switch ($file) { 537 - case DifferentialChangeType::FILE_TEXT: 538 - $message = pht('This file was copied to %s.', $paths); 539 - break; 540 - case DifferentialChangeType::FILE_IMAGE: 541 - $message = pht('This image was copied to %s.', $paths); 542 - break; 543 - case DifferentialChangeType::FILE_DIRECTORY: 544 - $message = pht('This directory was copied to %s.', $paths); 545 - break; 546 - case DifferentialChangeType::FILE_BINARY: 547 - $message = pht('This binary file was copied to %s.', $paths); 548 - break; 549 - case DifferentialChangeType::FILE_SYMLINK: 550 - $message = pht('This symlink was copied to %s.', $paths); 551 - break; 552 - case DifferentialChangeType::FILE_SUBMODULE: 553 - $message = pht('This submodule was copied to %s.', $paths); 554 - break; 555 - } 556 - break; 440 + private function processPrimitivesForOneUp(array $primitives) { 441 + // Primitives come out of buildPrimitives() in two-up format, because it 442 + // is the most general, flexible format. To put them into one-up format, 443 + // we need to filter and reorder them. In particular: 444 + // 445 + // - We discard unchanged lines in the old file; in one-up format, we 446 + // render them only once. 447 + // - We group contiguous blocks of old-modified and new-modified lines, so 448 + // they render in "block of old, block of new" order instead of 449 + // alternating old and new lines. 557 450 558 - case DifferentialChangeType::TYPE_MULTICOPY: 559 - $paths = 560 - "<strong>". 561 - phutil_escape_html(implode(', ', $changeset->getAwayPaths())). 562 - "</strong>"; 563 - switch ($file) { 564 - case DifferentialChangeType::FILE_TEXT: 565 - $message = pht( 566 - 'This file was deleted after being copied to %s.', 567 - $paths); 568 - break; 569 - case DifferentialChangeType::FILE_IMAGE: 570 - $message = pht( 571 - 'This image was deleted after being copied to %s.', 572 - $paths); 573 - break; 574 - case DifferentialChangeType::FILE_DIRECTORY: 575 - $message = pht( 576 - 'This directory was deleted after being copied to %s.', 577 - $paths); 578 - break; 579 - case DifferentialChangeType::FILE_BINARY: 580 - $message = pht( 581 - 'This binary file was deleted after being copied to %s.', 582 - $paths); 583 - break; 584 - case DifferentialChangeType::FILE_SYMLINK: 585 - $message = pht( 586 - 'This symlink was deleted after being copied to %s.', 587 - $paths); 588 - break; 589 - case DifferentialChangeType::FILE_SUBMODULE: 590 - $message = pht( 591 - 'This submodule was deleted after being copied to %s.', 592 - $paths); 593 - break; 594 - } 595 - break; 451 + $out = array(); 596 452 597 - default: 598 - switch ($file) { 599 - case DifferentialChangeType::FILE_TEXT: 600 - $message = pht('This is a file.'); 601 - break; 602 - case DifferentialChangeType::FILE_IMAGE: 603 - $message = pht('This is an image.'); 604 - break; 605 - case DifferentialChangeType::FILE_DIRECTORY: 606 - $message = pht('This is a directory.'); 607 - break; 608 - case DifferentialChangeType::FILE_BINARY: 609 - $message = pht('This is a binary file.'); 610 - break; 611 - case DifferentialChangeType::FILE_SYMLINK: 612 - $message = pht('This is a symlink.'); 613 - break; 614 - case DifferentialChangeType::FILE_SUBMODULE: 615 - $message = pht('This is a submodule.'); 616 - break; 617 - } 618 - break; 453 + $old_buf = array(); 454 + $new_buf = array(); 455 + foreach ($primitives as $primitive) { 456 + $type = $primitive['type']; 457 + if ($type == 'old') { 458 + if (!$primitive['htype']) { 459 + // This is a line which appears in both the old file and the new 460 + // file, or the spacer corresponding to a line added in the new file. 461 + // Ignore it when rendering a one-up diff. 462 + continue; 463 + } 464 + if ($new_buf) { 465 + $out[] = $new_buf; 466 + $new_buf = array(); 467 + } 468 + $old_buf[] = $primitive; 469 + } else if ($type == 'new') { 470 + if ($primitive['line'] === null) { 471 + // This is an empty spacer corresponding to a line removed from the 472 + // old file. Ignore it when rendering a one-up diff. 473 + continue; 474 + } 475 + if ($old_buf) { 476 + $out[] = $old_buf; 477 + $old_buf = array(); 478 + } 479 + $new_buf[] = $primitive; 480 + } else if ($type == 'context' || $type == 'no-context') { 481 + $out[] = $old_buf; 482 + $out[] = $new_buf; 483 + $old_buf = array(); 484 + $new_buf = array(); 485 + $out[] = array($primitive); 486 + } else if ($type == 'inline') { 487 + if ($primitive['right']) { 488 + $new_buf[] = $primitive; 489 + } else { 490 + $old_buf[] = $primitive; 491 + } 492 + } else { 493 + throw new Exception("Unknown primitive type '{$primitive}'!"); 619 494 } 620 495 } 621 496 622 - return 623 - '<div class="differential-meta-notice">'. 624 - $message. 625 - '</div>'; 626 - } 627 - 628 - protected function renderInlineComment( 629 - PhabricatorInlineCommentInterface $comment, 630 - $on_right = false) { 631 - 632 - $user = $this->getUser(); 633 - $edit = $user && 634 - ($comment->getAuthorPHID() == $user->getPHID()) && 635 - ($comment->isDraft()); 636 - $allow_reply = (bool)$user; 497 + $out[] = $old_buf; 498 + $out[] = $new_buf; 499 + $out = array_mergev($out); 637 500 638 - return id(new DifferentialInlineCommentView()) 639 - ->setInlineComment($comment) 640 - ->setOnRight($on_right) 641 - ->setHandles($this->getHandles()) 642 - ->setMarkupEngine($this->getMarkupEngine()) 643 - ->setEditable($edit) 644 - ->setAllowReply($allow_reply) 645 - ->render(); 501 + return $out; 646 502 } 647 503 648 504 }
+86
src/applications/differential/render/DifferentialChangesetTestRenderer.php
··· 1 + <?php 2 + 3 + abstract class DifferentialChangesetTestRenderer 4 + extends DifferentialChangesetRenderer { 5 + 6 + protected function renderChangeTypeHeader($force) { 7 + $changeset = $this->getChangeset(); 8 + 9 + $old = nonempty($changeset->getOldFile(), '-'); 10 + $away = nonempty(implode(', ', $changeset->getAwayPaths()), '-'); 11 + $ctype = $changeset->getChangeType(); 12 + $ftype = $changeset->getFileType(); 13 + $force = ($force ? '(forced)' : '(unforced)'); 14 + 15 + return "CTYPE {$ctype} {$ftype} {$force}\n". 16 + "{$old}\n". 17 + "{$away}\n"; 18 + } 19 + 20 + protected function renderShield($message, $force = 'default') { 21 + return "SHIELD ({$force}) {$message}\n"; 22 + } 23 + 24 + protected function renderPropertyChangeHeader() { 25 + $changeset = $this->getChangeset(); 26 + $old = $changeset->getOldProperties(); 27 + $new = $changeset->getNewProperties(); 28 + 29 + if (!$old && !$new) { 30 + return null; 31 + } 32 + 33 + $props = ''; 34 + foreach ($old as $key => $value) { 35 + $props .= "P - {$key} {$value}~\n"; 36 + } 37 + foreach ($new as $key => $value) { 38 + $props .= "P + {$key} {$value}~\n"; 39 + } 40 + 41 + return "PROPERTIES\n".$props; 42 + } 43 + 44 + public function renderTextChange( 45 + $range_start, 46 + $range_len, 47 + $rows) { 48 + 49 + $out = array(); 50 + 51 + $primitives = $this->buildPrimitives($range_start, $range_len); 52 + foreach ($primitives as $p) { 53 + $type = $p['type']; 54 + switch ($type) { 55 + case 'old': 56 + case 'new': 57 + $num = nonempty($p['line'], '-'); 58 + $render = $p['render']; 59 + $htype = nonempty($p['htype'], '.'); 60 + 61 + // TODO: This should probably happen earlier, whenever we deal with 62 + // \r and \t normalization? 63 + $render = rtrim($render, "\r\n"); 64 + $t = ($type == 'old') ? 'O' : 'N'; 65 + 66 + $out[] = "{$t} {$num} {$htype} {$render}~"; 67 + break; 68 + default: 69 + $out[] = $type; 70 + break; 71 + } 72 + } 73 + 74 + $out = implode("\n", $out)."\n"; 75 + return $out; 76 + } 77 + 78 + 79 + public function renderFileChange($old_file = null, 80 + $new_file = null, 81 + $id = 0, 82 + $vs = 0) { 83 + throw new Exception("Not implemented!"); 84 + } 85 + 86 + }
+5 -37
src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
··· 1 1 <?php 2 2 3 3 final class DifferentialChangesetTwoUpRenderer 4 - extends DifferentialChangesetRenderer { 5 - 6 - public function renderChangesetTable($contents) { 7 - $changeset = $this->getChangeset(); 8 - $props = $this->renderPropertyChangeHeader($changeset); 9 - $table = null; 10 - if ($contents) { 11 - $table = javelin_render_tag( 12 - 'table', 13 - array( 14 - 'class' => 'differential-diff remarkup-code PhabricatorMonospaced', 15 - 'sigil' => 'differential-diff', 16 - ), 17 - $contents); 18 - } 19 - 20 - if (!$table && !$props) { 21 - $notice = $this->renderChangeTypeHeader($changeset, true); 22 - } else { 23 - $notice = $this->renderChangeTypeHeader($changeset, false); 24 - } 4 + extends DifferentialChangesetHTMLRenderer { 25 5 26 - $result = implode( 27 - "\n", 28 - array( 29 - $notice, 30 - $props, 31 - $table, 32 - )); 33 - 34 - // TODO: Let the user customize their tab width / display style. 35 - $result = str_replace("\t", ' ', $result); 36 - 37 - // TODO: We should possibly post-process "\r" as well. 38 - 39 - return $result; 6 + public function isOneUpRenderer() { 7 + return false; 40 8 } 41 9 42 10 public function renderTextChange( ··· 360 328 } 361 329 } 362 330 363 - return implode('', $html); 331 + return $this->wrapChangeInTable(implode('', $html)); 364 332 } 365 333 366 334 public function renderFileChange($old_file = null, ··· 450 418 implode('', $html_old). 451 419 implode('', $html_new)); 452 420 453 - return $output; 421 + return $this->wrapChangeInTable($output); 454 422 } 455 423 456 424 }
+10
src/applications/differential/render/DifferentialChangesetTwoUpTestRenderer.php
··· 1 + <?php 2 + 3 + final class DifferentialChangesetTwoUpTestRenderer 4 + extends DifferentialChangesetTestRenderer { 5 + 6 + public function isOneUpRenderer() { 7 + return false; 8 + } 9 + 10 + }