Add keyboard shortcuts for diffing against base and latest
Add v + s/↓ to diff base against "right".
Add v + w/↑ to diff "left" against "latest".
Add v + a/← to diff base against "left"
Add v + d/→ to diff "right" against latest
The user is viewing diff of "left" against "right". "latest" is the
most recent patchset created by the user.
Change-Id: I86d61934e9132ac53f39c9ae88c49c46195b65c0
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js
index 49f27bc..92ecb8d 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.js
@@ -101,11 +101,14 @@
const DOC_ONLY = 'DOC_ONLY';
const GO_KEY = 'GO_KEY';
+const V_KEY = 'V_KEY';
// The maximum age of a keydown event to be used in a jump navigation. This
// is only for cases when the keyup event is lost.
const GO_KEY_TIMEOUT_MS = 1000;
+const V_KEY_TIMEOUT_MS = 1000;
+
const ShortcutSection = {
ACTIONS: 'Actions',
DIFFS: 'Diffs',
@@ -141,6 +144,11 @@
TOGGLE_DIFF_MODE: 'TOGGLE_DIFF_MODE',
REFRESH_CHANGE: 'REFRESH_CHANGE',
EDIT_TOPIC: 'EDIT_TOPIC',
+ DIFF_AGAINST_BASE: 'DIFF_AGAINST_BASE',
+ DIFF_AGAINST_LATEST: 'DIFF_AGAINST_LATEST',
+ DIFF_BASE_AGAINST_LEFT: 'DIFF_BASE_AGAINST_LEFT',
+ DIFF_RIGHT_AGAINST_LATEST: 'DIFF_RIGHT_AGAINST_LATEST',
+ DIFF_BASE_AGAINST_LATEST: 'DIFF_BASE_AGAINST_LATEST',
NEXT_LINE: 'NEXT_LINE',
PREV_LINE: 'PREV_LINE',
@@ -233,9 +241,29 @@
'Star/unstar change');
_describe(Shortcut.EDIT_TOPIC, ShortcutSection.ACTIONS,
'Add a change topic');
+_describe(Shortcut.DIFF_AGAINST_BASE, ShortcutSection.ACTIONS,
+ 'Diff against base');
+_describe(Shortcut.DIFF_AGAINST_LATEST, ShortcutSection.ACTIONS,
+ 'Diff against latest patchset');
+_describe(Shortcut.DIFF_BASE_AGAINST_LEFT, ShortcutSection.ACTIONS,
+ 'Diff base against left');
+_describe(Shortcut.DIFF_RIGHT_AGAINST_LATEST, ShortcutSection.ACTIONS,
+ 'Diff right against latest');
+_describe(Shortcut.DIFF_BASE_AGAINST_LATEST, ShortcutSection.ACTIONS,
+ 'Diff base against latest');
_describe(Shortcut.NEXT_LINE, ShortcutSection.DIFFS, 'Go to next line');
_describe(Shortcut.PREV_LINE, ShortcutSection.DIFFS, 'Go to previous line');
+_describe(Shortcut.DIFF_AGAINST_BASE, ShortcutSection.DIFFS,
+ 'Diff against base');
+_describe(Shortcut.DIFF_AGAINST_LATEST, ShortcutSection.DIFFS,
+ 'Diff against latest patchset');
+_describe(Shortcut.DIFF_BASE_AGAINST_LEFT, ShortcutSection.DIFFS,
+ 'Diff base against left');
+_describe(Shortcut.DIFF_RIGHT_AGAINST_LATEST, ShortcutSection.DIFFS,
+ 'Diff right against latest');
+_describe(Shortcut.DIFF_BASE_AGAINST_LATEST, ShortcutSection.DIFFS,
+ 'Diff base against latest');
_describe(Shortcut.VISIBLE_LINE, ShortcutSection.DIFFS,
'Move cursor to currently visible code');
_describe(Shortcut.NEXT_CHUNK, ShortcutSection.DIFFS,
@@ -435,39 +463,52 @@
const bindings = this.bindings.get(shortcut);
if (!bindings) { return null; }
if (bindings[0] === GO_KEY) {
- return [['g'].concat(bindings.slice(1))];
+ return bindings.slice(1).map(
+ binding => this._describeKey(binding)
+ )
+ .map(binding => ['g'].concat(binding));
+ }
+ if (bindings[0] === V_KEY) {
+ return bindings.slice(1).map(
+ binding => this._describeKey(binding)
+ )
+ .map(binding => ['v'].concat(binding));
}
return bindings
.filter(binding => binding !== DOC_ONLY)
.map(binding => this.describeBinding(binding));
}
+ _describeKey(key) {
+ switch (key) {
+ case 'shift':
+ return 'Shift';
+ case 'meta':
+ return 'Meta';
+ case 'ctrl':
+ return 'Ctrl';
+ case 'enter':
+ return 'Enter';
+ case 'up':
+ return '↑';
+ case 'down':
+ return '↓';
+ case 'left':
+ return '←';
+ case 'right':
+ return '→';
+ default:
+ return key;
+ }
+ }
+
describeBinding(binding) {
if (binding.length === 1) {
return [binding];
}
- return binding.split(':')[0].split('+').map(part => {
- switch (part) {
- case 'shift':
- return 'Shift';
- case 'meta':
- return 'Meta';
- case 'ctrl':
- return 'Ctrl';
- case 'enter':
- return 'Enter';
- case 'up':
- return '↑';
- case 'down':
- return '↓';
- case 'left':
- return '←';
- case 'right':
- return '→';
- default:
- return part;
- }
- });
+ return binding.split(':')[0].split('+').map(part =>
+ this._describeKey(part)
+ );
}
notifyListeners() {
@@ -489,6 +530,8 @@
// eslint-disable-next-line object-shorthand
GO_KEY: GO_KEY,
// eslint-disable-next-line object-shorthand
+ V_KEY: V_KEY,
+ // eslint-disable-next-line object-shorthand
Shortcut: Shortcut,
// eslint-disable-next-line object-shorthand
ShortcutSection: ShortcutSection,
@@ -498,17 +541,20 @@
type: Number,
value: null,
},
-
_shortcut_go_table: {
type: Array,
value() { return new Map(); },
},
+ _shortcut_v_table: {
+ type: Array,
+ value() { return new Map(); },
+ },
},
modifierPressed(e) {
e = getKeyboardEvent(e);
return e.altKey || e.ctrlKey || e.metaKey || e.shiftKey ||
- !!this._inGoKeyMode();
+ !!this._inGoKeyMode() || !!this._inVKeyMode();
},
isModifierPressed(e, modifier) {
@@ -566,7 +612,14 @@
return;
}
if (bindings[0] === GO_KEY) {
- this._shortcut_go_table.set(bindings[1], handler);
+ bindings.slice(1).forEach(binding =>
+ this._shortcut_go_table.set(binding, handler));
+ } else if (bindings[0] === V_KEY) {
+ // for each binding added with the go/v key, we set the handler to be
+ // handleVKeyAction. handleVKeyAction then looks up in th
+ // shortcut_table to see what the relevant handler should be
+ bindings.slice(1).forEach(binding =>
+ this._shortcut_v_table.set(binding, handler));
} else {
this.addOwnKeyBinding(bindings.join(' '), handler);
}
@@ -593,6 +646,14 @@
this.addOwnKeyBinding(key, '_handleGoAction');
});
}
+
+ this.addOwnKeyBinding('v:keydown', '_handleVKeyDown');
+ this.addOwnKeyBinding('v:keyup', '_handleVKeyUp');
+ if (this._shortcut_v_table.size > 0) {
+ this._shortcut_v_table.forEach((handler, key) => {
+ this.addOwnKeyBinding(key, '_handleVAction');
+ });
+ }
},
/** @override */
@@ -614,6 +675,33 @@
shortcutManager.removeListener(listener);
},
+ _handleVKeyDown(e) {
+ this._shortcut_v_key_last_pressed = Date.now();
+ },
+
+ _handleVKeyUp(e) {
+ setTimeout(() => {
+ this._shortcut_v_key_last_pressed = null;
+ }, V_KEY_TIMEOUT_MS);
+ },
+
+ _inVKeyMode() {
+ return this._shortcut_v_key_last_pressed &&
+ (Date.now() - this._shortcut_v_key_last_pressed <=
+ V_KEY_TIMEOUT_MS);
+ },
+
+ _handleVAction(e) {
+ if (!this._inVKeyMode() ||
+ !this._shortcut_v_table.has(e.detail.key) ||
+ this.shouldSuppressKeyboardShortcut(e)) {
+ return;
+ }
+ e.preventDefault();
+ const handler = this._shortcut_v_table.get(e.detail.key);
+ this[handler](e);
+ },
+
_handleGoKeyDown(e) {
this._shortcut_go_key_last_pressed = Date.now();
},
@@ -648,6 +736,7 @@
export const KeyboardShortcutBinder = {
DOC_ONLY,
GO_KEY,
+ V_KEY,
Shortcut,
ShortcutManager,
ShortcutSection,
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 799adde..1da1f35 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -441,6 +441,13 @@
[this.Shortcut.COLLAPSE_ALL_MESSAGES]: '_handleCollapseAllMessages',
[this.Shortcut.OPEN_DIFF_PREFS]: '_handleOpenDiffPrefsShortcut',
[this.Shortcut.EDIT_TOPIC]: '_handleEditTopic',
+ [this.Shortcut.DIFF_AGAINST_BASE]: '_handleDiffAgainstBase',
+ [this.Shortcut.DIFF_AGAINST_LATEST]: '_handleDiffAgainstLatest',
+ [this.Shortcut.DIFF_BASE_AGAINST_LEFT]: '_handleDiffBaseAgainstLeft',
+ [this.Shortcut.DIFF_RIGHT_AGAINST_LATEST]:
+ '_handleDiffRightAgainstLatest',
+ [this.Shortcut.DIFF_BASE_AGAINST_LATEST]:
+ '_handleDiffBaseAgainstLatest',
};
}
@@ -1404,6 +1411,82 @@
this.$.metadata.editTopic();
}
+ _handleDiffAgainstBase(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ if (this.patchNumEquals(this._patchRange.basePatchNum, 'PARENT')) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Base is already selected.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+ GerritNav.navigateToChange(this._change, this._patchRange.patchNum);
+ }
+
+ _handleDiffBaseAgainstLeft(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ if (this.patchNumEquals(this._patchRange.basePatchNum, 'PARENT')) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Left is already base.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+ GerritNav.navigateToChange(this._change, this._patchRange.basePatchNum);
+ }
+
+ _handleDiffAgainstLatest(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ const latestPatchNum = this.computeLatestPatchNum(this._allPatchSets);
+ if (this.patchNumEquals(this._patchRange.patchNum, latestPatchNum)) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Latest is already selected.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+ GerritNav.navigateToChange(this._change, latestPatchNum,
+ this._patchRange.basePatchNum);
+ }
+
+ _handleDiffRightAgainstLatest(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ const latestPatchNum = this.computeLatestPatchNum(this._allPatchSets);
+ if (this.patchNumEquals(this._patchRange.patchNum, latestPatchNum)) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Right is already latest.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+ GerritNav.navigateToChange(this._change, latestPatchNum,
+ this._patchRange.patchNum);
+ }
+
+ _handleDiffBaseAgainstLatest(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ const latestPatchNum = this.computeLatestPatchNum(this._allPatchSets);
+ if (this.patchNumEquals(this._patchRange.patchNum, latestPatchNum) &&
+ this.patchNumEquals(this._patchRange.basePatchNum, 'PARENT')) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Already diffing base against latest.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+ GerritNav.navigateToChange(this._change, latestPatchNum);
+ }
+
_handleRefreshChange(e) {
if (this.shouldSuppressKeyboardShortcut(e)) { return; }
e.preventDefault();
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
index acd16d7..5624cc7 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.js
@@ -333,6 +333,81 @@
assert.isTrue(replaceStateStub.called);
});
+ test('_handleDiffAgainstBase', () => {
+ element._changeNum = '1';
+ element._patchRange = {
+ patchNum: 3,
+ basePatchNum: 1,
+ };
+ sandbox.stub(element, 'computeLatestPatchNum').returns(10);
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ element._handleDiffAgainstBase(new CustomEvent(''));
+ assert(navigateToChangeStub.called);
+ const args = navigateToChangeStub.getCall(0).args;
+ assert.equal(args[1], 3);
+ assert.isNotOk(args[0]);
+ });
+
+ test('_handleDiffAgainstLatest', () => {
+ element._changeNum = '1';
+ element._patchRange = {
+ basePatchNum: 1,
+ patchNum: 3,
+ };
+ sandbox.stub(element, 'computeLatestPatchNum').returns(10);
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ element._handleDiffAgainstLatest(new CustomEvent(''));
+ assert(navigateToChangeStub.called);
+ const args = navigateToChangeStub.getCall(0).args;
+ assert.equal(args[1], 10);
+ assert.equal(args[2], 1);
+ });
+
+ test('_handleDiffBaseAgainstLeft', () => {
+ element._changeNum = '1';
+ element._patchRange = {
+ patchNum: 3,
+ basePatchNum: 1,
+ };
+ sandbox.stub(element, 'computeLatestPatchNum').returns(10);
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ element._handleDiffBaseAgainstLeft(new CustomEvent(''));
+ assert(navigateToChangeStub.called);
+ const args = navigateToChangeStub.getCall(0).args;
+ assert.equal(args[1], 1);
+ assert.isNotOk(args[0]);
+ });
+
+ test('_handleDiffRightAgainstLatest', () => {
+ element._changeNum = '1';
+ element._patchRange = {
+ basePatchNum: 1,
+ patchNum: 3,
+ };
+ sandbox.stub(element, 'computeLatestPatchNum').returns(10);
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ element._handleDiffRightAgainstLatest(new CustomEvent(''));
+ assert(navigateToChangeStub.called);
+ const args = navigateToChangeStub.getCall(0).args;
+ assert.equal(args[1], 10);
+ assert.equal(args[2], 3);
+ });
+
+ test('_handleDiffBaseAgainstLatest', () => {
+ element._changeNum = '1';
+ element._patchRange = {
+ basePatchNum: 1,
+ patchNum: 3,
+ };
+ sandbox.stub(element, 'computeLatestPatchNum').returns(10);
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ element._handleDiffBaseAgainstLatest(new CustomEvent(''));
+ assert(navigateToChangeStub.called);
+ const args = navigateToChangeStub.getCall(0).args;
+ assert.equal(args[1], 10);
+ assert.isNotOk(args[2]);
+ });
+
suite('plugins adding to file tab', () => {
setup(done => {
// Resolving it here instead of during setup() as other tests depend
diff --git a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
index 5c54011..b8b414d 100644
--- a/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
+++ b/polygerrit-ui/app/elements/core/gr-key-binding-display/gr-key-binding-display.js
@@ -30,7 +30,10 @@
static get properties() {
return {
- /** @type {Array<string>} */
+ /** @type {Array<Array<string>>}
+ * Each entry in the binding represents an array that is a keyboard
+ * shortcut containing [modifier, combination]
+ */
binding: Array,
};
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index cf89c63..12ee74d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -300,6 +300,13 @@
[this.Shortcut.TOGGLE_BLAME]: '_handleToggleBlame',
[this.Shortcut.TOGGLE_HIDE_ALL_COMMENT_THREADS]:
'_handleToggleHideAllCommentThreads',
+ [this.Shortcut.DIFF_AGAINST_BASE]: '_handleDiffAgainstBase',
+ [this.Shortcut.DIFF_AGAINST_LATEST]: '_handleDiffAgainstLatest',
+ [this.Shortcut.DIFF_BASE_AGAINST_LEFT]: '_handleDiffBaseAgainstLeft',
+ [this.Shortcut.DIFF_RIGHT_AGAINST_LATEST]:
+ '_handleDiffRightAgainstLatest',
+ [this.Shortcut.DIFF_BASE_AGAINST_LATEST]:
+ '_handleDiffBaseAgainstLatest',
// Final two are actually handled by gr-comment-thread.
[this.Shortcut.EXPAND_ALL_COMMENT_THREADS]: null,
@@ -1287,6 +1294,87 @@
this.toggleClass('hideComments');
}
+ _handleDiffAgainstBase(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ if (this.patchNumEquals(this._patchRange.basePatchNum, 'PARENT')) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Base is already selected.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+ GerritNav.navigateToDiff(
+ this._change, this._path, this._patchRange.patchNum);
+ }
+
+ _handleDiffBaseAgainstLeft(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ if (this.patchNumEquals(this._patchRange.basePatchNum, 'PARENT')) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Left is already base.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+ GerritNav.navigateToDiff(this._change, this._path,
+ this._patchRange.basePatchNum);
+ }
+
+ _handleDiffAgainstLatest(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+
+ const latestPatchNum = this.computeLatestPatchNum(this._allPatchSets);
+ if (this.patchNumEquals(this._patchRange.patchNum, latestPatchNum)) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Latest is already selected.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+
+ GerritNav.navigateToDiff(
+ this._change, this._path, latestPatchNum,
+ this._patchRange.basePatchNum);
+ }
+
+ _handleDiffRightAgainstLatest(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ const latestPatchNum = this.computeLatestPatchNum(this._allPatchSets);
+ if (this.patchNumEquals(this._patchRange.patchNum, latestPatchNum)) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Right is already latest.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+ GerritNav.navigateToDiff(this._change, this._path, latestPatchNum,
+ this._patchRange.patchNum);
+ }
+
+ _handleDiffBaseAgainstLatest(e) {
+ if (this.shouldSuppressKeyboardShortcut(e)) { return; }
+ const latestPatchNum = this.computeLatestPatchNum(this._allPatchSets);
+ if (this.patchNumEquals(this._patchRange.patchNum, latestPatchNum) &&
+ this.patchNumEquals(this._patchRange.basePatchNum, 'PARENT')) {
+ this.dispatchEvent(new CustomEvent('show-alert', {
+ detail: {
+ message: 'Already diffing base against latest.',
+ },
+ composed: true, bubbles: true,
+ }));
+ return;
+ }
+ GerritNav.navigateToDiff(this._change, this._path, latestPatchNum);
+ }
+
_computeBlameLoaderClass(isImageDiff, path) {
return !this.isMagicPath(path) && !isImageDiff ? 'show' : '';
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
index 21d1144..0dce3f7 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
@@ -287,6 +287,81 @@
assert.isTrue(expandStub.called);
});
+ test('diff against base', () => {
+ element._patchRange = {
+ basePatchNum: '5',
+ patchNum: '10',
+ };
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+ element._handleDiffAgainstBase(new CustomEvent(''));
+ const args = diffNavStub.getCall(0).args;
+ assert.equal(args[2], 10);
+ assert.isNotOk(args[3]);
+ });
+
+ test('diff against latest', () => {
+ element._patchRange = {
+ basePatchNum: '5',
+ patchNum: '10',
+ };
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ sandbox.stub(element, 'computeLatestPatchNum').returns(12);
+ const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+ element._handleDiffAgainstLatest(new CustomEvent(''));
+ const args = diffNavStub.getCall(0).args;
+ assert.equal(args[2], 12);
+ assert.equal(args[3], 5);
+ });
+
+ test('_handleDiffBaseAgainstLeft', () => {
+ element._changeNum = '1';
+ element._patchRange = {
+ patchNum: 3,
+ basePatchNum: 1,
+ };
+ sandbox.stub(element, 'computeLatestPatchNum').returns(10);
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+ element._handleDiffBaseAgainstLeft(new CustomEvent(''));
+ assert(diffNavStub.called);
+ const args = diffNavStub.getCall(0).args;
+ assert.equal(args[2], 1);
+ assert.isNotOk(args[3]);
+ });
+
+ test('_handleDiffRightAgainstLatest', () => {
+ element._changeNum = '1';
+ element._patchRange = {
+ basePatchNum: 1,
+ patchNum: 3,
+ };
+ sandbox.stub(element, 'computeLatestPatchNum').returns(10);
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+ element._handleDiffRightAgainstLatest(new CustomEvent(''));
+ assert(diffNavStub.called);
+ const args = diffNavStub.getCall(0).args;
+ assert.equal(args[2], 10);
+ assert.equal(args[3], 3);
+ });
+
+ test('_handleDiffBaseAgainstLatest', () => {
+ element._changeNum = '1';
+ element._patchRange = {
+ basePatchNum: 1,
+ patchNum: 3,
+ };
+ sandbox.stub(element, 'computeLatestPatchNum').returns(10);
+ sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
+ const diffNavStub = sandbox.stub(GerritNav, 'navigateToDiff');
+ element._handleDiffBaseAgainstLatest(new CustomEvent(''));
+ assert(diffNavStub.called);
+ const args = diffNavStub.getCall(0).args;
+ assert.equal(args[2], 10);
+ assert.isNotOk(args[3]);
+ });
+
test('keyboard shortcuts with patch range', () => {
element._changeNum = '42';
element._patchRange = {
diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js
index 91af97c..a01e8a4 100644
--- a/polygerrit-ui/app/elements/gr-app-element.js
+++ b/polygerrit-ui/app/elements/gr-app-element.js
@@ -270,9 +270,9 @@
this.Shortcut.EDIT_TOPIC, 't');
this.bindShortcut(
- this.Shortcut.OPEN_REPLY_DIALOG, 'a');
+ this.Shortcut.OPEN_REPLY_DIALOG, 'a:keyup');
this.bindShortcut(
- this.Shortcut.OPEN_DOWNLOAD_DIALOG, 'd');
+ this.Shortcut.OPEN_DOWNLOAD_DIALOG, 'd:keyup');
this.bindShortcut(
this.Shortcut.EXPAND_ALL_MESSAGES, 'x');
this.bindShortcut(
@@ -285,6 +285,16 @@
this.Shortcut.UP_TO_CHANGE, 'u');
this.bindShortcut(
this.Shortcut.TOGGLE_DIFF_MODE, 'm:keyup');
+ this.bindShortcut(
+ this.Shortcut.DIFF_AGAINST_BASE, this.V_KEY, 'down', 's');
+ this.bindShortcut(
+ this.Shortcut.DIFF_AGAINST_LATEST, this.V_KEY, 'up', 'w');
+ this.bindShortcut(
+ this.Shortcut.DIFF_BASE_AGAINST_LEFT, this.V_KEY, 'left', 'a');
+ this.bindShortcut(
+ this.Shortcut.DIFF_RIGHT_AGAINST_LATEST, this.V_KEY, 'right', 'd');
+ this.bindShortcut(
+ this.Shortcut.DIFF_BASE_AGAINST_LATEST, this.V_KEY, 'b');
this.bindShortcut(
this.Shortcut.NEXT_LINE, 'j', 'down');