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

Perform basic block interdiffs when diffing abstract blocks, and interdiff markdown in Jupyter notebooks

Summary:
Depends on D20844. Ref T13425. When we line up two blocks and they can be interdiffed (generally: they both have the same type of content), let the Engine interdiff them.

Then, make the Jupyter engine interdiff markdown.

Test Plan: {F6898583}

Maniphest Tasks: T13425

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

+324 -75
+2
src/__phutil_library_map__.php
··· 3139 3139 'PhabricatorDivinerApplication' => 'applications/diviner/application/PhabricatorDivinerApplication.php', 3140 3140 'PhabricatorDocumentEngine' => 'applications/files/document/PhabricatorDocumentEngine.php', 3141 3141 'PhabricatorDocumentEngineBlock' => 'applications/files/diff/PhabricatorDocumentEngineBlock.php', 3142 + 'PhabricatorDocumentEngineBlockDiff' => 'applications/files/diff/PhabricatorDocumentEngineBlockDiff.php', 3142 3143 'PhabricatorDocumentEngineBlocks' => 'applications/files/diff/PhabricatorDocumentEngineBlocks.php', 3143 3144 'PhabricatorDocumentRef' => 'applications/files/document/PhabricatorDocumentRef.php', 3144 3145 'PhabricatorDocumentRenderingEngine' => 'applications/files/document/render/PhabricatorDocumentRenderingEngine.php', ··· 9478 9479 'PhabricatorDivinerApplication' => 'PhabricatorApplication', 9479 9480 'PhabricatorDocumentEngine' => 'Phobject', 9480 9481 'PhabricatorDocumentEngineBlock' => 'Phobject', 9482 + 'PhabricatorDocumentEngineBlockDiff' => 'Phobject', 9481 9483 'PhabricatorDocumentEngineBlocks' => 'Phobject', 9482 9484 'PhabricatorDocumentRef' => 'Phobject', 9483 9485 'PhabricatorDocumentRenderingEngine' => 'Phobject',
+15 -4
src/applications/differential/parser/DifferentialChangesetParser.php
··· 868 868 ->setHighlightingDisabled($this->highlightingDisabled) 869 869 ->setDepthOnlyLines($this->getDepthOnlyLines()); 870 870 871 - $engine_blocks = $this->newDocumentEngineBlocks(); 871 + list($engine, $old_ref, $new_ref) = $this->newDocumentEngine(); 872 + if ($engine) { 873 + $engine_blocks = $engine->newEngineBlocks( 874 + $old_ref, 875 + $new_ref); 876 + } else { 877 + $engine_blocks = null; 878 + } 879 + 872 880 $has_document_engine = ($engine_blocks !== null); 873 881 874 882 $shield = null; ··· 1043 1051 $vs = $id; 1044 1052 } 1045 1053 1046 - $renderer->setDocumentEngineBlocks($engine_blocks); 1054 + $renderer 1055 + ->setDocumentEngine($engine) 1056 + ->setDocumentEngineBlocks($engine_blocks); 1047 1057 1048 1058 return $renderer->renderDocumentEngineBlocks( 1049 1059 $engine_blocks, ··· 1657 1667 return $prefix.$line; 1658 1668 } 1659 1669 1660 - private function newDocumentEngineBlocks() { 1670 + private function newDocumentEngine() { 1661 1671 $changeset = $this->changeset; 1662 1672 $viewer = $this->getViewer(); 1663 1673 ··· 1728 1738 } 1729 1739 1730 1740 if ($document_engine) { 1731 - return $document_engine->newEngineBlocks( 1741 + return array( 1742 + $document_engine, 1732 1743 $old_ref, 1733 1744 $new_ref); 1734 1745 }
+1 -1
src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
··· 242 242 'type' => 'old-file', 243 243 'htype' => '', 244 244 'line' => $block->getBlockKey(), 245 - 'render' => $block->newContentView(), 245 + 'render' => $block->getContent(), 246 246 ); 247 247 } 248 248
+11
src/applications/differential/render/DifferentialChangesetRenderer.php
··· 35 35 private $highlightingDisabled; 36 36 private $scopeEngine = false; 37 37 private $depthOnlyLines; 38 + 39 + private $documentEngine; 38 40 private $documentEngineBlocks; 39 41 40 42 private $oldFile = false; ··· 238 240 } 239 241 protected function getOldChangesetID() { 240 242 return $this->oldChangesetID; 243 + } 244 + 245 + public function setDocumentEngine(PhabricatorDocumentEngine $engine) { 246 + $this->documentEngine = $engine; 247 + return $this; 248 + } 249 + 250 + public function getDocumentEngine() { 251 + return $this->documentEngine; 241 252 } 242 253 243 254 public function setDocumentEngineBlocks(
+45 -31
src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
··· 369 369 $old_changeset_key, 370 370 $new_changeset_key) { 371 371 372 + $engine = $this->getDocumentEngine(); 373 + 374 + $old_ref = null; 375 + $new_ref = null; 376 + $refs = $block_list->getDocumentRefs(); 377 + if ($refs) { 378 + list($old_ref, $new_ref) = $refs; 379 + } 380 + 372 381 $old_comments = $this->getOldComments(); 373 382 $new_comments = $this->getNewComments(); 374 383 ··· 392 401 393 402 if ($old) { 394 403 $old_key = $old->getBlockKey(); 395 - 396 - $old_classes = $old->getClasses(); 397 - 398 - if ($old->getDifferenceType() === '-') { 399 - $old_classes[] = 'old'; 400 - $old_classes[] = 'old-full'; 401 - } 402 - 403 - $old_classes[] = 'diff-flush'; 404 - 405 - $old_classes = implode(' ', $old_classes); 406 - 407 404 $is_visible = $old->getIsVisible(); 408 405 } else { 409 406 $old_key = null; 410 - $old_classes = null; 411 407 } 412 408 413 409 if ($new) { 414 410 $new_key = $new->getBlockKey(); 415 - $new_classes = $new->getClasses(); 416 - 417 - if ($new->getDifferenceType() === '+') { 418 - $new_classes[] = 'new'; 419 - $new_classes[] = 'new-full'; 420 - } 421 - 422 - $new_classes[] = 'diff-flush'; 423 - 424 - $new_classes = implode(' ', $new_classes); 425 - 426 411 $is_visible = $new->getIsVisible(); 427 412 } else { 428 413 $new_key = null; 429 - $new_classes = null; 430 414 } 431 415 432 416 if (!$is_visible) { ··· 454 438 } 455 439 456 440 if ($is_rem && $is_add) { 457 - list($old_content, $new_content) = array( 458 - $old->newContentView(), 459 - $new->newContentView(), 460 - ); 441 + $block_diff = $engine->newBlockDiffViews( 442 + $old_ref, 443 + $old, 444 + $new_ref, 445 + $new); 446 + 447 + $old_content = $block_diff->getOldContent(); 448 + $new_content = $block_diff->getNewContent(); 449 + 450 + $old_classes = $block_diff->getOldClasses(); 451 + $new_classes = $block_diff->getNewClasses(); 461 452 } else { 453 + $old_classes = array(); 454 + $new_classes = array(); 455 + 462 456 if ($old) { 463 - $old_content = $old->newContentView(); 457 + $old_content = $engine->newBlockContentView( 458 + $old_ref, 459 + $old); 460 + 461 + if ($is_rem) { 462 + $old_classes[] = 'old'; 463 + $old_classes[] = 'old-full'; 464 + } 464 465 } else { 465 466 $old_content = null; 466 467 } 467 468 468 469 if ($new) { 469 - $new_content = $new->newContentView(); 470 + $new_content = $engine->newBlockContentView( 471 + $new_ref, 472 + $new); 473 + 474 + if ($is_add) { 475 + $new_classes[] = 'new'; 476 + $new_classes[] = 'new-full'; 477 + } 470 478 } else { 471 479 $new_content = null; 472 480 } 473 481 } 482 + 483 + $old_classes[] = 'diff-flush'; 484 + $old_classes = implode(' ', $old_classes); 485 + 486 + $new_classes[] = 'diff-flush'; 487 + $new_classes = implode(' ', $new_classes); 474 488 475 489 $old_inline_rows = array(); 476 490 if ($old_key !== null) {
-14
src/applications/files/diff/PhabricatorDocumentEngineBlock.php
··· 5 5 6 6 private $blockKey; 7 7 private $content; 8 - private $classes = array(); 9 8 private $differenceHash; 10 9 private $differenceType; 11 10 private $isVisible; ··· 19 18 return $this->content; 20 19 } 21 20 22 - public function newContentView() { 23 - return $this->getContent(); 24 - } 25 - 26 21 public function setBlockKey($block_key) { 27 22 $this->blockKey = $block_key; 28 23 return $this; ··· 30 25 31 26 public function getBlockKey() { 32 27 return $this->blockKey; 33 - } 34 - 35 - public function addClass($class) { 36 - $this->classes[] = $class; 37 - return $this; 38 - } 39 - 40 - public function getClasses() { 41 - return $this->classes; 42 28 } 43 29 44 30 public function setDifferenceHash($difference_hash) {
+47
src/applications/files/diff/PhabricatorDocumentEngineBlockDiff.php
··· 1 + <?php 2 + 3 + final class PhabricatorDocumentEngineBlockDiff 4 + extends Phobject { 5 + 6 + private $oldContent; 7 + private $newContent; 8 + private $oldClasses = array(); 9 + private $newClasses = array(); 10 + 11 + public function setOldContent($old_content) { 12 + $this->oldContent = $old_content; 13 + return $this; 14 + } 15 + 16 + public function getOldContent() { 17 + return $this->oldContent; 18 + } 19 + 20 + public function setNewContent($new_content) { 21 + $this->newContent = $new_content; 22 + return $this; 23 + } 24 + 25 + public function getNewContent() { 26 + return $this->newContent; 27 + } 28 + 29 + public function addOldClass($class) { 30 + $this->oldClasses[] = $class; 31 + return $this; 32 + } 33 + 34 + public function getOldClasses() { 35 + return $this->oldClasses; 36 + } 37 + 38 + public function addNewClass($class) { 39 + $this->newClasses[] = $class; 40 + return $this; 41 + } 42 + 43 + public function getNewClasses() { 44 + return $this->newClasses; 45 + } 46 + 47 + }
+4
src/applications/files/diff/PhabricatorDocumentEngineBlocks.php
··· 26 26 return $this; 27 27 } 28 28 29 + public function getDocumentRefs() { 30 + return ipull($this->lists, 'ref'); 31 + } 32 + 29 33 public function newTwoUpLayout() { 30 34 $rows = array(); 31 35 $lists = $this->lists;
+24
src/applications/files/document/PhabricatorDocumentEngine.php
··· 37 37 return false; 38 38 } 39 39 40 + public function newBlockDiffViews( 41 + PhabricatorDocumentRef $uref, 42 + PhabricatorDocumentEngineBlock $ublock, 43 + PhabricatorDocumentRef $vref, 44 + PhabricatorDocumentEngineBlock $vblock) { 45 + 46 + $u_content = $this->newBlockContentView($uref, $ublock); 47 + $v_content = $this->newBlockContentView($vref, $vblock); 48 + 49 + return id(new PhabricatorDocumentEngineBlockDiff()) 50 + ->setOldContent($u_content) 51 + ->addOldClass('old') 52 + ->addOldClass('old-full') 53 + ->setNewContent($v_content) 54 + ->addNewClass('new') 55 + ->addNewClass('new-full'); 56 + } 57 + 58 + public function newBlockContentView( 59 + PhabricatorDocumentRef $ref, 60 + PhabricatorDocumentEngineBlock $block) { 61 + return $block->getContent(); 62 + } 63 + 40 64 public function newEngineBlocks( 41 65 PhabricatorDocumentRef $uref, 42 66 PhabricatorDocumentRef $vref) {
+17 -1
src/applications/files/document/PhabricatorImageDocumentEngine.php
··· 39 39 ->addBlockList($vref, $v_blocks); 40 40 } 41 41 42 + public function newBlockDiffViews( 43 + PhabricatorDocumentRef $uref, 44 + PhabricatorDocumentEngineBlock $ublock, 45 + PhabricatorDocumentRef $vref, 46 + PhabricatorDocumentEngineBlock $vblock) { 47 + 48 + $u_content = $this->newBlockContentView($uref, $ublock); 49 + $v_content = $this->newBlockContentView($vref, $vblock); 50 + 51 + return id(new PhabricatorDocumentEngineBlockDiff()) 52 + ->setOldContent($u_content) 53 + ->addOldClass('diff-image-cell') 54 + ->setNewContent($v_content) 55 + ->addNewClass('diff-image-cell'); 56 + } 57 + 58 + 42 59 private function newDiffBlocks(PhabricatorDocumentRef $ref) { 43 60 $blocks = array(); 44 61 ··· 59 76 60 77 $blocks[] = id(new PhabricatorDocumentEngineBlock()) 61 78 ->setBlockKey('1') 62 - ->addClass('diff-image-cell') 63 79 ->setDifferenceHash($hash) 64 80 ->setContent($image_view); 65 81
+158 -24
src/applications/files/document/PhabricatorJupyterDocumentEngine.php
··· 60 60 return $blocks; 61 61 } 62 62 63 - private function newDiffBlocks(PhabricatorDocumentRef $ref) { 63 + public function newBlockDiffViews( 64 + PhabricatorDocumentRef $uref, 65 + PhabricatorDocumentEngineBlock $ublock, 66 + PhabricatorDocumentRef $vref, 67 + PhabricatorDocumentEngineBlock $vblock) { 68 + 69 + $ucell = $ublock->getContent(); 70 + $vcell = $vblock->getContent(); 71 + 72 + $utype = idx($ucell, 'cell_type'); 73 + $vtype = idx($vcell, 'cell_type'); 74 + 75 + if ($utype === $vtype) { 76 + switch ($utype) { 77 + case 'markdown': 78 + $usource = idx($ucell, 'source'); 79 + $usource = implode('', $usource); 80 + 81 + $vsource = idx($vcell, 'source'); 82 + $vsource = implode('', $vsource); 83 + 84 + $diff = id(new PhutilProseDifferenceEngine()) 85 + ->getDiff($usource, $vsource); 86 + 87 + $u_content = $this->newProseDiffCell($diff, array('=', '-')); 88 + $v_content = $this->newProseDiffCell($diff, array('=', '+')); 89 + 90 + $u_content = $this->newJupyterCell(null, $u_content, null); 91 + $v_content = $this->newJupyterCell(null, $v_content, null); 92 + 93 + $u_content = $this->newCellContainer($u_content); 94 + $v_content = $this->newCellContainer($v_content); 95 + 96 + return id(new PhabricatorDocumentEngineBlockDiff()) 97 + ->setOldContent($u_content) 98 + ->addOldClass('old') 99 + ->setNewContent($v_content) 100 + ->addNewClass('new'); 101 + } 102 + } 103 + 104 + return parent::newBlockDiffViews($uref, $ublock, $vref, $vblock); 105 + } 106 + 107 + public function newBlockContentView( 108 + PhabricatorDocumentRef $ref, 109 + PhabricatorDocumentEngineBlock $block) { 110 + 64 111 $viewer = $this->getViewer(); 65 - $content = $ref->loadData(); 112 + $cell = $block->getContent(); 113 + 114 + $cell_content = $this->renderJupyterCell($viewer, $cell); 115 + 116 + return $this->newCellContainer($cell_content); 117 + } 118 + 119 + private function newCellContainer($cell_content) { 120 + $notebook_table = phutil_tag( 121 + 'table', 122 + array( 123 + 'class' => 'jupyter-notebook', 124 + ), 125 + $cell_content); 126 + 127 + $container = phutil_tag( 128 + 'div', 129 + array( 130 + 'class' => 'document-engine-jupyter document-engine-diff', 131 + ), 132 + $notebook_table); 66 133 67 - $cells = $this->newCells($content, true); 134 + return $container; 135 + } 68 136 69 - $idx = 1; 70 - $blocks = array(); 71 - foreach ($cells as $cell) { 72 - $cell_content = $this->renderJupyterCell($viewer, $cell); 137 + private function newProseDiffCell(PhutilProseDiff $diff, array $mask) { 138 + $mask = array_fuse($mask); 73 139 74 - $notebook_table = phutil_tag( 75 - 'table', 76 - array( 77 - 'class' => 'jupyter-notebook', 78 - ), 79 - $cell_content); 140 + $result = array(); 141 + foreach ($diff->getParts() as $part) { 142 + $type = $part['type']; 143 + $text = $part['text']; 80 144 81 - $container = phutil_tag( 145 + if (!isset($mask[$type])) { 146 + continue; 147 + } 148 + 149 + switch ($type) { 150 + case '-': 151 + $result[] = phutil_tag( 152 + 'span', 153 + array( 154 + 'class' => 'bright', 155 + ), 156 + $text); 157 + break; 158 + case '+': 159 + $result[] = phutil_tag( 160 + 'span', 161 + array( 162 + 'class' => 'bright', 163 + ), 164 + $text); 165 + break; 166 + case '=': 167 + $result[] = $text; 168 + break; 169 + } 170 + } 171 + 172 + return array( 173 + null, 174 + phutil_tag( 82 175 'div', 83 176 array( 84 - 'class' => 'document-engine-jupyter document-engine-diff', 177 + 'class' => 'jupyter-cell-markdown', 85 178 ), 86 - $notebook_table); 179 + $result), 180 + ); 181 + } 182 + 183 + private function newDiffBlocks(PhabricatorDocumentRef $ref) { 184 + $viewer = $this->getViewer(); 185 + $content = $ref->loadData(); 186 + 187 + $cells = $this->newCells($content, true); 87 188 189 + $idx = 1; 190 + $blocks = array(); 191 + foreach ($cells as $cell) { 88 192 // When the cell is a source code line, we can hash just the raw 89 193 // input rather than all the cell metadata. 90 194 ··· 92 196 case 'code/line': 93 197 $hash_input = $cell['raw']; 94 198 break; 199 + case 'markdown': 200 + $hash_input = implode('', $cell['source']); 201 + break; 95 202 default: 96 203 $hash_input = serialize($cell); 97 204 break; ··· 104 211 $blocks[] = id(new PhabricatorDocumentEngineBlock()) 105 212 ->setBlockKey($idx) 106 213 ->setDifferenceHash($hash) 107 - ->setContent($container); 214 + ->setContent($cell); 108 215 109 216 $idx++; 110 217 } ··· 204 311 foreach ($cells as $cell) { 205 312 $cell_type = idx($cell, 'cell_type'); 206 313 314 + if ($cell_type === 'markdown') { 315 + $source = $cell['source']; 316 + $source = implode('', $source); 317 + 318 + // Attempt to split contiguous blocks of markdown into smaller 319 + // pieces. 320 + 321 + $chunks = preg_split( 322 + '/\n\n+/', 323 + $source); 324 + 325 + foreach ($chunks as $chunk) { 326 + $result = $cell; 327 + $result['source'] = array($chunk); 328 + $results[] = $result; 329 + } 330 + 331 + continue; 332 + } 333 + 207 334 if ($cell_type !== 'code') { 208 335 $results[] = $cell; 209 336 continue; ··· 261 388 262 389 list($label, $content) = $this->renderJupyterCellContent($viewer, $cell); 263 390 391 + $classes = null; 392 + switch (idx($cell, 'cell_type')) { 393 + case 'code/line': 394 + $classes = 'jupyter-cell-flush'; 395 + break; 396 + } 397 + 398 + return $this->newJupyterCell( 399 + $label, 400 + $content, 401 + $classes); 402 + } 403 + 404 + private function newJupyterCell($label, $content, $classes) { 264 405 $label_cell = phutil_tag( 265 406 'td', 266 407 array( 267 408 'class' => 'jupyter-label', 268 409 ), 269 410 $label); 270 - 271 - $classes = null; 272 - switch (idx($cell, 'cell_type')) { 273 - case 'code/line': 274 - $classes = 'jupyter-cell-flush'; 275 - break; 276 - } 277 411 278 412 $content_cell = phutil_tag( 279 413 'td',