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

Correct various minor diff copy behaviors

Summary:
Ref T12822. Fixes a few things:

- Firefox selection of weird ranges with an inline between the start and end of the range now works correctly.
- "Show More Context" rows now render, highlight, and select properly.
- Prepares for nodes to have copy-text which is different from display-text.
- Don't do anything too fancy in 1-up/unified mode. We don't copy line numbers after the `content: attr(data-n)` change, but that's as far as we go, because trying to do more than that is kind of weird and not terribly intuitive.

Test Plan:
- Selected and copied weird ranges in Firefox.
- Kept an eye on "Show More Context" rows across select and copy operations.
- Generally poked around in Safari/Firefox/Chrome.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T12822

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

+168 -70
+20 -20
resources/celerity/map.php
··· 10 10 'conpherence.pkg.css' => '3c8a0668', 11 11 'conpherence.pkg.js' => '020aebcf', 12 12 'core.pkg.css' => '261ee8cf', 13 - 'core.pkg.js' => '5ba0b6d7', 14 - 'differential.pkg.css' => 'd1b29c9c', 15 - 'differential.pkg.js' => '0e2b0e2c', 13 + 'core.pkg.js' => 'e368deda', 14 + 'differential.pkg.css' => '249b542d', 15 + 'differential.pkg.js' => '53f8d00c', 16 16 'diffusion.pkg.css' => '42c75c37', 17 17 'diffusion.pkg.js' => '91192d85', 18 18 'maniphest.pkg.css' => '35995d6d', ··· 61 61 'rsrc/css/application/dashboard/dashboard.css' => '4267d6c6', 62 62 'rsrc/css/application/diff/inline-comment-summary.css' => '81eb368d', 63 63 'rsrc/css/application/differential/add-comment.css' => '7e5900d9', 64 - 'rsrc/css/application/differential/changeset-view.css' => 'e2b81e85', 64 + 'rsrc/css/application/differential/changeset-view.css' => 'cc3fd795', 65 65 'rsrc/css/application/differential/core.css' => 'bdb93065', 66 66 'rsrc/css/application/differential/phui-inline-comment.css' => '48acce5b', 67 67 'rsrc/css/application/differential/revision-comment.css' => '7dbc8d1d', ··· 375 375 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9', 376 376 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '9b1cbd76', 377 377 'rsrc/js/application/diff/DiffChangeset.js' => 'd0a85a85', 378 - 'rsrc/js/application/diff/DiffChangesetList.js' => '26fb79ba', 378 + 'rsrc/js/application/diff/DiffChangesetList.js' => '04023d82', 379 379 'rsrc/js/application/diff/DiffInline.js' => 'a4a14a94', 380 380 'rsrc/js/application/diff/behavior-preview-link.js' => 'f51e9c17', 381 381 'rsrc/js/application/differential/behavior-diff-radios.js' => '925fe8cd', ··· 473 473 'rsrc/js/core/behavior-linked-container.js' => '74446546', 474 474 'rsrc/js/core/behavior-more.js' => '506aa3f4', 475 475 'rsrc/js/core/behavior-object-selector.js' => 'a4af0b4a', 476 - 'rsrc/js/core/behavior-oncopy.js' => 'f20d66c1', 476 + 'rsrc/js/core/behavior-oncopy.js' => 'de59bf15', 477 477 'rsrc/js/core/behavior-phabricator-nav.js' => 'f166c949', 478 478 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '2f80333f', 479 479 'rsrc/js/core/behavior-read-only-warning.js' => 'b9109f8f', ··· 541 541 'conpherence-thread-manager' => 'aec8e38c', 542 542 'conpherence-transaction-css' => '3a3f5e7e', 543 543 'd3' => 'd67475f5', 544 - 'differential-changeset-view-css' => 'e2b81e85', 544 + 'differential-changeset-view-css' => 'cc3fd795', 545 545 'differential-core-view-css' => 'bdb93065', 546 546 'differential-revision-add-comment-css' => '7e5900d9', 547 547 'differential-revision-comment-css' => '7dbc8d1d', ··· 636 636 'javelin-behavior-phabricator-nav' => 'f166c949', 637 637 'javelin-behavior-phabricator-notification-example' => '29819b75', 638 638 'javelin-behavior-phabricator-object-selector' => 'a4af0b4a', 639 - 'javelin-behavior-phabricator-oncopy' => 'f20d66c1', 639 + 'javelin-behavior-phabricator-oncopy' => 'de59bf15', 640 640 'javelin-behavior-phabricator-remarkup-assist' => '2f80333f', 641 641 'javelin-behavior-phabricator-reveal-content' => 'b105a3a6', 642 642 'javelin-behavior-phabricator-search-typeahead' => '1cb7d027', ··· 754 754 'phabricator-darkmessage' => '26cd4b73', 755 755 'phabricator-dashboard-css' => '4267d6c6', 756 756 'phabricator-diff-changeset' => 'd0a85a85', 757 - 'phabricator-diff-changeset-list' => '26fb79ba', 757 + 'phabricator-diff-changeset-list' => '04023d82', 758 758 'phabricator-diff-inline' => 'a4a14a94', 759 759 'phabricator-drag-and-drop-file-upload' => '4370900d', 760 760 'phabricator-draggable-list' => '3c6bd549', ··· 907 907 'javelin-uri', 908 908 'javelin-util', 909 909 ), 910 + '04023d82' => array( 911 + 'javelin-install', 912 + 'phuix-button-view', 913 + ), 910 914 '04f8a1e3' => array( 911 915 'javelin-behavior', 912 916 'javelin-stratcom', ··· 1086 1090 'javelin-dom', 1087 1091 'javelin-json', 1088 1092 'phabricator-draggable-list', 1089 - ), 1090 - '26fb79ba' => array( 1091 - 'javelin-install', 1092 - 'phuix-button-view', 1093 1093 ), 1094 1094 '27daef73' => array( 1095 1095 'multirow-row-manager', ··· 1961 1961 'javelin-util', 1962 1962 'phabricator-keyboard-shortcut-manager', 1963 1963 ), 1964 + 'cc3fd795' => array( 1965 + 'phui-inline-comment-view-css', 1966 + ), 1964 1967 'cf32921f' => array( 1965 1968 'javelin-behavior', 1966 1969 'javelin-dom', ··· 2006 2009 'javelin-behavior', 2007 2010 'javelin-uri', 2008 2011 'phabricator-notification', 2012 + ), 2013 + 'de59bf15' => array( 2014 + 'javelin-behavior', 2015 + 'javelin-dom', 2009 2016 ), 2010 2017 'dfa1d313' => array( 2011 2018 'javelin-behavior', ··· 2032 2039 'javelin-dom', 2033 2040 'javelin-stratcom', 2034 2041 ), 2035 - 'e2b81e85' => array( 2036 - 'phui-inline-comment-view-css', 2037 - ), 2038 2042 'e562708c' => array( 2039 2043 'javelin-install', 2040 2044 ), ··· 2085 2089 'javelin-vector', 2086 2090 'javelin-request', 2087 2091 'javelin-util', 2088 - ), 2089 - 'f20d66c1' => array( 2090 - 'javelin-behavior', 2091 - 'javelin-dom', 2092 2092 ), 2093 2093 'f340a484' => array( 2094 2094 'javelin-install',
+11 -1
src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
··· 432 432 $classes[] = 'PhabricatorMonospaced'; 433 433 $classes[] = $this->getRendererTableClass(); 434 434 435 + $sigils = array(); 436 + $sigils[] = 'differential-diff'; 437 + foreach ($this->getTableSigils() as $sigil) { 438 + $sigils[] = $sigil; 439 + } 440 + 435 441 return javelin_tag( 436 442 'table', 437 443 array( 438 444 'class' => implode(' ', $classes), 439 - 'sigil' => 'differential-diff intercept-copy', 445 + 'sigil' => implode(' ', $sigils), 440 446 ), 441 447 array( 442 448 $this->renderColgroup(), 443 449 $content, 444 450 )); 451 + } 452 + 453 + protected function getTableSigils() { 454 + return array(); 445 455 } 446 456 447 457 protected function buildInlineComment(
+15 -5
src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
··· 117 117 phutil_tag( 118 118 'td', 119 119 array( 120 - 'colspan' => 2, 120 + 'class' => 'show-context-line n left-context', 121 + )), 122 + phutil_tag( 123 + 'td', 124 + array( 121 125 'class' => 'show-more', 122 126 ), 123 127 $contents), 124 128 phutil_tag( 125 - 'th', 129 + 'td', 126 130 array( 127 - 'class' => 'show-context-line', 128 - ), 129 - $context_line ? (int)$context_line : null), 131 + 'class' => 'show-context-line n', 132 + 'data-n' => $context_line, 133 + )), 130 134 phutil_tag( 131 135 'td', 132 136 array( ··· 446 450 } 447 451 448 452 return $this->newOffsetMap; 453 + } 454 + 455 + protected function getTableSigils() { 456 + return array( 457 + 'intercept-copy', 458 + ); 449 459 } 450 460 451 461 }
+25 -6
webroot/rsrc/css/application/differential/changeset-view.css
··· 87 87 color: {$darkgreytext}; 88 88 } 89 89 90 - .differential-changeset-immutable .differential-diff th { 90 + .differential-changeset-immutable .differential-diff td { 91 91 cursor: auto; 92 92 } 93 93 ··· 182 182 content: attr(data-n); 183 183 } 184 184 185 + .differential-diff td.show-context-line.n { 186 + cursor: auto; 187 + } 188 + 185 189 .differential-diff td.cov { 186 190 padding: 0; 187 191 } ··· 222 226 } 223 227 224 228 .differential-diff td.show-more, 225 - .differential-diff th.show-context-line, 229 + .differential-diff td.show-context-line, 226 230 .differential-diff td.show-context, 227 231 .differential-diff td.differential-shield { 228 232 background: {$lightbluebackground}; ··· 232 236 } 233 237 234 238 .device .differential-diff td.show-more, 235 - .device .differential-diff th.show-context-line, 239 + .device .differential-diff td.show-context-line, 236 240 .device .differential-diff td.show-context, 237 241 .device .differential-diff td.differential-shield { 238 242 padding: 6px 0; ··· 250 254 color: {$bluetext}; 251 255 } 252 256 253 - .differential-diff th.show-context-line { 257 + .differential-diff td.show-context-line { 254 258 padding-right: 6px; 259 + } 260 + 261 + .differential-diff td.show-context-line.left-context { 262 + border-right: none; 255 263 } 256 264 257 265 .differential-diff td.show-context { ··· 431 439 432 440 .differential-diff.copy-l > tbody > tr > td, 433 441 .differential-diff.copy-r > tbody > tr > td { 434 - -moz-user-select: -moz-none; 435 - -khtml-user-select: none; 442 + -moz-user-select: none; 436 443 -ms-user-select: none; 437 444 -webkit-user-select: none; 438 445 user-select: none; ··· 444 451 } 445 452 446 453 .differential-diff.copy-l > tbody > tr > td:nth-child(2) { 454 + -moz-user-select: auto; 455 + -ms-user-select: auto; 447 456 -webkit-user-select: auto; 448 457 user-select: auto; 449 458 opacity: 1; 450 459 } 451 460 461 + .differential-diff.copy-l > tbody > tr > td.show-more:nth-child(2) { 462 + -moz-user-select: none; 463 + -ms-user-select: none; 464 + -webkit-user-select: none; 465 + user-select: none; 466 + opacity: 0.25; 467 + } 468 + 452 469 .differential-diff.copy-r > tbody > tr > td:nth-child(5) { 470 + -moz-user-select: auto; 471 + -ms-user-select: auto; 453 472 -webkit-user-select: auto; 454 473 user-select: auto; 455 474 opacity: 1;
+19 -3
webroot/rsrc/js/application/diff/DiffChangesetList.js
··· 1246 1246 return changeset.getInlineForRow(inline_row); 1247 1247 }, 1248 1248 1249 - getLineNumberFromHeader: function(th) { 1250 - return parseInt(th.getAttribute('data-n')); 1249 + getLineNumberFromHeader: function(node) { 1250 + var n = parseInt(node.getAttribute('data-n')); 1251 + 1252 + if (!n) { 1253 + return null; 1254 + } 1255 + 1256 + // If this is a line number that's part of a row showing more context, 1257 + // we don't want to let users leave inlines here. 1258 + 1259 + try { 1260 + JX.DOM.findAbove(node, 'tr', 'context-target'); 1261 + return null; 1262 + } catch (ex) { 1263 + // Ignore. 1264 + } 1265 + 1266 + return n; 1251 1267 }, 1252 1268 1253 1269 getDisplaySideFromHeader: function(th) { ··· 1295 1311 }, 1296 1312 1297 1313 _updateRange: function(target, is_out) { 1298 - // Don't update the range if this "<th />" doesn't correspond to a line 1314 + // Don't update the range if this target doesn't correspond to a line 1299 1315 // number. For instance, this may be a dead line number, like the empty 1300 1316 // line numbers on the left hand side of a newly added file. 1301 1317 var number = this.getLineNumberFromHeader(target);
+78 -35
webroot/rsrc/js/core/behavior-oncopy.js
··· 186 186 return; 187 187 } 188 188 189 - var text_nodes = []; 189 + var text = []; 190 190 for (var ii = 0; ii < ranges.length; ii++) { 191 191 var range = ranges[ii]; 192 192 193 193 var fragment = range.cloneContents(); 194 - if (!fragment.children.length) { 194 + if (!fragment.childNodes.length) { 195 195 continue; 196 196 } 197 197 ··· 217 217 218 218 for (var jj = 0; jj < fragment.childNodes.length; jj++) { 219 219 var node = fragment.childNodes[jj]; 220 - if (JX.DOM.isType(node, 'tr')) { 221 - // This is an inline comment row, so we never want to copy any 222 - // content inside of it. 223 - if (JX.Stratcom.hasSigil(node, 'inline-row')) { 224 - continue; 225 - } 220 + text.push(extract_text(node)); 221 + } 222 + } 223 + 224 + text = flatten_list(text); 225 + text = text.join(''); 226 + 227 + var rawEvent = e.getRawEvent(); 228 + var data; 229 + if ('clipboardData' in rawEvent) { 230 + data = rawEvent.clipboardData; 231 + } else { 232 + data = window.clipboardData; 233 + } 234 + data.setData('Text', text); 235 + 236 + e.prevent(); 237 + } 238 + 239 + function extract_text(node) { 240 + var ii; 241 + var text = []; 242 + 243 + if (JX.DOM.isType(node, 'tr')) { 244 + // This is an inline comment row, so we never want to copy any 245 + // content inside of it. 246 + if (JX.Stratcom.hasSigil(node, 'inline-row')) { 247 + return null; 248 + } 226 249 227 - // Assume anything else is a source code row. Keep only "<td>" cells 228 - // with the correct mode. 229 - for (var kk = 0; kk < node.childNodes.length; kk++) { 230 - var child = node.childNodes[kk]; 250 + // This is a "Show More Context" row, so we never want to copy any 251 + // of the content inside. 252 + if (JX.Stratcom.hasSigil(node, 'context-target')) { 253 + return null; 254 + } 231 255 232 - var node_mode = child.getAttribute('data-copy-mode'); 233 - if (node_mode === copy_mode) { 234 - text_nodes.push(child); 235 - } 236 - } 237 - } else { 238 - // For anything else, assume this is a text fragment or part of 239 - // a table cell or something and should be included in the selection 240 - // range. 241 - text_nodes.push(node); 242 - } 256 + // Assume anything else is a source code row. Keep only "<td>" cells 257 + // with the correct mode. 258 + for (ii = 0; ii < node.childNodes.length; ii++) { 259 + text.push(extract_text(node.childNodes[ii])); 243 260 } 244 261 245 - var text = []; 246 - for (ii = 0; ii < text_nodes.length; ii++) { 247 - text.push(text_nodes[ii].textContent); 262 + return text; 263 + } 264 + 265 + if (JX.DOM.isType(node, 'td')) { 266 + var node_mode = node.getAttribute('data-copy-mode'); 267 + if (node_mode !== copy_mode) { 268 + return; 248 269 } 249 - text = text.join(''); 250 270 251 - var rawEvent = e.getRawEvent(); 252 - var data; 253 - if ('clipboardData' in rawEvent) { 254 - data = rawEvent.clipboardData; 271 + // Otherwise, fall through and extract this node's text normally. 272 + } 273 + 274 + if (!node.childNodes || !node.childNodes.length) { 275 + return node.textContent; 276 + } 277 + 278 + for (ii = 0; ii < node.childNodes.length; ii++) { 279 + var child = node.childNodes[ii]; 280 + text.push(extract_text(child)); 281 + } 282 + 283 + return text; 284 + } 285 + 286 + function flatten_list(list) { 287 + var stack = [list]; 288 + var result = []; 289 + while (stack.length) { 290 + var next = stack.pop(); 291 + if (JX.isArray(next)) { 292 + for (var ii = 0; ii < next.length; ii++) { 293 + stack.push(next[ii]); 294 + } 295 + } else if (next === null) { 296 + continue; 297 + } else if (next === undefined) { 298 + continue; 255 299 } else { 256 - data = window.clipboardData; 300 + result.push(next); 257 301 } 258 - data.setData('Text', text); 302 + } 259 303 260 - e.prevent(); 261 - } 304 + return result.reverse(); 262 305 } 263 306 264 307 JX.enableDispatch(document.body, 'copy');