mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-09 23:15:58 -06:00
Support keyboard navigation in the debugger. Fixes #2897
In passing, fix injection of variable values. Fixes #2981
This commit is contained in:
parent
65337daeba
commit
0e41b3364b
@ -89,3 +89,43 @@ 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> is browser and platform dependant. The following table lists the default access keys for supported browsers.
|
||||
|
||||
+-------------------+------------+------------+------------+
|
||||
| | Windows | Linux | Mac |
|
||||
+===================+============+============+============+
|
||||
| Internet Explorer | Alt | Alt | |
|
||||
+-------------------+------------+------------+------------+
|
||||
| Chrome | Alt | Alt | Ctrl+Alt |
|
||||
+-------------------+------------+------------+------------+
|
||||
| Firefox | Alt+Shift | Alt+Shift | Ctrl+Alt |
|
||||
+-------------------+------------+------------+------------+
|
||||
| Safari | Alt | | Ctrl+Alt |
|
||||
+-------------------+------------+------------+------------+
|
||||
|
119
web/pgadmin/static/js/keyboard_shortcuts.js
Normal file
119
web/pgadmin/static/js/keyboard_shortcuts.js
Normal file
@ -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,
|
||||
};
|
@ -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')]
|
||||
)
|
||||
|
||||
|
@ -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;
|
||||
});
|
||||
});
|
||||
|
@ -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',
|
||||
})
|
||||
);
|
||||
|
@ -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 }}"/>
|
||||
|
50
web/regression/javascript/common_keyboard_shortcuts_spec.js
Normal file
50
web/regression/javascript/common_keyboard_shortcuts_spec.js
Normal file
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user