mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Added SQL Formatter support in Query Tool. Fixes #2042
This commit is contained in:
@@ -9,8 +9,12 @@
|
||||
|
||||
"""A blueprint module providing utility functions for the application."""
|
||||
|
||||
from flask import url_for
|
||||
import sqlparse
|
||||
from flask import request, url_for
|
||||
from flask_security import login_required
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.ajax import make_json_response
|
||||
from pgadmin.utils.preferences import Preferences
|
||||
|
||||
MODULE_NAME = 'sql'
|
||||
|
||||
@@ -23,6 +27,61 @@ class SQLModule(PgAdminModule):
|
||||
'when': None
|
||||
}]
|
||||
|
||||
def get_exposed_url_endpoints(self):
|
||||
"""
|
||||
Returns:
|
||||
list: URL endpoints
|
||||
"""
|
||||
return [
|
||||
'sql.format', 'sql.format'
|
||||
]
|
||||
|
||||
|
||||
# Initialise the module
|
||||
blueprint = SQLModule(MODULE_NAME, __name__, url_prefix='/misc/sql')
|
||||
|
||||
|
||||
def sql_format(sql):
|
||||
"""
|
||||
This function takes a SQL statement, formats it, and returns it
|
||||
"""
|
||||
p = Preferences.module('sqleditor')
|
||||
output = sqlparse.format(sql,
|
||||
keyword_case=p.preference(
|
||||
'keyword_case').get(),
|
||||
identifier_case=p.preference(
|
||||
'identifier_case').get(),
|
||||
strip_comments=p.preference(
|
||||
'strip_comments').get(),
|
||||
reindent=p.preference(
|
||||
'reindent').get(),
|
||||
reindent_aligned=p.preference(
|
||||
'reindent_aligned').get(),
|
||||
use_space_around_operators=p.preference(
|
||||
'spaces_around_operators').get(),
|
||||
comma_first=p.preference(
|
||||
'comma_first').get(),
|
||||
wrap_after=p.preference(
|
||||
'wrap_after').get(),
|
||||
indent_tabs=not p.preference(
|
||||
'use_spaces').get(),
|
||||
indent_width=p.preference(
|
||||
'tab_size').get())
|
||||
|
||||
return output
|
||||
|
||||
|
||||
@blueprint.route("/format", methods=['POST'], endpoint="format")
|
||||
@login_required
|
||||
def sql_format_wrapper():
|
||||
"""
|
||||
This endpoint takes a SQL statement, formats it, and returns it
|
||||
"""
|
||||
sql = ''
|
||||
if request.data:
|
||||
sql = sql_format(request.get_json()['sql'])
|
||||
|
||||
return make_json_response(
|
||||
data={'sql': sql},
|
||||
status=200
|
||||
)
|
||||
|
@@ -17,7 +17,8 @@ const PERIOD_KEY = 190,
|
||||
LEFT_KEY = 37,
|
||||
UP_KEY = 38,
|
||||
RIGHT_KEY = 39,
|
||||
DOWN_KEY = 40;
|
||||
DOWN_KEY = 40,
|
||||
K_KEY = 75;
|
||||
|
||||
function isMac() {
|
||||
return window.navigator.platform.search('Mac') != -1;
|
||||
@@ -247,6 +248,12 @@ function keyboardShortcutsQueryTool(
|
||||
) && keyCode === PERIOD_KEY) {
|
||||
this._stopEventPropagation(event);
|
||||
queryToolActions.uncommentLineCode(sqlEditorController);
|
||||
} else if ((
|
||||
(this.isMac() && event.metaKey) ||
|
||||
(!this.isMac() && event.ctrlKey)
|
||||
) && !event.altKey && event.shiftKey && keyCode === K_KEY) {
|
||||
this._stopEventPropagation(event);
|
||||
queryToolActions.formatSql(sqlEditorController);
|
||||
} else if (keyCode == ESC_KEY) {
|
||||
queryToolActions.focusOut(sqlEditorController);
|
||||
/*Apply only for sub-dropdown*/
|
||||
|
@@ -123,6 +123,10 @@ let queryToolActions = {
|
||||
);
|
||||
},
|
||||
|
||||
formatSql: function (sqlEditorController) {
|
||||
sqlEditorController.gridView.on_format_sql();
|
||||
},
|
||||
|
||||
focusOut: function () {
|
||||
document.activeElement.blur();
|
||||
pgWindow.document.activeElement.blur();
|
||||
|
@@ -189,6 +189,15 @@
|
||||
{{ _(' (Shift+Ctrl+/)') }}{%- endif %}</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item" id="btn-format-sql" href="#" tabindex="0">
|
||||
<span>{{ _('Format SQL') }}{% if client_platform == 'macos' -%}
|
||||
{{ _(' (Shift+Cmd+K)') }}
|
||||
{% else %}
|
||||
{{ _(' (Shift+Ctrl+K)') }}{%- endif %}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group mr-1" role="group" aria-label="">
|
||||
|
@@ -140,6 +140,9 @@ define('tools.querytool', [
|
||||
// Indentation options
|
||||
'click #btn-indent-code': 'on_indent_code',
|
||||
'click #btn-unindent-code': 'on_unindent_code',
|
||||
// Format
|
||||
'click #btn-format-sql': 'on_format_sql',
|
||||
// Transaction control
|
||||
'click #btn-commit': 'on_commit_transaction',
|
||||
'click #btn-rollback': 'on_rollback_transaction',
|
||||
},
|
||||
@@ -1743,6 +1746,16 @@ define('tools.querytool', [
|
||||
);
|
||||
},
|
||||
|
||||
on_format_sql: function() {
|
||||
var self = this;
|
||||
// Trigger the format signal to the SqlEditorController class
|
||||
self.handler.trigger(
|
||||
'pgadmin-sqleditor:format_sql',
|
||||
self,
|
||||
self.handler
|
||||
);
|
||||
},
|
||||
|
||||
// Callback function for the clear button click.
|
||||
on_clear: function(ev) {
|
||||
var self = this;
|
||||
@@ -2401,6 +2414,8 @@ define('tools.querytool', [
|
||||
// Indentation related
|
||||
self.on('pgadmin-sqleditor:indent_selected_code', self._indent_selected_code, self);
|
||||
self.on('pgadmin-sqleditor:unindent_selected_code', self._unindent_selected_code, self);
|
||||
// Format
|
||||
self.on('pgadmin-sqleditor:format_sql', self._format_sql, self);
|
||||
|
||||
window.parent.$(window.parent.document).on('pgadmin-sqleditor:rows-copied', self._copied_in_other_session);
|
||||
},
|
||||
@@ -4230,6 +4245,41 @@ define('tools.querytool', [
|
||||
editor.execCommand('indentLess');
|
||||
},
|
||||
|
||||
/*
|
||||
* This function will format the SQL
|
||||
*/
|
||||
_format_sql: function() {
|
||||
var self = this,
|
||||
editor = self.gridView.query_tool_obj,
|
||||
selection = true,
|
||||
sql = '';
|
||||
|
||||
sql = editor.getSelection();
|
||||
|
||||
if (sql == '') {
|
||||
sql = editor.getValue();
|
||||
selection = false;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url_for('sql.format'),
|
||||
data: JSON.stringify({'sql': sql}),
|
||||
method: 'POST',
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
})
|
||||
.done(function(res) {
|
||||
if (selection === true) {
|
||||
editor.replaceSelection(res.data.sql, 'around');
|
||||
} else {
|
||||
editor.setValue(res.data.sql);
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
/* failure should be ignored */
|
||||
});
|
||||
},
|
||||
|
||||
isQueryRunning: function() {
|
||||
return is_query_running;
|
||||
},
|
||||
|
@@ -169,27 +169,6 @@ def register_query_tool_preferences(self):
|
||||
)
|
||||
)
|
||||
|
||||
self.tab_size = self.preference.register(
|
||||
'Editor', 'tab_size',
|
||||
gettext("Tab size"), 'integer', 4,
|
||||
min_val=2,
|
||||
max_val=8,
|
||||
category_label=gettext('Options'),
|
||||
help_str=gettext(
|
||||
'The number of spaces per tab. Minimum 2, maximum 8.'
|
||||
)
|
||||
)
|
||||
|
||||
self.use_spaces = self.preference.register(
|
||||
'Editor', 'use_spaces',
|
||||
gettext("Use spaces?"), 'boolean', False,
|
||||
category_label=gettext('Options'),
|
||||
help_str=gettext(
|
||||
'Specifies whether or not to insert spaces instead of tabs '
|
||||
'when the tab key or auto-indent are used.'
|
||||
)
|
||||
)
|
||||
|
||||
self.wrap_code = self.preference.register(
|
||||
'Editor', 'wrap_code',
|
||||
gettext("Line wrapping?"), 'boolean', False,
|
||||
@@ -703,3 +682,98 @@ def register_query_tool_preferences(self):
|
||||
category_label=gettext('Keyboard shortcuts'),
|
||||
fields=shortcut_fields
|
||||
)
|
||||
|
||||
# Register options for SQL formatting
|
||||
self.keyword_case = self.preference.register(
|
||||
'editor', 'keyword_case',
|
||||
gettext("Keyword case"), 'radioModern', 'upper',
|
||||
options=[{'label': 'Upper case', 'value': 'upper'},
|
||||
{'label': 'Lower case', 'value': 'lower'},
|
||||
{'label': 'Capitalized', 'value': 'capitalize'}],
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext(
|
||||
'Convert keywords to upper, lower, or capitalized casing.'
|
||||
)
|
||||
)
|
||||
|
||||
self.identifier_case = self.preference.register(
|
||||
'editor', 'identifier_case',
|
||||
gettext("Identifier case"), 'radioModern', 'upper',
|
||||
options=[{'label': 'Upper case', 'value': 'upper'},
|
||||
{'label': 'Lower case', 'value': 'lower'},
|
||||
{'label': 'Capitalized', 'value': 'capitalize'}],
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext(
|
||||
'Convert identifiers to upper, lower, or capitalized casing.'
|
||||
)
|
||||
)
|
||||
|
||||
self.strip_comments = self.preference.register(
|
||||
'editor', 'strip_comments',
|
||||
gettext("Strip comments?"), 'boolean', False,
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext('If set to True, comments will be removed.')
|
||||
)
|
||||
|
||||
self.reindent = self.preference.register(
|
||||
'editor', 'reindent',
|
||||
gettext("Re-indent?"), 'boolean', True,
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext('If set to True, the indentations of the '
|
||||
'statements are changed.')
|
||||
)
|
||||
|
||||
self.reindent_aligned = self.preference.register(
|
||||
'editor', 'reindent_aligned',
|
||||
gettext("Re-indent aligned?"), 'boolean', False,
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext('If set to True, the indentations of the '
|
||||
'statements are changed, and statements are '
|
||||
'aligned by keywords.')
|
||||
)
|
||||
|
||||
self.spaces_around_operators = self.preference.register(
|
||||
'editor', 'spaces_around_operators',
|
||||
gettext("Spaces around operators?"), 'boolean', True,
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext('If set to True, spaces are used around all '
|
||||
'operators.')
|
||||
)
|
||||
|
||||
self.comma_first = self.preference.register(
|
||||
'editor', 'comma_first',
|
||||
gettext("Comma-first notation?"), 'boolean', False,
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext('If set to True, comma-first notation for column '
|
||||
'names is used.')
|
||||
)
|
||||
|
||||
self.wrap_after = self.preference.register(
|
||||
'editor', 'wrap_after',
|
||||
gettext("Wrap after N characters"), 'integer', 4,
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext("The column limit (in characters) for wrapping "
|
||||
"comma-separated lists. If zero, it puts "
|
||||
"every item in the list on its own line.")
|
||||
)
|
||||
|
||||
self.tab_size = self.preference.register(
|
||||
'editor', 'tab_size',
|
||||
gettext("Tab size"), 'integer', 4,
|
||||
min_val=2,
|
||||
max_val=8,
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext(
|
||||
'The number of spaces per tab. Minimum 2, maximum 8.'
|
||||
)
|
||||
)
|
||||
|
||||
self.use_spaces = self.preference.register(
|
||||
'editor', 'use_spaces',
|
||||
gettext("Use spaces?"), 'boolean', False,
|
||||
category_label=gettext('SQL formatting'),
|
||||
help_str=gettext(
|
||||
'Specifies whether or not to insert spaces instead of tabs '
|
||||
'when the tab key or auto-indent are used.'
|
||||
)
|
||||
)
|
||||
|
Reference in New Issue
Block a user