From d5e61f52500787453c7637cc49207128cad5c5d7 Mon Sep 17 00:00:00 2001 From: Alan Huang Date: Mon, 11 Jun 2012 15:59:26 -0700 Subject: [PATCH] Let the viewer collapse/expand individual files in a diff. Summary: Added a dropdown menu button and the keyboard shortcut 'h' to the web diff view. These hide or show the annotated code display. Test Plan: Viewed an example diff that changed a large number of source files and played around with keyboard shortcuts. Everything seemed to work as expected. Reviewers: nh, epriestley Reviewed By: epriestley CC: aran, epriestley, Korvin Differential Revision: https://secure.phabricator.com/D2714 --- .../view/DifferentialChangesetListView.php | 2 + .../differential/behavior-comment-jump.js | 3 + .../differential/behavior-dropdown-menus.js | 15 +++++ .../differential/behavior-keyboard-nav.js | 34 +++++++++- .../differential/behavior-toggle-files.js | 62 +++++++++++++++++++ 5 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 webroot/rsrc/js/application/differential/behavior-toggle-files.js diff --git a/src/applications/differential/view/DifferentialChangesetListView.php b/src/applications/differential/view/DifferentialChangesetListView.php index 3a43f095c6..a819390bfb 100644 --- a/src/applications/differential/view/DifferentialChangesetListView.php +++ b/src/applications/differential/view/DifferentialChangesetListView.php @@ -112,6 +112,8 @@ final class DifferentialChangesetListView extends AphrontView { Javelin::initBehavior('buoyant', array()); + Javelin::initBehavior('differential-toggle-files', array()); + $output = array(); $mapping = array(); foreach ($changesets as $key => $changeset) { diff --git a/webroot/rsrc/js/application/differential/behavior-comment-jump.js b/webroot/rsrc/js/application/differential/behavior-comment-jump.js index 217c85b078..f4d5f91228 100644 --- a/webroot/rsrc/js/application/differential/behavior-comment-jump.js +++ b/webroot/rsrc/js/application/differential/behavior-comment-jump.js @@ -19,6 +19,9 @@ JX.behavior('differential-comment-jump', function(config) { break; } } + JX.Stratcom.invoke('differential-toggle-file-request', null, { + element: jumpto, + }); JX.DOM.scrollTo(jumpto); e.kill(); }); diff --git a/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js b/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js index 280dfec42a..78498d0ae4 100644 --- a/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js +++ b/webroot/rsrc/js/application/differential/behavior-dropdown-menus.js @@ -52,6 +52,13 @@ JX.behavior('differential-dropdown-menus', function(config) { var menu = new JX.PhabricatorDropdownMenu(buttons[ii]) .addItem(reveal_item); + var visible_item = new JX.PhabricatorMenuItem('', function () { + JX.Stratcom.invoke('differential-toggle-file', null, { + diff: JX.DOM.scry(JX.$(data.containerID), 'table', 'differential-diff'), + }); + }); + menu.addItem(visible_item); + if (diffusion_item) { menu.addItem(diffusion_item); } @@ -93,6 +100,14 @@ JX.behavior('differential-dropdown-menus', function(config) { reveal_item.setDisabled(true); reveal_item.setName('Entire File Shown'); } + + var diff = JX.DOM.find(JX.$(data.containerID), + 'table', 'differential-diff'); + if (JX.Stratcom.getData(diff).hidden) { + visible_item.setName('Expand File'); + } else { + visible_item.setName('Collapse File'); + } }); } diff --git a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js b/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js index a36305a478..2377445369 100644 --- a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js +++ b/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js @@ -14,6 +14,8 @@ JX.behavior('differential-keyboard-navigation', function(config) { var selection_begin = null; var selection_end = null; + var refreshFocus = function() {}; + function init() { if (changesets) { return; @@ -31,6 +33,12 @@ JX.behavior('differential-keyboard-navigation', function(config) { var type; var ii; + // Don't show code blocks inside a collapsed file. + var diff = JX.DOM.scry(changesets[cursor], 'table', 'differential-diff'); + if (diff.length == 1 && JX.Stratcom.getData(diff[0]).hidden) { + return blocks; + } + function push() { if (start) { blocks.push([start, rows[ii - 1]]); @@ -122,7 +130,9 @@ JX.behavior('differential-keyboard-navigation', function(config) { selection_end = blocks[focus][1]; manager.scrollTo(selection_begin); - manager.focusOn(selection_begin, selection_end); + (refreshFocus = function() { + manager.focusOn(selection_begin, selection_end); + })(); return; } else { @@ -149,6 +159,16 @@ JX.behavior('differential-keyboard-navigation', function(config) { function() { changesets = null; }); + // Same thing when a file is hidden or shown; don't want to highlight + // invisible code. + JX.Stratcom.listen( + 'differential-toggle-file-toggled', + null, + function() { + changesets = null; + init(); + refreshFocus(); + }); var haunt_mode = 0; function haunt() { @@ -205,6 +225,18 @@ JX.behavior('differential-keyboard-navigation', function(config) { .register(); + new JX.KeyboardShortcut('h', 'Collapse or expand the file display.') + .setHandler(function(manager) { + if (!changesets || !changesets[cursor]) { + return; + } + JX.Stratcom.invoke('differential-toggle-file', null, { + diff: JX.DOM.scry(changesets[cursor], 'table', 'differential-diff'), + }); + }) + .register(); + + function inline_op(node, op) { if (!JX.DOM.scry(node, 'a', 'differential-inline-' + op)) { // No link for this operation, e.g. editing a comment you can't edit. diff --git a/webroot/rsrc/js/application/differential/behavior-toggle-files.js b/webroot/rsrc/js/application/differential/behavior-toggle-files.js new file mode 100644 index 0000000000..4d623b35d4 --- /dev/null +++ b/webroot/rsrc/js/application/differential/behavior-toggle-files.js @@ -0,0 +1,62 @@ +/** + * @provides javelin-behavior-differential-toggle-files + * @requires javelin-behavior + * javelin-dom + * javelin-stratcom + * phabricator-keyboard-shortcut + */ + +JX.behavior('differential-toggle-files', function(config) { + + JX.Stratcom.listen( + 'differential-toggle-file', + null, + function(e) { + if (e.getData().diff.length != 1) { + return; + } + var diff = e.getData().diff[0], + data = JX.Stratcom.getData(diff); + if(data.hidden) { + data.hidden = false; + JX.DOM.show(diff); + } else { + data.hidden = true; + JX.DOM.hide(diff); + } + JX.Stratcom.invoke('differential-toggle-file-toggled'); + }); + + JX.Stratcom.listen( + 'differential-toggle-file-request', + null, + function(e) { + var elt = e.getData().element; + while (elt !== document.body) { + if (JX.Stratcom.hasSigil(elt, 'differential-diff') && + JX.Stratcom.getData(elt).hidden) { + JX.Stratcom.invoke('differential-toggle-file', null, { + diff: [ elt ], + }); + return; + } + elt = elt.parentNode; + } + }); + + JX.Stratcom.listen( + 'hashchange', + null, + function(e) { + var id = window.location.hash; + if (!id.match(/^#/)) { + return; + } + JX.Stratcom.invoke('differential-toggle-file-request', null, { + element: JX.$(id.substr(1)), + }); + // This event is processed after the hash has changed, so it doesn't + // automatically jump there like we want. + JX.DOM.scrollTo(JX.$(id.substr(1))); + }); +});