1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 05:50:55 +01:00

Move most Differetial keyboard shortcuts into DiffChangesetList

Summary: Ref T12616. This moves most keyboard shortcuts into DiffChangesetList. It breaks some shortcuts that I plan to restore later, noted in T12616 (toggle file, edit inline, reply to inline), since I think ripping them out now and rebuilding them in a little bit will make things much simpler.

Test Plan:
  - Used j, k, n, p, J, K shortcuts to navigate a revision.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12616

Differential Revision: https://secure.phabricator.com/D17859
This commit is contained in:
epriestley 2017-05-08 13:24:15 -07:00
parent 41379f39de
commit 588a66c04d
8 changed files with 273 additions and 296 deletions

View file

@ -13,7 +13,7 @@ return array(
'core.pkg.js' => '2ff7879f', 'core.pkg.js' => '2ff7879f',
'darkconsole.pkg.js' => '1f9a31bc', 'darkconsole.pkg.js' => '1f9a31bc',
'differential.pkg.css' => '58712637', 'differential.pkg.css' => '58712637',
'differential.pkg.js' => '6375358e', 'differential.pkg.js' => 'e6129b80',
'diffusion.pkg.css' => 'b93d9b8c', 'diffusion.pkg.css' => 'b93d9b8c',
'diffusion.pkg.js' => '84c8f8fd', 'diffusion.pkg.js' => '84c8f8fd',
'favicon.ico' => '30672e08', 'favicon.ico' => '30672e08',
@ -390,15 +390,14 @@ return array(
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173',
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375',
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63',
'rsrc/js/application/diff/DiffChangeset.js' => '4c9c47ad', 'rsrc/js/application/diff/DiffChangeset.js' => '3d4b3c5e',
'rsrc/js/application/diff/DiffChangesetList.js' => '589a30aa', 'rsrc/js/application/diff/DiffChangesetList.js' => 'e2c315d9',
'rsrc/js/application/diff/DiffInline.js' => '98c12b2f', 'rsrc/js/application/diff/DiffInline.js' => '98c12b2f',
'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832',
'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d', 'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d',
'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76',
'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1',
'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '89d11432', 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '89d11432',
'rsrc/js/application/differential/behavior-keyboard-nav.js' => '92904457',
'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819',
'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb',
'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d',
@ -624,7 +623,6 @@ return array(
'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 'javelin-behavior-differential-diff-radios' => 'e1ff79b1',
'javelin-behavior-differential-edit-inline-comments' => '89d11432', 'javelin-behavior-differential-edit-inline-comments' => '89d11432',
'javelin-behavior-differential-feedback-preview' => 'b064af76', 'javelin-behavior-differential-feedback-preview' => 'b064af76',
'javelin-behavior-differential-keyboard-navigation' => '92904457',
'javelin-behavior-differential-populate' => '5e41c819', 'javelin-behavior-differential-populate' => '5e41c819',
'javelin-behavior-differential-toggle-files' => 'ca3f91eb', 'javelin-behavior-differential-toggle-files' => 'ca3f91eb',
'javelin-behavior-differential-user-select' => 'a8d8459d', 'javelin-behavior-differential-user-select' => 'a8d8459d',
@ -783,8 +781,8 @@ return array(
'phabricator-darklog' => 'c8e1ffe3', 'phabricator-darklog' => 'c8e1ffe3',
'phabricator-darkmessage' => 'c48cccdd', 'phabricator-darkmessage' => 'c48cccdd',
'phabricator-dashboard-css' => 'fe5b1869', 'phabricator-dashboard-css' => 'fe5b1869',
'phabricator-diff-changeset' => '4c9c47ad', 'phabricator-diff-changeset' => '3d4b3c5e',
'phabricator-diff-changeset-list' => '589a30aa', 'phabricator-diff-changeset-list' => 'e2c315d9',
'phabricator-diff-inline' => '98c12b2f', 'phabricator-diff-inline' => '98c12b2f',
'phabricator-drag-and-drop-file-upload' => '58dea2fa', 'phabricator-drag-and-drop-file-upload' => '58dea2fa',
'phabricator-draggable-list' => 'bea6e7f4', 'phabricator-draggable-list' => 'bea6e7f4',
@ -1160,6 +1158,17 @@ return array(
'javelin-util', 'javelin-util',
'javelin-uri', 'javelin-uri',
), ),
'3d4b3c5e' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
'phabricator-diff-inline',
),
'3dbf94d5' => array( '3dbf94d5' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-dom', 'javelin-dom',
@ -1270,17 +1279,6 @@ return array(
'javelin-uri', 'javelin-uri',
'phabricator-notification', 'phabricator-notification',
), ),
'4c9c47ad' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
'javelin-install',
'javelin-workflow',
'javelin-router',
'javelin-behavior-device',
'javelin-vector',
'phabricator-diff-inline',
),
'4d863052' => array( '4d863052' => array(
'javelin-dom', 'javelin-dom',
'javelin-util', 'javelin-util',
@ -1348,9 +1346,6 @@ return array(
'javelin-vector', 'javelin-vector',
'javelin-dom', 'javelin-dom',
), ),
'589a30aa' => array(
'javelin-install',
),
'58dea2fa' => array( '58dea2fa' => array(
'javelin-install', 'javelin-install',
'javelin-util', 'javelin-util',
@ -1629,12 +1624,6 @@ return array(
'javelin-dom', 'javelin-dom',
'javelin-request', 'javelin-request',
), ),
92904457 => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'phabricator-keyboard-shortcut',
),
'92b9ec77' => array( '92b9ec77' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -2119,6 +2108,9 @@ return array(
'javelin-stratcom', 'javelin-stratcom',
'javelin-dom', 'javelin-dom',
), ),
'e2c315d9' => array(
'javelin-install',
),
'e2e0a072' => array( 'e2e0a072' => array(
'javelin-behavior', 'javelin-behavior',
'javelin-stratcom', 'javelin-stratcom',
@ -2440,7 +2432,6 @@ return array(
'javelin-behavior-differential-populate', 'javelin-behavior-differential-populate',
'javelin-behavior-differential-diff-radios', 'javelin-behavior-differential-diff-radios',
'javelin-behavior-differential-comment-jump', 'javelin-behavior-differential-comment-jump',
'javelin-behavior-differential-keyboard-navigation',
'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-aphront-drag-and-drop-textarea',
'javelin-behavior-phabricator-object-selector', 'javelin-behavior-phabricator-object-selector',
'javelin-behavior-repository-crossreference', 'javelin-behavior-repository-crossreference',

View file

@ -197,7 +197,6 @@ return array(
'javelin-behavior-differential-populate', 'javelin-behavior-differential-populate',
'javelin-behavior-differential-diff-radios', 'javelin-behavior-differential-diff-radios',
'javelin-behavior-differential-comment-jump', 'javelin-behavior-differential-comment-jump',
'javelin-behavior-differential-keyboard-navigation',
'javelin-behavior-aphront-drag-and-drop-textarea', 'javelin-behavior-aphront-drag-and-drop-textarea',
'javelin-behavior-phabricator-object-selector', 'javelin-behavior-phabricator-object-selector',
'javelin-behavior-repository-crossreference', 'javelin-behavior-repository-crossreference',

View file

@ -465,7 +465,6 @@ final class DifferentialRevisionViewController extends DifferentialController {
} }
Javelin::initBehavior('differential-user-select'); Javelin::initBehavior('differential-user-select');
Javelin::initBehavior('differential-keyboard-navigation');
$view = id(new PHUITwoColumnView()) $view = id(new PHUITwoColumnView())
->setHeader($header) ->setHeader($header)

View file

@ -234,6 +234,16 @@ final class DifferentialChangesetListView extends AphrontView {
'Highlight As...' => pht('Highlight As...'), 'Highlight As...' => pht('Highlight As...'),
'Loading...' => pht('Loading...'), 'Loading...' => pht('Loading...'),
'Jump to next change.' => pht('Jump to next change.'),
'Jump to previous change.' => pht('Jump to previous change.'),
'Jump to next file.' => pht('Jump to next file.'),
'Jump to previous file.' => pht('Jump to previous file.'),
'Jump to next inline comment.' => pht('Jump to next inline comment.'),
'Jump to previous inline comment.' =>
pht('Jump to previous inline comment.'),
'Jump to the table of contents.' =>
pht('Jump to the table of contents.'),
), ),
)); ));

