diff --git a/web/pgadmin/misc/templates/sqlautocomplete/sql/default/keywords.sql b/web/pgadmin/misc/templates/sqlautocomplete/sql/default/keywords.sql index cd571e1..9f762b8 100644 --- a/web/pgadmin/misc/templates/sqlautocomplete/sql/default/keywords.sql +++ b/web/pgadmin/misc/templates/sqlautocomplete/sql/default/keywords.sql @@ -1,2 +1,7 @@ {# SQL query for getting keywords #} +{% if upper_case %} SELECT upper(word) as word FROM pg_get_keywords() +{% else %} +SELECT word FROM pg_get_keywords() +{% endif %} + diff --git a/web/pgadmin/static/js/keyboard_shortcuts.js b/web/pgadmin/static/js/keyboard_shortcuts.js index 996a591..1fe07e9 100644 --- a/web/pgadmin/static/js/keyboard_shortcuts.js +++ b/web/pgadmin/static/js/keyboard_shortcuts.js @@ -136,6 +136,7 @@ function keyboardShortcutsQueryTool( let downloadCsvKeys = keyboardShortcutConfig['download_csv']; let nextPanelKeys = keyboardShortcutConfig['move_next']; let previousPanelKeys = keyboardShortcutConfig['move_previous']; + let toggleCaseKeys = keyboardShortcutConfig['toggle_case']; if (this.validateShortcutKeys(executeKeys, event)) { this._stopEventPropagation(event); @@ -149,6 +150,9 @@ function keyboardShortcutsQueryTool( } else if (this.validateShortcutKeys(downloadCsvKeys, event)) { this._stopEventPropagation(event); queryToolActions.download(sqlEditorController); + } else if (this.validateShortcutKeys(toggleCaseKeys, event)) { + this._stopEventPropagation(event); + queryToolActions.toggleCaseOfSelectedText(sqlEditorController); } else if (( (this.isMac() && event.metaKey) || (!this.isMac() && event.ctrlKey) diff --git a/web/pgadmin/static/js/sqleditor/query_tool_actions.js b/web/pgadmin/static/js/sqleditor/query_tool_actions.js index 9e8e450..411c6ed 100644 --- a/web/pgadmin/static/js/sqleditor/query_tool_actions.js +++ b/web/pgadmin/static/js/sqleditor/query_tool_actions.js @@ -132,6 +132,8 @@ let queryToolActions = { 'sqleditor', 'move_next'); let previousPanelPerf = window.top.pgAdmin.Browser.get_preference( 'sqleditor', 'move_previous'); + let toggleCasePerf = window.top.pgAdmin.Browser.get_preference( + 'sqleditor', 'toggle_case'); if(!executeQueryPref && sqlEditorController.handler.is_new_browser_tab) { executeQueryPref = window.opener.pgAdmin.Browser.get_preference( @@ -151,6 +153,9 @@ let queryToolActions = { ), previousPanelPerf = window.opener.pgAdmin.Browser.get_preference( 'sqleditor', 'move_previous' + ), + toggleCasePerf = window.opener.pgAdmin.Browser.get_preference( + 'sqleditor', 'toggle_case' ); } @@ -161,9 +166,23 @@ let queryToolActions = { 'download_csv': downloadCsvPref.value, 'move_next': nextPanelPerf.value, 'move_previous': previousPanelPerf.value, + 'toggle_case': toggleCasePerf.value, }; }, + + toggleCaseOfSelectedText: function (sqlEditorController) { + let codeMirrorObj = sqlEditorController.gridView.query_tool_obj; + let selectedText = codeMirrorObj.getSelection(); + + if (!selectedText) return; + + if (selectedText === selectedText.toUpperCase()) { + codeMirrorObj.replaceSelection(selectedText.toLowerCase()); + } else { + codeMirrorObj.replaceSelection(selectedText.toUpperCase()); + } + }, }; module.exports = queryToolActions; diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py index 7c43d9b..a5a7bcb 100644 --- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py +++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py @@ -535,6 +535,32 @@ def RegisterQueryToolPreferences(self): fields=accesskey_fields ) + self.preference.register( + 'keyboard_shortcuts', + 'toggle_case', + gettext('Toggle case of selected text'), + 'keyboardshortcut', + { + 'alt': False, + 'shift': True, + 'control': True, + 'key': { + 'key_code': 91, + 'char': 'u' + } + }, + category_label=gettext('Keyboard shortcuts'), + fields=shortcut_fields + ) + + self.preference.register( + 'auto_completion', 'keywords_in_uppercase', + gettext("Keywords in uppercase"), 'boolean', True, + category_label=gettext('Auto completion'), + help_str=gettext('If set to True, Keywords will be displayed ' + 'in upper case for auto completion.') + ) + def get_query_tool_keyboard_shortcuts(): @@ -564,6 +590,7 @@ def get_query_tool_keyboard_shortcuts(): explain_query = qt_perf.preference('explain_query').get() explain_analyze_query = qt_perf.preference('explain_analyze_query').get() find_options = qt_perf.preference('btn_find_options').get() + toggle_case = qt_perf.preference('toggle_case').get() return { 'keys': { @@ -587,7 +614,8 @@ def get_query_tool_keyboard_shortcuts(): 'explain_analyze_query': explain_analyze_query.get('key').get( 'char' ), - 'find_options': find_options.get('key').get('char') + 'find_options': find_options.get('key').get('char'), + 'toggle_case': toggle_case.get('key').get('char') }, 'shortcuts': { 'conn_status': conn_status, @@ -608,7 +636,8 @@ def get_query_tool_keyboard_shortcuts(): 'execute_query': execute_query, 'explain_query': explain_query, 'explain_analyze_query': explain_analyze_query, - 'find_options': find_options + 'find_options': find_options, + 'toggle_case': toggle_case }, } diff --git a/web/pgadmin/utils/sqlautocomplete/autocomplete.py b/web/pgadmin/utils/sqlautocomplete/autocomplete.py index af5514b..8a25c13 100644 --- a/web/pgadmin/utils/sqlautocomplete/autocomplete.py +++ b/web/pgadmin/utils/sqlautocomplete/autocomplete.py @@ -26,6 +26,7 @@ from .function_metadata import FunctionMetadata from .parseutils import ( last_word, extract_tables, find_prev_keyword, parse_partial_identifier) from .prioritization import PrevalenceCounter +from pgadmin.utils.preferences import Preferences PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -101,8 +102,17 @@ class SQLAutoComplete(object): for record in res['rows']: self.search_path.append(record['schema']) + pref = Preferences.module('sqleditor') + keywords_in_uppercase = \ + pref.preference('keywords_in_uppercase').get() + # Fetch the keywords query = render_template("/".join([self.sql_path, 'keywords.sql'])) + # If setting 'Keywords in uppercase' is set to True in + # Preferences then fetch the keywords in upper case. + if keywords_in_uppercase: + query = render_template( + "/".join([self.sql_path, 'keywords.sql']), upper_case=True) status, res = self.conn.execute_dict(query) if status: for record in res['rows']: diff --git a/web/regression/javascript/sqleditor/query_tool_actions_spec.js b/web/regression/javascript/sqleditor/query_tool_actions_spec.js index f3ac781..61c1839 100644 --- a/web/regression/javascript/sqleditor/query_tool_actions_spec.js +++ b/web/regression/javascript/sqleditor/query_tool_actions_spec.js @@ -11,7 +11,8 @@ import queryToolActions from 'sources/sqleditor/query_tool_actions'; describe('queryToolActions', () => { let sqlEditorController, getSelectionSpy, getValueSpy, - selectedQueryString, entireQueryString; + selectedQueryString, entireQueryString, + replaceSelectionSpy; describe('executeQuery', () => { describe('when the command is being run from the query tool', () => { @@ -437,9 +438,93 @@ describe('queryToolActions', () => { }); }); + describe('toggleCaseOfSelectedText', () => { + describe('when there is no query text', () => { + beforeEach(() => { + setUpSpies('', ''); + }); + it('does nothing', () => { + expect( + queryToolActions.toggleCaseOfSelectedText(sqlEditorController) + ).not.toBeDefined(); + }); + }); + + describe('when there is empty selection', () => { + beforeEach(() => { + setUpSpies('', 'a string\nddd\nsss'); + + sqlEditorController.gridView.query_tool_obj.getCursor = (isFrom) => { + return isFrom ? 3 : 3; + }; + }); + + it('does nothing', () => { + expect( + queryToolActions.toggleCaseOfSelectedText(sqlEditorController) + ).not.toBeDefined(); + }); + }); + + describe('when selected query is in lower case', () => { + beforeEach(() => { + setUpSpies('string', 'a string\nddd\nsss'); + }); + + it('toggle the selection and string should be in upper case', () => { + queryToolActions.toggleCaseOfSelectedText(sqlEditorController); + expect(replaceSelectionSpy + ).toHaveBeenCalledWith('STRING'); + }); + + it('(negative scenario toggle the selection and string should be in upper case', () => { + queryToolActions.toggleCaseOfSelectedText(sqlEditorController); + expect(replaceSelectionSpy + ).not.toHaveBeenCalledWith('string'); + }); + }); + + describe('when selected query is in upper case', () => { + beforeEach(() => { + setUpSpies('STRING', 'a string\nddd\nsss'); + }); + + it('toggle the selection and string should be in lower case', () => { + queryToolActions.toggleCaseOfSelectedText(sqlEditorController); + expect(replaceSelectionSpy + ).toHaveBeenCalledWith('string'); + }); + + it('(negative scenario toggle the selection and string should be in lower case', () => { + queryToolActions.toggleCaseOfSelectedText(sqlEditorController); + expect(replaceSelectionSpy + ).not.toHaveBeenCalledWith('STRING'); + }); + }); + + describe('when selected query is in mixed case', () => { + beforeEach(() => { + setUpSpies('sTRIng', 'a string\nddd\nsss'); + }); + + it('toggle the selection and string should be in upper case', () => { + queryToolActions.toggleCaseOfSelectedText(sqlEditorController); + expect(replaceSelectionSpy + ).toHaveBeenCalledWith('STRING'); + }); + + it('(negative scenario toggle the selection and string should be in upper case', () => { + queryToolActions.toggleCaseOfSelectedText(sqlEditorController); + expect(replaceSelectionSpy + ).not.toHaveBeenCalledWith('sTRIng'); + }); + }); + }); + function setUpSpies(selectedQueryString, entireQueryString) { getValueSpy = jasmine.createSpy('getValueSpy').and.returnValue(entireQueryString); getSelectionSpy = jasmine.createSpy('getSelectionSpy').and.returnValue(selectedQueryString); + replaceSelectionSpy = jasmine.createSpy('replaceSelectionSpy'); sqlEditorController = { gridView: { @@ -449,6 +534,7 @@ describe('queryToolActions', () => { toggleComment: jasmine.createSpy('toggleCommentSpy'), lineComment: jasmine.createSpy('lineCommentSpy'), uncomment: jasmine.createSpy('uncommentSpy'), + replaceSelection: replaceSelectionSpy, getCursor: (isFrom) => { return entireQueryString.indexOf(selectedQueryString) + (isFrom ? 0 : selectedQueryString.length); },