Hi Dave,
Please find updated patch with docs and JS tests.
--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Mon, Jan 22, 2018 at 4:02 PM, Dave Page <[email protected]> wrote:
> Hi
>
> This seems to be missing documentation updates. I would also expect to see
> some Jasmine unit tests for some/all of the new JS functions.
>
> On Tue, Jan 16, 2018 at 1:29 PM, Murtuza Zabuawala <murtuza.zabuawala@
> enterprisedb.com> wrote:
>
>> Hi,
>>
>> PFA patch to add keyboard navigation in Debugger module via
>> Tab/Shift-Tab key.
>> RM#2897
>>
>> Also fixed the issue where user was not able to update values in
>> variable's panel while debugging.
>> RM#2981
>>
>> Currrently we have execution related shortcuts using accesskey,
>> Shortcuts (Execution related)
>> ----------------------------------
>> <accesskey> + i = Step in
>> <accesskey> + o = Step over
>> <accesskey> + c = Continue/Restart
>> <accesskey> + t = Toggle breakpoint
>> <accesskey> + x = Clear all breakpoints
>> <accesskey> + s = Stop
>>
>> Shortcuts (Panel navigation related)
>> ----------------------------------
>> Alt + Shift + Right Arrow
>> Alt + Shift + Left Arrow
>>
>> Edit/Enter values in Grid (Parameter & Local variables panel's)
>> -------------------------------------------------------------
>> Alt + Shift + g
>>
>> Please review.
>>
>> *Note:* As of now inner panel's are not getting focused on Tab/Shift-Tab
>> keys but once RM#2895 <https://redmine.postgresql.org/issues/2895> patch
>> gets committed it will start working automatically as it's inherited code
>> which will add tabindex tag automatically on each newly created wcDocker
>> panel.
>>
>> --
>> Regards,
>> Murtuza Zabuawala
>> EnterpriseDB: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
diff --git a/docs/en_US/keyboard_shortcuts.rst
b/docs/en_US/keyboard_shortcuts.rst
index de67f3e..119bd79 100644
--- a/docs/en_US/keyboard_shortcuts.rst
+++ b/docs/en_US/keyboard_shortcuts.rst
@@ -89,3 +89,32 @@ When using the Query Tool, the following shortcuts are
available:
+--------------------------+--------------------+-----------------------------------+
| Ctrl+Shift+F | Cmd+Shift+F | Replace
|
+--------------------------+--------------------+-----------------------------------+
+
+**Debugger**
+
+When using the Debugger, the following shortcuts are available:
+
++--------------------------+---------------------------+------------------------------+
+| Shortcut (Windows/Linux) | Shortcut (Mac) | Function
|
++==========================+===========================+==============================+
+| <accesskey> + i | <accesskey> + i | Step in
|
++--------------------------+---------------------------+------------------------------+
+| <accesskey> + o | <accesskey> + o | Step over
|
++--------------------------+---------------------------+------------------------------+
+| <accesskey> + c | <accesskey> + c | Continue/Restart
|
++--------------------------+---------------------------+------------------------------+
+| <accesskey> + t | <accesskey> + t | Toggle breakpoint
|
++--------------------------+---------------------------+------------------------------+
+| <accesskey> + x | <accesskey> + x | Clear all breakpoints
|
++--------------------------+---------------------------+------------------------------+
+| <accesskey> + s | <accesskey> + s | Stop
|
++--------------------------+---------------------------+------------------------------+
+| Alt + Shift + Right Arrow| Alt + Shift + Right Arrow | Move to next inner
panel |
++--------------------------+---------------------------+------------------------------+
+| Alt + Shift + Left Arrow | Alt + Shift + Left Arrow | Move to previous
inner panel |
++--------------------------+---------------------------+------------------------------+
+| Alt + Shift + g | Alt + Shift + g | Enter or Edit values
in Grid |
++--------------------------+---------------------------+------------------------------+
+
+.. note:: <accesskey> can be browser and platform dependant please `click here
<https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey>`_
for more information.
+
diff --git a/web/pgadmin/static/js/keyboard_shortcuts.js
b/web/pgadmin/static/js/keyboard_shortcuts.js
new file mode 100644
index 0000000..780e089
--- /dev/null
+++ b/web/pgadmin/static/js/keyboard_shortcuts.js
@@ -0,0 +1,119 @@
+import $ from 'jquery';
+
+const EDIT_KEY = 71, // Key: G -> Grid values
+ LEFT_ARROW_KEY = 37,
+ RIGHT_ARROW_KEY = 39;
+
+function isMac() {
+ return window.navigator.platform.search('Mac') != -1;
+}
+
+function isKeyCtrlAlt(event) {
+ return event.ctrlKey || event.altKey;
+}
+
+function isKeyAltShift(event) {
+ return event.altKey || event.shiftKey;
+}
+
+function isKeyCtrlShift(event) {
+ return event.ctrlKey || event.shiftKey;
+}
+
+function isKeyCtrlAltShift(event) {
+ return event.ctrlKey || event.altKey || event.shiftKey;
+}
+
+function isAltShiftBoth(event) {
+ return event.altKey && event.shiftKey && !event.ctrlKey;
+}
+
+function isCtrlShiftBoth(event) {
+ return event.ctrlKey && event.shiftKey && !event.altKey;
+}
+
+function isCtrlAltBoth(event) {
+ return event.ctrlKey && event.altKey && !event.shiftKey;
+}
+
+function _stopEventPropagation(event) {
+ event.cancelBubble = true;
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopImmediatePropagation();
+}
+
+/* Debugger: Keyboard Shortcuts handling */
+function keyboardShortcutsDebugger($el, event) {
+ let keyCode = event.which || event.keyCode;
+
+ // To handle debugger's internal tab navigation like Parameters/Messages...
+ if (this.isAltShiftBoth(event)) {
+ // Get the active wcDocker panel from DOM element
+ let panel_id, panel_content, $input;
+ switch(keyCode) {
+ case LEFT_ARROW_KEY:
+ this._stopEventPropagation(event);
+ panel_id = this.getInnerPanel($el, 'left');
+ break;
+ case RIGHT_ARROW_KEY:
+ this._stopEventPropagation(event);
+ panel_id = this.getInnerPanel($el, 'right');
+ break;
+ case EDIT_KEY:
+ this._stopEventPropagation(event);
+ panel_content = $el.find(
+ 'div.wcPanelTabContent:not(".wcPanelTabContentHidden")'
+ );
+ if(panel_content.length) {
+ $input = $(panel_content).find('td.editable:first');
+ if($input.length)
+ $input.click();
+ }
+ break;
+ }
+ // Actual panel starts with 1 in wcDocker
+ return panel_id;
+ }
+}
+
+// Finds the desired panel on which user wants to navigate to
+function getInnerPanel($el, direction) {
+ if(!$el || !$el.length)
+ return false;
+
+ let total_panels = $el.find('.wcPanelTab');
+ // If no panels found OR if single panel
+ if (!total_panels.length || total_panels.length == 1)
+ return false;
+
+ let active_panel = $(total_panels).filter('.wcPanelTabActive'),
+ id = parseInt($(active_panel).attr('id')),
+ fist_panel = 0,
+ last_panel = total_panels.length - 1;
+
+ // Find desired panel
+ if (direction == 'left') {
+ if(id > fist_panel)
+ id--;
+ } else {
+ if (id < last_panel)
+ id++;
+ }
+ return id;
+}
+
+module.exports = {
+ processEventDebugger: keyboardShortcutsDebugger,
+ getInnerPanel: getInnerPanel,
+ // misc functions
+ _stopEventPropagation: _stopEventPropagation,
+ isMac: isMac,
+ isKeyCtrlAlt: isKeyCtrlAlt,
+ isKeyAltShift: isKeyAltShift,
+ isKeyCtrlShift: isKeyCtrlShift,
+ isKeyCtrlAltShift: isKeyCtrlAltShift,
+ isAltShiftBoth: isAltShiftBoth,
+ isCtrlShiftBoth: isCtrlShiftBoth,
+ isCtrlAltBoth: isCtrlAltBoth,
+};
diff --git a/web/pgadmin/tools/debugger/__init__.py
b/web/pgadmin/tools/debugger/__init__.py
index a173c8e..099adb4 100644
--- a/web/pgadmin/tools/debugger/__init__.py
+++ b/web/pgadmin/tools/debugger/__init__.py
@@ -17,6 +17,8 @@ import random
from flask import url_for, Response, render_template, request, session,
current_app
from flask_babel import gettext
from flask_security import login_required
+from werkzeug.useragents import UserAgent
+
from pgadmin.utils import PgAdminModule
from pgadmin.utils.ajax import bad_request
from pgadmin.utils.ajax import make_json_response, \
@@ -346,6 +348,9 @@ def direct_new(trans_id):
if "linux" in _platform:
is_linux_platform = True
+ # We need client OS information to render correct Keyboard shortcuts
+ user_agent = UserAgent(request.headers.get('User-Agent'))
+
return render_template(
"debugger/direct.html",
_=gettext,
@@ -354,6 +359,7 @@ def direct_new(trans_id):
debug_type=debug_type,
is_desktop_mode=current_app.PGADMIN_RUNTIME,
is_linux=is_linux_platform,
+ client_platform=user_agent.platform,
stylesheets=[url_for('debugger.static', filename='css/debugger.css')]
)
diff --git a/web/pgadmin/tools/debugger/static/js/debugger_ui.js
b/web/pgadmin/tools/debugger/static/js/debugger_ui.js
index 3a35ceb..f9049d8 100644
--- a/web/pgadmin/tools/debugger/static/js/debugger_ui.js
+++ b/web/pgadmin/tools/debugger/static/js/debugger_ui.js
@@ -132,7 +132,8 @@ define([
// Variables to store the data sent from sqlite database
var func_args_data = this.func_args_data = [];
- // As we are not getting pgBrowser.tree when we debug again so
tree info will be updated from the server data
+ // As we are not getting pgBrowser.tree when we debug again
+ // so tree info will be updated from the server data
if (restart_debug == 0) {
var t = pgBrowser.tree,
i = t.selected(),
@@ -501,7 +502,8 @@ define([
}
}
- // Check if the arguments already available in the sqlite database
then we should use the existing arguments
+ // Check if the arguments already available in the sqlite database
+ // then we should use the existing arguments
if (func_args_data.length == 0) {
this.debuggerInputArgsColl =
new DebuggerInputArgCollections(my_obj);
@@ -537,12 +539,12 @@ define([
setup: function() {
return {
buttons: [{
- text: 'Debug',
+ text: gettext('Debug'),
key: 13,
className: 'btn btn-primary',
},
{
- text: 'Cancel',
+ text: gettext('Cancel'),
key: 27,
className: 'btn btn-primary',
},
@@ -563,13 +565,14 @@ define([
},
// Callback functions when click on the buttons of the Alertify
dialogs
callback: function(e) {
- if (e.button.text === 'Debug') {
+ if (e.button.text === gettext('Debug')) {
// Initialize the target once the debug button is clicked and
// create asynchronous connection and unique transaction ID
var self = this;
- // If the debugging is started again then treeInfo is already
stored in this.data so we can use the same.
+ // If the debugging is started again then treeInfo is already
+ // stored in this.data so we can use the same.
if (self.restart_debug == 0) {
var t = pgBrowser.tree,
i = t.selected(),
@@ -791,7 +794,8 @@ define([
},
});
} else {
- // If the debugging is started again then we should only set
the arguments and start the listener again
+ // If the debugging is started again then we should only set
the
+ // arguments and start the listener again
baseUrl = url_for('debugger.start_listener', {
'trans_id': self.data.trans_id,
});
@@ -838,7 +842,7 @@ define([
return true;
}
- if (e.button.text === 'Cancel') {
+ if (e.button.text === gettext('Cancel')) {
//close the dialog...
return false;
}
@@ -853,8 +857,8 @@ define([
);
/*
- If we already have data available in sqlite database then we
should enable the debug button otherwise
- disable the debug button.
+ If we already have data available in sqlite database then we
should
+ enable the debug button otherwise disable the debug button.
*/
if (this.func_args_data.length == 0) {
this.__internal.buttons[0].element.disabled = true;
@@ -875,7 +879,8 @@ define([
for (var i = 0; i < this.collection.length; i++) {
- // TODO: Need to check the "NULL" and "Expression" column
value to enable/disable the "Debug" button
+ // TODO: Need to check the "NULL" and "Expression" column
value to
+ // enable/disable the "Debug" button
if (this.collection.models[i].get('value') == '' ||
this.collection.models[i].get('value') == null ||
this.collection.models[i].get('value') == undefined) {
@@ -899,9 +904,11 @@ define([
});
}
- Alertify.debuggerInputArgsDialog('Debugger', args,
restart_debug).resizeTo('60%', '60%');
+ Alertify.debuggerInputArgsDialog(
+ gettext('Debugger'), args, restart_debug
+ ).resizeTo('60%', '60%');
};
return res;
-});
\ No newline at end of file
+});
diff --git a/web/pgadmin/tools/debugger/static/js/direct.js
b/web/pgadmin/tools/debugger/static/js/direct.js
index d0dbad7..b601841 100644
--- a/web/pgadmin/tools/debugger/static/js/direct.js
+++ b/web/pgadmin/tools/debugger/static/js/direct.js
@@ -2,10 +2,10 @@ define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'pgadmin.alertifyjs', 'sources/pgadmin', 'pgadmin.browser', 'backbone',
'pgadmin.backgrid', 'pgadmin.backform', 'sources/../bundle/codemirror',
- 'pgadmin.tools.debugger.ui', 'wcdocker',
+ 'pgadmin.tools.debugger.ui', 'sources/keyboard_shortcuts', 'wcdocker',
], function(
gettext, url_for, $, _, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
- Backform, codemirror, debug_function_again
+ Backform, codemirror, debug_function_again, keyboardShortcuts
) {
var CodeMirror = codemirror.default,
@@ -31,7 +31,8 @@ define([
/*
Function to set the breakpoint and send the line no. which is set to
server
- trans_id :- Unique Transaction ID, line_no - line no. to set the
breakpoint, set_type = 0 - clear , 1 - set
+ trans_id :- Unique Transaction ID, line_no - line no. to set the
breakpoint,
+ set_type = 0 - clear , 1 - set
*/
set_breakpoint: function(trans_id, line_no, set_type) {
// Make ajax call to set/clear the break point by user
@@ -57,7 +58,8 @@ define([
});
},
- // Function to get the latest breakpoint information and update the
gutters of codemirror
+ // Function to get the latest breakpoint information and update the
+ // gutters of codemirror
UpdateBreakpoint: function(trans_id) {
var self = this;
@@ -223,7 +225,8 @@ define([
// Call function to create and update local variables
self.AddLocalVariables(res.data.result);
self.AddParameters(res.data.result);
- // If debug function is restarted then again start listener to
read the updated messages.
+ // If debug function is restarted then again start listener to
+ // read the updated messages.
if (pgTools.DirectDebug.debug_restarted) {
if (pgTools.DirectDebug.debug_type) {
self.poll_end_execution_result(trans_id);
@@ -281,8 +284,8 @@ define([
},
/*
- poll the actual result after user has executed the "continue",
"step-into", "step-over" actions and get the
- other updated information from the server.
+ poll the actual result after user has executed the "continue",
"step-into",
+ "step-over" actions and get the other updated information from the
server.
*/
poll_result: function(trans_id) {
var self = this;
@@ -299,8 +302,9 @@ define([
poll_timeout;
/*
- During the execution we should poll the result in minimum seconds
but once the execution is completed
- and wait for the another debugging session then we should decrease
the polling frequency.
+ During the execution we should poll the result in minimum seconds but
+ once the execution is completed and wait for the another debugging
+ session then we should decrease the polling frequency.
*/
if (pgTools.DirectDebug.polling_timeout_idle) {
// Poll the result after 1 second
@@ -333,8 +337,13 @@ define([
pgTools.DirectDebug.docker.finishLoading(50);
pgTools.DirectDebug.editor.setValue(res.data.result[0].src);
self.UpdateBreakpoint(trans_id);
-
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap',
'CodeMirror-activeline-background');
-
pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2),
'wrap', 'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.removeLineClass(
+ self.active_line_no, 'wrap',
'CodeMirror-activeline-background'
+ );
+ pgTools.DirectDebug.editor.addLineClass(
+ (res.data.result[0].linenumber - 2),
+ 'wrap', 'CodeMirror-activeline-background'
+ );
self.active_line_no = (res.data.result[0].linenumber -
2);
// Update the stack, local variables and parameters
information
@@ -343,7 +352,9 @@ define([
} else if (!pgTools.DirectDebug.debug_type &&
!pgTools.DirectDebug.first_time_indirect_debug) {
pgTools.DirectDebug.docker.finishLoading(50);
if (self.active_line_no != undefined) {
-
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap',
'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.removeLineClass(
+ self.active_line_no, 'wrap',
'CodeMirror-activeline-background'
+ );
}
self.clear_all_breakpoint(trans_id);
self.execute_query(trans_id);
@@ -358,8 +369,13 @@ define([
self.UpdateBreakpoint(trans_id);
}
-
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap',
'CodeMirror-activeline-background');
-
pgTools.DirectDebug.editor.addLineClass((res.data.result[0].linenumber - 2),
'wrap', 'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.removeLineClass(
+ self.active_line_no, 'wrap',
'CodeMirror-activeline-background'
+ );
+ pgTools.DirectDebug.editor.addLineClass(
+ (res.data.result[0].linenumber - 2),
+ 'wrap', 'CodeMirror-activeline-background'
+ );
self.active_line_no = (res.data.result[0].linenumber -
2);
// Update the stack, local variables and parameters
information
@@ -378,7 +394,9 @@ define([
pgTools.DirectDebug.polling_timeout_idle = true;
// If status is Busy then poll the result by recursive call
to the poll function
if (!pgTools.DirectDebug.debug_type) {
- pgTools.DirectDebug.docker.startLoading(gettext('Waiting
for another session to invoke the target...'));
+ pgTools.DirectDebug.docker.startLoading(
+ gettext('Waiting for another session to invoke the
target...')
+ );
// As we are waiting for another session to invoke the
target,disable all the buttons
self.enable('stop', false);
@@ -429,8 +447,9 @@ define([
},
/*
- For the direct debugging, we need to check weather the functions
execution is completed or not. After completion
- of the debugging, we will stop polling the result until new execution
starts.
+ For the direct debugging, we need to check weather the functions
execution
+ is completed or not. After completion of the debugging, we will stop
polling
+ the result until new execution starts.
*/
poll_end_execution_result: function(trans_id) {
var self = this;
@@ -470,10 +489,13 @@ define([
if (res.data.status === 'Success') {
if (res.data.result == undefined) {
/*
- "result" is undefined only in case of EDB procedure. As
Once the EDB procedure execution is completed
- then we are not getting any result so we need ignore the
result.
+ "result" is undefined only in case of EDB procedure.
+ As Once the EDB procedure execution is completed then we
are
+ not getting any result so we need ignore the result.
*/
-
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap',
'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.removeLineClass(
+ self.active_line_no, 'wrap',
'CodeMirror-activeline-background'
+ );
pgTools.DirectDebug.direct_execution_completed = true;
pgTools.DirectDebug.polling_timeout_idle = true;
@@ -488,7 +510,8 @@ define([
// remove progress cursor
$('.debugger-container').removeClass('show_progress');
- // Execution completed so disable the buttons other than
"Continue/Start" button because user can still
+ // Execution completed so disable the buttons other than
+ // "Continue/Start" button because user can still
// start the same execution again.
self.enable('stop', false);
self.enable('step_over', false);
@@ -501,7 +524,9 @@ define([
} else {
// Call function to create and update local variables ....
if (res.data.result != null) {
-
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap',
'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.removeLineClass(
+ self.active_line_no, 'wrap',
'CodeMirror-activeline-background'
+ );
self.AddResults(res.data.col_info, res.data.result);
pgTools.DirectDebug.results_panel.focus();
pgTools.DirectDebug.direct_execution_completed = true;
@@ -518,7 +543,8 @@ define([
// remove progress cursor
$('.debugger-container').removeClass('show_progress');
- // Execution completed so disable the buttons other than
"Continue/Start" button because user can still
+ // Execution completed so disable the buttons other than
+ // "Continue/Start" button because user can still
// start the same execution again.
self.enable('stop', false);
self.enable('step_over', false);
@@ -532,7 +558,8 @@ define([
}
}
} else if (res.data.status === 'Busy') {
- // If status is Busy then poll the result by recursive call
to the poll function
+ // If status is Busy then poll the result by recursive call
to
+ // the poll function
self.poll_end_execution_result(trans_id);
// Update the message tab of the debugger
if (res.data.status_message) {
@@ -545,9 +572,12 @@ define([
);
} else if (res.data.status === 'ERROR') {
pgTools.DirectDebug.direct_execution_completed = true;
-
pgTools.DirectDebug.editor.removeLineClass(self.active_line_no, 'wrap',
'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.removeLineClass(
+ self.active_line_no, 'wrap',
'CodeMirror-activeline-background'
+ );
- //Set the Alertify message to inform the user that execution
is completed with error.
+ //Set the Alertify message to inform the user that execution
is
+ // completed with error.
if (!pgTools.DirectDebug.is_user_aborted_debugging) {
Alertify.error(res.info, 3);
}
@@ -625,14 +655,16 @@ define([
}
/*
- Need to check if restart debugging really require to open the
input dialog ?
- If yes then we will get the previous arguments from database and
populate the input dialog
- If no then we should directly start the listener.
+ Need to check if restart debugging really require to open the
input
+ dialog? If yes then we will get the previous arguments from
database
+ and populate the input dialog, If no then we should directly
start the
+ listener.
*/
if (res.data.result.require_input) {
debug_function_again(res.data.result, restart_dbg);
} else {
- // Debugging of void function is started again so we need to
start the listener again
+ // Debugging of void function is started again so we need to
start
+ // the listener again
var baseUrl = url_for('debugger.start_listener', {
'trans_id': trans_id,
});
@@ -803,7 +835,9 @@ define([
success: function(res) {
if (res.data.status) {
// Call function to create and update local variables ....
- pgTools.DirectDebug.editor.removeLineClass(self.active_line_no,
'wrap', 'CodeMirror-activeline-background');
+ pgTools.DirectDebug.editor.removeLineClass(
+ self.active_line_no, 'wrap', 'CodeMirror-activeline-background'
+ );
pgTools.DirectDebug.direct_execution_completed = true;
pgTools.DirectDebug.is_user_aborted_debugging = true;
@@ -1099,8 +1133,10 @@ define([
result_grid.render();
// Render the result grid into result panel
-
pgTools.DirectDebug.results_panel.$container.find('.debug_results').append(result_grid.el);
-
+ pgTools.DirectDebug.results_panel
+ .$container
+ .find('.debug_results')
+ .append(result_grid.el);
},
AddLocalVariables: function(result) {
@@ -1120,11 +1156,13 @@ define([
},
});
- // Collection which contains the model for function informations.
+ // Collection which contains the model for function information.
var VariablesCollection = Backbone.Collection.extend({
model: DebuggerVariablesModel,
});
+ VariablesCollection.prototype.on('change',
self.deposit_parameter_value, self);
+
var gridCols = [{
name: 'name',
label: gettext('Name'),
@@ -1170,7 +1208,10 @@ define([
variable_grid.render();
// Render the variables grid into local variables panel
-
pgTools.DirectDebug.local_variables_panel.$container.find('.local_variables').append(variable_grid.el);
+ pgTools.DirectDebug.local_variables_panel
+ .$container
+ .find('.local_variables')
+ .append(variable_grid.el);
},
@@ -1331,7 +1372,7 @@ define([
controller about the click and controller will take the action for the
specified button click.
*/
var DebuggerToolbarView = Backbone.View.extend({
- el: '#btn-toolbar',
+ el: '.dubugger_main_container',
initialize: function() {
controller.on('pgDebugger:button:state:stop', this.enable_stop, this);
controller.on('pgDebugger:button:state:step_over',
this.enable_step_over, this);
@@ -1347,6 +1388,7 @@ define([
'click .btn-continue': 'on_continue',
'click .btn-step-over': 'on_step_over',
'click .btn-step-into': 'on_step_into',
+ 'keydown': 'keyAction',
},
enable_stop: function(enable) {
var $btn = this.$el.find('.btn-stop');
@@ -1414,7 +1456,6 @@ define([
$btn.attr('disabled', 'disabled');
}
},
-
on_stop: function() {
controller.Stop(pgTools.DirectDebug.trans_id);
},
@@ -1433,19 +1474,28 @@ define([
on_step_into: function() {
controller.Step_into(pgTools.DirectDebug.trans_id);
},
+ keyAction: function (event) {
+ var $el = this.$el, panel_id, actual_panel;
+ panel_id = keyboardShortcuts.processEventDebugger($el, event);
+ // Panel navigation
+ if(!_.isUndefined(panel_id) && !_.isNull(panel_id)) {
+ actual_panel = panel_id + 1;
+ pgTools.DirectDebug.docker.findPanels()[actual_panel].focus();
+ }
+ },
});
/*
- Function is responsible to create the new wcDocker instance for debugger
and initialize the debugger panel inside
- the docker instance.
+ Function is responsible to create the new wcDocker instance for debugger
and
+ initialize the debugger panel inside the docker instance.
*/
var DirectDebug = function() {};
_.extend(DirectDebug.prototype, {
- init: function(trans_id, debug_type) { /* We should get the transaction id
from the server during initialization here */
+ /* We should get the transaction id from the server during initialization
here */
+ init: function(trans_id, debug_type) {
// We do not want to initialize the module multiple times.
-
var self = this;
_.bindAll(pgTools.DirectDebug, 'messages');
@@ -1524,7 +1574,8 @@ define([
this.intializePanels();
},
- // Read the messages of the database server and get the port ID and attach
the executer to that port.
+ // Read the messages of the database server and get the port ID and attach
+ // the executer to that port.
messages: function(trans_id) {
var self = this;
// Make ajax call to listen the database message
@@ -1615,7 +1666,7 @@ define([
height: '100%',
isCloseable: false,
isPrivate: true,
- content: '<div id ="parameters" class="parameters"></div>',
+ content: '<div id ="parameters" class="parameters"
tabindex="0"></div>',
});
// Create the Local variables panel to display the local variables
of the function.
@@ -1626,7 +1677,7 @@ define([
height: '100%',
isCloseable: false,
isPrivate: true,
- content: '<div id ="local_variables"
class="local_variables"></div>',
+ content: '<div id ="local_variables" class="local_variables"
tabindex="0"></div>',
});
// Create the messages panel to display the message returned from
the database server
@@ -1637,7 +1688,7 @@ define([
height: '100%',
isCloseable: false,
isPrivate: true,
- content: '<div id="messages" class="messages"></div>',
+ content: '<div id="messages" class="messages" tabindex="0"></div>',
});
// Create the result panel to display the result after debugging the
function
@@ -1648,7 +1699,7 @@ define([
height: '100%',
isCloseable: false,
isPrivate: true,
- content: '<div id="debug_results" class="debug_results"></div>',
+ content: '<div id="debug_results" class="debug_results"
tabindex="0"></div>',
});
// Create the stack pane panel to display the debugging stack
information.
@@ -1659,7 +1710,7 @@ define([
height: '100%',
isCloseable: false,
isPrivate: true,
- content: '<div id="stack_pane" class="stack_pane"></div>',
+ content: '<div id="stack_pane" class="stack_pane"
tabindex="0"></div>',
});
// Load all the created panels
@@ -1671,34 +1722,48 @@ define([
});
self.code_editor_panel = self.docker.addPanel('code', wcDocker.DOCK.TOP);
-
self.parameters_panel = self.docker.addPanel(
- 'parameters', wcDocker.DOCK.BOTTOM, self.code_editor_panel);
- self.local_variables_panel = self.docker.addPanel('local_variables',
wcDocker.DOCK.STACKED, self.parameters_panel, {
- tabOrientation: wcDocker.TAB.TOP,
- });
- self.messages_panel = self.docker.addPanel('messages',
wcDocker.DOCK.STACKED, self.parameters_panel);
+ 'parameters', wcDocker.DOCK.BOTTOM, self.code_editor_panel
+ );
+ self.local_variables_panel = self.docker.addPanel(
+ 'local_variables',
+ wcDocker.DOCK.STACKED,
+ self.parameters_panel, {
+ tabOrientation: wcDocker.TAB.TOP,
+ }
+ );
+ self.messages_panel = self.docker.addPanel(
+ 'messages', wcDocker.DOCK.STACKED, self.parameters_panel);
self.results_panel = self.docker.addPanel(
- 'results', wcDocker.DOCK.STACKED, self.parameters_panel);
+ 'results', wcDocker.DOCK.STACKED, self.parameters_panel);
self.stack_pane_panel = self.docker.addPanel(
- 'stack_pane', wcDocker.DOCK.STACKED, self.parameters_panel);
+ 'stack_pane', wcDocker.DOCK.STACKED, self.parameters_panel);
- var editor_pane = $('<div id="stack_editor_pane"
class="full-container-pane info"></div>');
- var code_editor_area = $('<textarea
id="debugger-editor-textarea"></textarea>').append(editor_pane);
+ var editor_pane = $('<div id="stack_editor_pane" ' +
+ 'class="full-container-pane info"></div>');
+ var code_editor_area = $('<textarea id="debugger-editor-textarea">' +
+ '</textarea>').append(editor_pane);
self.code_editor_panel.layout().addItem(code_editor_area);
// To show the line-number and set breakpoint marker details by user.
self.editor = CodeMirror.fromTextArea(
code_editor_area.get(0), {
+ tabindex: 0,
lineNumbers: true,
foldOptions: {
widget: '\u2026',
},
foldGutter: {
- rangeFinder:
CodeMirror.fold.combine(CodeMirror.pgadminBeginRangeFinder,
CodeMirror.pgadminIfRangeFinder,
- CodeMirror.pgadminLoopRangeFinder,
CodeMirror.pgadminCaseRangeFinder),
+ rangeFinder: CodeMirror.fold.combine(
+ CodeMirror.pgadminBeginRangeFinder,
+ CodeMirror.pgadminIfRangeFinder,
+ CodeMirror.pgadminLoopRangeFinder,
+ CodeMirror.pgadminCaseRangeFinder
+ ),
},
- gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter',
'breakpoints'],
+ gutters: [
+ 'CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'breakpoints',
+ ],
mode: 'text/x-pgsql',
readOnly: true,
extraKeys: pgAdmin.Browser.editor_shortcut_keys,
@@ -1741,7 +1806,7 @@ define([
panel.title(title);
panel.closeable(false);
panel.layout().addItem(
- $('<div>', {
+ $('<div tabindex="0">', {
'class': 'pg-debugger-panel',
})
);
diff --git a/web/pgadmin/tools/debugger/templates/debugger/direct.html
b/web/pgadmin/tools/debugger/templates/debugger/direct.html
index 29bb729..8ab50cb 100644
--- a/web/pgadmin/tools/debugger/templates/debugger/direct.html
+++ b/web/pgadmin/tools/debugger/templates/debugger/direct.html
@@ -35,44 +35,57 @@ try {
.debugger-container .wcLoadingIcon.fa-pulse{-webkit-animation: none;}
</style>
{% endif %}
-<nav class="navbar-inverse navbar-fixed-top">
- <div id="btn-toolbar" class="btn-toolbar pg-prop-btn-group bg-gray-2
border-gray-3" role="toolbar" aria-label="">
- <div class="btn-group" role="group" aria-label="">
- <button type="button" class="btn btn-default btn-step-into"
title="{{ _('Step into') }}"
- tabindex="1">
- <i class="fa fa-indent"></i>
- </button>
- <button type="button" class="btn btn-default btn-step-over"
title="{{ _('Step over') }}"
- tabindex="2">
- <i class="fa fa-outdent"></i>
- </button>
- <button type="button" class="btn btn-default btn-continue"
title="{{ _('Continue/Start') }}"
- tabindex="3">
- <i class="fa fa-play-circle"></i>
- </button>
+<div class="dubugger_main_container" tabindex="0">
+ <nav class="navbar-inverse navbar-fixed-top">
+ <div id="btn-toolbar" class="btn-toolbar pg-prop-btn-group bg-gray-2
border-gray-3" role="toolbar" aria-label="">
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-step-into"
+ title="{{ _('Step into') }}"
+ accesskey="i"
+ tabindex="0" autofocus="autofocus">
+ <i class="fa fa-indent"></i>
+ </button>
+ <button type="button" class="btn btn-default btn-step-over"
+ title="{{ _('Step over') }}"
+ accesskey="o"
+ tabindex="0">
+ <i class="fa fa-outdent"></i>
+ </button>
+ <button type="button" class="btn btn-default btn-continue"
+ title="{{ _('Continue/Start') }}"
+ accesskey="c"
+ tabindex="0">
+ <i class="fa fa-play-circle"></i>
+ </button>
+ </div>
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default
btn-toggle-breakpoint"
+ title="{{ _('Toggle breakpoint') }}"
+ accesskey="t"
+ tabindex="0">
+ <i class="fa fa-circle"></i>
+ </button>
+ <button type="button" class="btn btn-default
btn-clear-breakpoint"
+ title="{{ _('Clear all breakpoints') }}"
+ accesskey="x"
+ tabindex="0">
+ <i class="fa fa-ban"></i>
+ </button>
+ </div>
+ <div class="btn-group" role="group" aria-label="">
+ <button type="button" class="btn btn-default btn-stop"
+ accesskey="s"
+ title="{{ _('Stop') }}"
+ tabindex="0">
+ <i class="fa fa-stop-circle"></i>
+ </button>
+ </div>
</div>
- <div class="btn-group" role="group" aria-label="">
- <button type="button" class="btn btn-default
btn-toggle-breakpoint" title="{{ _('Toggle breakpoint') }}"
- tabindex="4">
- <i class="fa fa-circle"></i>
- </button>
- <button type="button" class="btn btn-default btn-clear-breakpoint"
title="{{ _('Clear all breakpoints') }}"
- tabindex="5">
- <i class="fa fa-ban"></i>
- </button>
- </div>
- <div class="btn-group" role="group" aria-label="">
- <button type="button" class="btn btn-default btn-stop" title="{{
_('Stop') }}"
- tabindex="6">
- <i class="fa fa-stop-circle"></i>
- </button>
- </div>
- </div>
-</nav>
-<div id="container" class="debugger-container"></div>
+ </nav>
+ <div id="container" class="debugger-container" tabindex="0"></div>
+</div>
{% endblock %}
-
{% block css_link %}
{% for stylesheet in stylesheets %}
<link type="text/css" rel="stylesheet" href="{{ stylesheet }}"/>
diff --git a/web/regression/javascript/common_keyboard_shortcuts_spec.js
b/web/regression/javascript/common_keyboard_shortcuts_spec.js
new file mode 100644
index 0000000..8887dc6
--- /dev/null
+++ b/web/regression/javascript/common_keyboard_shortcuts_spec.js
@@ -0,0 +1,50 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+
+import keyboardShortcuts from 'sources/keyboard_shortcuts';
+
+describe('the keyboard shortcuts', () => {
+ const F1_KEY = 112,
+ EDIT_KEY = 71, // Key: G -> Grid values
+ LEFT_ARROW_KEY = 37,
+ RIGHT_ARROW_KEY = 39,
+ MOVE_NEXT = 'right';
+
+ let debuggerElementSpy, event;
+ beforeEach(() => {
+ event = {
+ shift: false,
+ which: undefined,
+ preventDefault: jasmine.createSpy('preventDefault'),
+ cancelBubble: false,
+ stopPropagation: jasmine.createSpy('stopPropagation'),
+ stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
+ };
+ });
+
+ describe('when the key is not handled by the function', function () {
+ beforeEach(() => {
+ event.which = F1_KEY;
+ keyboardShortcuts.processEventDebugger(debuggerElementSpy, event);
+ });
+
+ it('should allow event to propagate', () => {
+ expect(event.preventDefault).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when user wants to goto next panel', function () {
+
+ it('returns panel id', function () {
+ expect(keyboardShortcuts.getInnerPanel(debuggerElementSpy,
'right')).toEqual(false);
+ });
+ });
+
+
+});