View file

@ -720,8 +720,6 @@ final class DiffusionCommitController extends DiffusionController {
$request = $this->getRequest(); $request = $this->getRequest();
$viewer = $request->getUser(); $viewer = $request->getUser();
Javelin::initBehavior('differential-keyboard-navigation');
// TODO: This is pretty awkward, unify the CSS between Diffusion and // TODO: This is pretty awkward, unify the CSS between Diffusion and
// Differential better. // Differential better.
require_celerity_resource('differential-core-view-css'); require_celerity_resource('differential-core-view-css');

View file

@ -318,6 +318,88 @@ JX.install('DiffChangeset', {
return this._highlight; return this._highlight;
}, },
getSelectableItems: function() {
var items = [];
items.push({
type: 'file',
changeset: this,
target: this,
nodes: {
begin: this._node,
end: null
}
});
var rows = JX.DOM.scry(this._node, 'tr');
var blocks = [];
var block;
var ii;
for (ii = 0; ii < rows.length; ii++) {
var type = this._getRowType(rows[ii]);
if (!block || (block.type !== type)) {
block = {
type: type,
items: []
};
blocks.push(block);
}
block.items.push(rows[ii]);
}
for (ii = 0; ii < blocks.length; ii++) {
block = blocks[ii];
if (block.type == 'change') {
items.push({
type: block.type,
changeset: this,
target: block.items[0],
nodes: {
begin: block.items[0],
end: block.items[block.items.length - 1]
}
});
}
if (block.type == 'comment') {
for (var jj = 0; jj < block.items.length; jj++) {
items.push({
type: block.type,
changeset: this,
target: block.items[jj],
nodes: {
begin: block.items[jj],
end: block.items[jj]
}
});
}
}
}
return items;
},
_getRowType: function(row) {
// NOTE: Don't do "className.indexOf()" elsewhere. This is evil legacy
// magic.
if (row.className.indexOf('inline') !== -1) {
return 'comment';
}
var cells = JX.DOM.scry(row, 'td');
for (var ii = 0; ii < cells.length; ii++) {
if (cells[ii].className.indexOf('old') !== -1 ||
cells[ii].className.indexOf('new') !== -1) {
return 'change';
}
}
},
_getNodeData: function() { _getNodeData: function() {
return JX.Stratcom.getData(this._node); return JX.Stratcom.getData(this._node);
}, },

View file

@ -55,15 +55,49 @@ JX.install('DiffChangesetList', {
}, },
members: { members: {
_initialized: false,
_asleep: true, _asleep: true,
_changesets: null, _changesets: null,
_cursorItem: null,
_lastKeyboardManager: null,
sleep: function() { sleep: function() {
this._asleep = true; this._asleep = true;
}, },
wake: function() { wake: function() {
this._asleep = false; this._asleep = false;
if (this._initialized) {
return;
}
this._initialized = true;
var pht = this.getTranslations();
var label;
label = pht('Jump to next change.');
this._installJumpKey('j', label, 1);
label = pht('Jump to previous change.');
this._installJumpKey('k', label, -1);
label = pht('Jump to next file.');
this._installJumpKey('J', label, 1, 'file');
label = pht('Jump to previous file.');
this._installJumpKey('K', label, -1, 'file');
label = pht('Jump to next inline comment.');
this._installJumpKey('n', label, 1, 'comment');
label = pht('Jump to previous inline comment.');
this._installJumpKey('p', label, -1, 'comment');
label = pht('Jump to the table of contents.');
this._installKey('t', label, this._ontoc);
}, },
isAsleep: function() { isAsleep: function() {
@ -132,6 +166,134 @@ JX.install('DiffChangesetList', {
} }
}, },
_installKey: function(key, label, handler) {
handler = JX.bind(this, this._ifawake, handler);
return new JX.KeyboardShortcut(key, label)
.setHandler(handler)
.register();
},
_installJumpKey: function(key, label, delta, filter) {
filter = filter || null;
var handler = JX.bind(this, this._onjumpkey, delta, filter);
return this._installKey(key, label, handler);
},
_ontoc: function(manager) {
var toc = JX.$('toc');
manager.scrollTo(toc);
},
_onjumpkey: function(delta, filter, manager) {
var state = this._getSelectionState();
var cursor = state.cursor;
var items = state.items;
// If there's currently no selection and the user tries to go back,
// don't do anything.
if ((cursor === null) && (delta < 0)) {
return;
}
while (true) {
if (cursor === null) {
cursor = 0;
} else {
cursor = cursor + delta;
}
// If we've gone backward past the first change, bail out.
if (cursor < 0) {
return;
}
// If we've gone forward off the end of the list, bail out.
if (cursor >= items.length) {
return;
}
// If we're selecting things of a particular type (like only files)
// and the next item isn't of that type, move past it.
if (filter !== null) {
if (items[cursor].type !== filter) {
continue;
}
}
// Otherwise, we've found a valid item to select.
break;
}
this._setSelectionState(items[cursor], manager);
},
_getSelectionState: function() {
var items = this._getSelectableItems();
var cursor = null;
if (this._cursorItem !== null) {
for (var ii = 0; ii < items.length; ii++) {
var item = items[ii];
if (this._cursorItem.target === item.target) {
cursor = ii;
break;
}
}
}
return {
cursor: cursor,
items: items
};
},
_setSelectionState: function(item, manager) {
this._cursorItem = item;
this._redrawSelection(manager, true);
return this;
},
_redrawSelection: function(manager, scroll) {
manager = manager || this._lastKeyboardManager;
this._lastKeyboardManager = manager;
if (this.isAsleep()) {
manager.focusOn(null);
return;
}
var cursor = this._cursorItem;
if (!cursor) {
manager.focusOn(null);
return;
}
manager.focusOn(cursor.nodes.begin, cursor.nodes.end);
if (scroll) {
manager.scrollTo(cursor.nodes.begin);
}
return this;
},
_getSelectableItems: function() {
var result = [];
for (var ii = 0; ii < this._changesets.length; ii++) {
var items = this._changesets[ii].getSelectableItems();
for (var jj = 0; jj < items.length; jj++) {
result.push(items[jj]);
}
}
return result;
},
_onmore: function(e) { _onmore: function(e) {
e.kill(); e.kill();

View file

@ -1,264 +0,0 @@
/**
* @provides javelin-behavior-differential-keyboard-navigation
* @requires javelin-behavior
* javelin-dom
* javelin-stratcom
* phabricator-keyboard-shortcut
*/
JX.behavior('differential-keyboard-navigation', function(config) {
var cursor = -1;
var changesets;
var selection_begin = null;
var selection_end = null;
var refreshFocus = function() {};
function init() {
if (changesets) {
return;
}
changesets = JX.DOM.scry(document.body, 'div', 'differential-changeset');
}
function getBlocks(cursor) {
// TODO: This might not be terribly fast; we can't currently memoize it
// because it can change as ajax requests come in (e.g., content loads).
var rows = JX.DOM.scry(changesets[cursor], 'tr');
var blocks = [[changesets[cursor], changesets[cursor]]];
var start = null;
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]]);
}
start = null;
}
for (ii = 0; ii < rows.length; ii++) {
type = getRowType(rows[ii]);
if (type == 'comment') {
// If we see these types of rows, make a block for each one.
push();
}
if (!type) {
push();
} else if (type && !start) {
start = rows[ii];
}
}
push();
return blocks;
}
function getRowType(row) {
// NOTE: Being somewhat over-general here to allow other types of objects
// to be easily focused in the future (inline comments, 'show more..').
if (row.className.indexOf('inline') !== -1) {
return 'comment';
}
if (row.className.indexOf('differential-changeset') !== -1) {
return 'file';
}
var cells = JX.DOM.scry(row, 'td');
for (var ii = 0; ii < cells.length; ii++) {
// NOTE: The semantic use of classnames here is for performance; don't
// emulate this elsewhere since it's super terrible.
if (cells[ii].className.indexOf('old') !== -1 ||
cells[ii].className.indexOf('new') !== -1) {
return 'change';
}
}
return null;
}
function jump(manager, delta, jump_to_type) {
init();
if (cursor < 0) {
if (delta < 0) {
// If the user goes "back" without a selection, just reject the action.
return;
} else {
cursor = 0;
}
}
while (true) {
var blocks = getBlocks(cursor);
var focus;
if (delta < 0) {
focus = blocks.length;
} else {
focus = -1;
}
for (var ii = 0; ii < blocks.length; ii++) {
if (blocks[ii][0] == selection_begin) {
focus = ii;
break;
}
}
while (true) {
focus += delta;
if (blocks[focus]) {
var row_type = getRowType(blocks[focus][0]);
if (jump_to_type && row_type != jump_to_type) {
continue;
}
selection_begin = blocks[focus][0];
selection_end = blocks[focus][1];
manager.scrollTo(selection_begin);
refreshFocus = function() {
manager.focusOn(selection_begin, selection_end);
};
refreshFocus();
return;
} else {
var adjusted = (cursor + delta);
if (adjusted < 0 || adjusted >= changesets.length) {
// Stop cursor movement when the user reaches either end.
return;
}
cursor = adjusted;
// Break the inner loop and go to the next file.
break;
}
}
}
}
// When inline comments are updated, wipe out our cache of blocks since
// comments may have been added or deleted.
JX.Stratcom.listen(
null,
'differential-inline-comment-update',
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();
});
new JX.KeyboardShortcut('j', 'Jump to next change.')
.setHandler(function(manager) {
jump(manager, 1);
})
.register();
new JX.KeyboardShortcut('k', 'Jump to previous change.')
.setHandler(function(manager) {
jump(manager, -1);
})
.register();
new JX.KeyboardShortcut('J', 'Jump to next file.')
.setHandler(function(manager) {
jump(manager, 1, 'file');
})
.register();
new JX.KeyboardShortcut('K', 'Jump to previous file.')
.setHandler(function(manager) {
jump(manager, -1, 'file');
})
.register();
new JX.KeyboardShortcut('n', 'Jump to next inline comment.')
.setHandler(function(manager) {
jump(manager, 1, 'comment');
})
.register();
new JX.KeyboardShortcut('p', 'Jump to previous inline comment.')
.setHandler(function(manager) {
jump(manager, -1, 'comment');
})
.register();
new JX.KeyboardShortcut('t', 'Jump to the table of contents.')
.setHandler(function(manager) {
var toc = JX.$('toc');
manager.scrollTo(toc);
})
.register();
new JX.KeyboardShortcut(
'h',
'Collapse or expand the file display (after jump).')
.setHandler(function() {
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) {
// nothing selected
if (!node) {
return;
}
if (!JX.DOM.scry(node, 'a', 'differential-inline-' + op)) {
// No link for this operation, e.g. editing a comment you can't edit.
return;
}
var data = {
node: JX.DOM.find(node, 'div', 'differential-inline-comment'),
op: op
};
JX.Stratcom.invoke('differential-inline-action', null, data);
}
new JX.KeyboardShortcut('r', 'Reply to selected inline comment.')
.setHandler(function() {
inline_op(selection_begin, 'reply');
})
.register();
new JX.KeyboardShortcut('e', 'Edit selected inline comment.')
.setHandler(function() {
inline_op(selection_begin, 'edit');
})
.register();
});