mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Add an option to auto-complete keywords in upper case. Fixes #2686
This commit is contained in:
parent
df7b4d55c6
commit
b48145f01f
BIN
docs/en_US/images/preferences_sql_auto_completion.png
Normal file
BIN
docs/en_US/images/preferences_sql_auto_completion.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 129 KiB |
@ -127,6 +127,13 @@ Please note: the default help paths include the *VERSION* placeholder; the $VERS
|
|||||||
|
|
||||||
Expand the *SQL Editor* node to access panels that allow you to specify your preferences for the SQL Editor tool.
|
Expand the *SQL Editor* node to access panels that allow you to specify your preferences for the SQL Editor tool.
|
||||||
|
|
||||||
|
.. image:: images/preferences_sql_auto_completion.png
|
||||||
|
:alt: Preferences dialog sqleditor auto completion option
|
||||||
|
|
||||||
|
Use the fields on the *Auto Completion* panel to set the auto completion options.
|
||||||
|
|
||||||
|
* When the *Keywords in uppercase* switch is set to *True* then keywords are shown in upper case.
|
||||||
|
|
||||||
.. image:: images/preferences_sql_csv_output.png
|
.. image:: images/preferences_sql_csv_output.png
|
||||||
:alt: Preferences dialog sqleditor csv output option
|
:alt: Preferences dialog sqleditor csv output option
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ Features
|
|||||||
********
|
********
|
||||||
|
|
||||||
| `Feature #1447 <https://redmine.postgresql.org/issues/1447>`_ - Add support for SSH tunneled connections
|
| `Feature #1447 <https://redmine.postgresql.org/issues/1447>`_ - Add support for SSH tunneled connections
|
||||||
|
| `Feature #2686 <https://redmine.postgresql.org/issues/2686>`_ - Add an option to auto-complete keywords in upper case
|
||||||
| `Feature #3204 <https://redmine.postgresql.org/issues/3204>`_ - Add support for LISTEN/NOTIFY in the query tool
|
| `Feature #3204 <https://redmine.postgresql.org/issues/3204>`_ - Add support for LISTEN/NOTIFY in the query tool
|
||||||
| `Feature #3362 <https://redmine.postgresql.org/issues/3362>`_ - Function and procedure support for PG11
|
| `Feature #3362 <https://redmine.postgresql.org/issues/3362>`_ - Function and procedure support for PG11
|
||||||
|
|
||||||
@ -36,5 +37,5 @@ Bug fixes
|
|||||||
| `Bug #3342 <https://redmine.postgresql.org/issues/3342>`_ - Set SESSION_COOKIE_SAMESITE='Lax' per Flask recommendation to prevents sending cookies with CSRF-prone requests from external sites, such as submitting a form
|
| `Bug #3342 <https://redmine.postgresql.org/issues/3342>`_ - Set SESSION_COOKIE_SAMESITE='Lax' per Flask recommendation to prevents sending cookies with CSRF-prone requests from external sites, such as submitting a form
|
||||||
| `Bug #3353 <https://redmine.postgresql.org/issues/3353>`_ - Handle errors properly if they occur when renaming a database
|
| `Bug #3353 <https://redmine.postgresql.org/issues/3353>`_ - Handle errors properly if they occur when renaming a database
|
||||||
| `Bug #3374 <https://redmine.postgresql.org/issues/3374>`_ - Fix autocomplete
|
| `Bug #3374 <https://redmine.postgresql.org/issues/3374>`_ - Fix autocomplete
|
||||||
| `Bug #3392 <https://redmine.postgresql.org/issues/3392>`_ - Fix IPv6 support in the container build.
|
| `Bug #3392 <https://redmine.postgresql.org/issues/3392>`_ - Fix IPv6 support in the container build
|
||||||
| `Bug #3409 <https://redmine.postgresql.org/issues/3409>`_ - Avoid an exception on GreenPlum when retrieving RE-SQL on a table
|
| `Bug #3409 <https://redmine.postgresql.org/issues/3409>`_ - Avoid an exception on GreenPlum when retrieving RE-SQL on a table
|
@ -1,2 +1,7 @@
|
|||||||
{# SQL query for getting keywords #}
|
{# SQL query for getting keywords #}
|
||||||
|
{% if upper_case %}
|
||||||
SELECT upper(word) as word FROM pg_get_keywords()
|
SELECT upper(word) as word FROM pg_get_keywords()
|
||||||
|
{% else %}
|
||||||
|
SELECT word FROM pg_get_keywords()
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
@ -136,6 +136,7 @@ function keyboardShortcutsQueryTool(
|
|||||||
let downloadCsvKeys = keyboardShortcutConfig['download_csv'];
|
let downloadCsvKeys = keyboardShortcutConfig['download_csv'];
|
||||||
let nextPanelKeys = keyboardShortcutConfig['move_next'];
|
let nextPanelKeys = keyboardShortcutConfig['move_next'];
|
||||||
let previousPanelKeys = keyboardShortcutConfig['move_previous'];
|
let previousPanelKeys = keyboardShortcutConfig['move_previous'];
|
||||||
|
let toggleCaseKeys = keyboardShortcutConfig['toggle_case'];
|
||||||
|
|
||||||
if (this.validateShortcutKeys(executeKeys, event)) {
|
if (this.validateShortcutKeys(executeKeys, event)) {
|
||||||
this._stopEventPropagation(event);
|
this._stopEventPropagation(event);
|
||||||
@ -149,6 +150,9 @@ function keyboardShortcutsQueryTool(
|
|||||||
} else if (this.validateShortcutKeys(downloadCsvKeys, event)) {
|
} else if (this.validateShortcutKeys(downloadCsvKeys, event)) {
|
||||||
this._stopEventPropagation(event);
|
this._stopEventPropagation(event);
|
||||||
queryToolActions.download(sqlEditorController);
|
queryToolActions.download(sqlEditorController);
|
||||||
|
} else if (this.validateShortcutKeys(toggleCaseKeys, event)) {
|
||||||
|
this._stopEventPropagation(event);
|
||||||
|
queryToolActions.toggleCaseOfSelectedText(sqlEditorController);
|
||||||
} else if ((
|
} else if ((
|
||||||
(this.isMac() && event.metaKey) ||
|
(this.isMac() && event.metaKey) ||
|
||||||
(!this.isMac() && event.ctrlKey)
|
(!this.isMac() && event.ctrlKey)
|
||||||
|
@ -132,6 +132,8 @@ let queryToolActions = {
|
|||||||
'sqleditor', 'move_next');
|
'sqleditor', 'move_next');
|
||||||
let previousPanelPerf = window.top.pgAdmin.Browser.get_preference(
|
let previousPanelPerf = window.top.pgAdmin.Browser.get_preference(
|
||||||
'sqleditor', 'move_previous');
|
'sqleditor', 'move_previous');
|
||||||
|
let toggleCasePerf = window.top.pgAdmin.Browser.get_preference(
|
||||||
|
'sqleditor', 'toggle_case');
|
||||||
|
|
||||||
if(!executeQueryPref && sqlEditorController.handler.is_new_browser_tab) {
|
if(!executeQueryPref && sqlEditorController.handler.is_new_browser_tab) {
|
||||||
executeQueryPref = window.opener.pgAdmin.Browser.get_preference(
|
executeQueryPref = window.opener.pgAdmin.Browser.get_preference(
|
||||||
@ -151,6 +153,9 @@ let queryToolActions = {
|
|||||||
),
|
),
|
||||||
previousPanelPerf = window.opener.pgAdmin.Browser.get_preference(
|
previousPanelPerf = window.opener.pgAdmin.Browser.get_preference(
|
||||||
'sqleditor', 'move_previous'
|
'sqleditor', 'move_previous'
|
||||||
|
),
|
||||||
|
toggleCasePerf = window.opener.pgAdmin.Browser.get_preference(
|
||||||
|
'sqleditor', 'toggle_case'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,9 +166,23 @@ let queryToolActions = {
|
|||||||
'download_csv': downloadCsvPref.value,
|
'download_csv': downloadCsvPref.value,
|
||||||
'move_next': nextPanelPerf.value,
|
'move_next': nextPanelPerf.value,
|
||||||
'move_previous': previousPanelPerf.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;
|
module.exports = queryToolActions;
|
||||||
|
@ -535,6 +535,32 @@ def RegisterQueryToolPreferences(self):
|
|||||||
fields=accesskey_fields
|
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():
|
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_query = qt_perf.preference('explain_query').get()
|
||||||
explain_analyze_query = qt_perf.preference('explain_analyze_query').get()
|
explain_analyze_query = qt_perf.preference('explain_analyze_query').get()
|
||||||
find_options = qt_perf.preference('btn_find_options').get()
|
find_options = qt_perf.preference('btn_find_options').get()
|
||||||
|
toggle_case = qt_perf.preference('toggle_case').get()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'keys': {
|
'keys': {
|
||||||
@ -587,7 +614,8 @@ def get_query_tool_keyboard_shortcuts():
|
|||||||
'explain_analyze_query': explain_analyze_query.get('key').get(
|
'explain_analyze_query': explain_analyze_query.get('key').get(
|
||||||
'char'
|
'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': {
|
'shortcuts': {
|
||||||
'conn_status': conn_status,
|
'conn_status': conn_status,
|
||||||
@ -608,7 +636,8 @@ def get_query_tool_keyboard_shortcuts():
|
|||||||
'execute_query': execute_query,
|
'execute_query': execute_query,
|
||||||
'explain_query': explain_query,
|
'explain_query': explain_query,
|
||||||
'explain_analyze_query': explain_analyze_query,
|
'explain_analyze_query': explain_analyze_query,
|
||||||
'find_options': find_options
|
'find_options': find_options,
|
||||||
|
'toggle_case': toggle_case
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ from .function_metadata import FunctionMetadata
|
|||||||
from .parseutils import (
|
from .parseutils import (
|
||||||
last_word, extract_tables, find_prev_keyword, parse_partial_identifier)
|
last_word, extract_tables, find_prev_keyword, parse_partial_identifier)
|
||||||
from .prioritization import PrevalenceCounter
|
from .prioritization import PrevalenceCounter
|
||||||
|
from pgadmin.utils.preferences import Preferences
|
||||||
|
|
||||||
PY2 = sys.version_info[0] == 2
|
PY2 = sys.version_info[0] == 2
|
||||||
PY3 = sys.version_info[0] == 3
|
PY3 = sys.version_info[0] == 3
|
||||||
@ -101,8 +102,17 @@ class SQLAutoComplete(object):
|
|||||||
for record in res['rows']:
|
for record in res['rows']:
|
||||||
self.search_path.append(record['schema'])
|
self.search_path.append(record['schema'])
|
||||||
|
|
||||||
|
pref = Preferences.module('sqleditor')
|
||||||
|
keywords_in_uppercase = \
|
||||||
|
pref.preference('keywords_in_uppercase').get()
|
||||||
|
|
||||||
# Fetch the keywords
|
# Fetch the keywords
|
||||||
query = render_template("/".join([self.sql_path, 'keywords.sql']))
|
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)
|
status, res = self.conn.execute_dict(query)
|
||||||
if status:
|
if status:
|
||||||
for record in res['rows']:
|
for record in res['rows']:
|
||||||
|
@ -11,7 +11,8 @@ import queryToolActions from 'sources/sqleditor/query_tool_actions';
|
|||||||
describe('queryToolActions', () => {
|
describe('queryToolActions', () => {
|
||||||
let sqlEditorController,
|
let sqlEditorController,
|
||||||
getSelectionSpy, getValueSpy,
|
getSelectionSpy, getValueSpy,
|
||||||
selectedQueryString, entireQueryString;
|
selectedQueryString, entireQueryString,
|
||||||
|
replaceSelectionSpy;
|
||||||
|
|
||||||
describe('executeQuery', () => {
|
describe('executeQuery', () => {
|
||||||
describe('when the command is being run from the query tool', () => {
|
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) {
|
function setUpSpies(selectedQueryString, entireQueryString) {
|
||||||
getValueSpy = jasmine.createSpy('getValueSpy').and.returnValue(entireQueryString);
|
getValueSpy = jasmine.createSpy('getValueSpy').and.returnValue(entireQueryString);
|
||||||
getSelectionSpy = jasmine.createSpy('getSelectionSpy').and.returnValue(selectedQueryString);
|
getSelectionSpy = jasmine.createSpy('getSelectionSpy').and.returnValue(selectedQueryString);
|
||||||
|
replaceSelectionSpy = jasmine.createSpy('replaceSelectionSpy');
|
||||||
|
|
||||||
sqlEditorController = {
|
sqlEditorController = {
|
||||||
gridView: {
|
gridView: {
|
||||||
@ -449,6 +534,7 @@ describe('queryToolActions', () => {
|
|||||||
toggleComment: jasmine.createSpy('toggleCommentSpy'),
|
toggleComment: jasmine.createSpy('toggleCommentSpy'),
|
||||||
lineComment: jasmine.createSpy('lineCommentSpy'),
|
lineComment: jasmine.createSpy('lineCommentSpy'),
|
||||||
uncomment: jasmine.createSpy('uncommentSpy'),
|
uncomment: jasmine.createSpy('uncommentSpy'),
|
||||||
|
replaceSelection: replaceSelectionSpy,
|
||||||
getCursor: (isFrom) => {
|
getCursor: (isFrom) => {
|
||||||
return entireQueryString.indexOf(selectedQueryString) + (isFrom ? 0 : selectedQueryString.length);
|
return entireQueryString.indexOf(selectedQueryString) + (isFrom ? 0 : selectedQueryString.length);
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user