mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2026-08-09 04:28:38 -05:00
Fix an issue in query tool where toggle case of selected text loses selection. #7277
Also make changes to give pgAdmin shortcuts higher priority over CodeMirror default shortcuts.
This commit is contained in:
@@ -79,13 +79,85 @@ function onBeforeUnload(e) {
|
||||
e.preventDefault();
|
||||
e.returnValue = 'prevent';
|
||||
}
|
||||
|
||||
const FIXED_PREF = {
|
||||
find: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 70,
|
||||
'char': 'F',
|
||||
},
|
||||
},
|
||||
replace: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': true,
|
||||
'key': {
|
||||
'key_code': 70,
|
||||
'char': 'F',
|
||||
},
|
||||
},
|
||||
gotolinecol: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 76,
|
||||
'char': 'L',
|
||||
},
|
||||
},
|
||||
indent: {
|
||||
'control': false,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 9,
|
||||
'char': 'Tab',
|
||||
},
|
||||
},
|
||||
unindent: {
|
||||
'control': false,
|
||||
'shift': true,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 9,
|
||||
'char': 'Tab',
|
||||
},
|
||||
},
|
||||
comment: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 191,
|
||||
'char': '/',
|
||||
},
|
||||
},
|
||||
format_sql: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 75,
|
||||
'char': 'k',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedNodeInfo, qtPanelDocker, qtPanelId, eventBusObj}) {
|
||||
const containerRef = React.useRef(null);
|
||||
const preferencesStore = usePreferences();
|
||||
const [qtState, _setQtState] = useState({
|
||||
preferences: {
|
||||
browser: preferencesStore.getPreferencesForModule('browser'),
|
||||
sqleditor: preferencesStore.getPreferencesForModule('sqleditor'),
|
||||
sqleditor: {...preferencesStore.getPreferencesForModule('sqleditor'), ...FIXED_PREF},
|
||||
graphs: preferencesStore.getPreferencesForModule('graphs'),
|
||||
misc: preferencesStore.getPreferencesForModule('misc'),
|
||||
},
|
||||
@@ -362,7 +434,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
||||
state => {
|
||||
setQtState({preferences: {
|
||||
browser: state.getPreferencesForModule('browser'),
|
||||
sqleditor: state.getPreferencesForModule('sqleditor'),
|
||||
sqleditor: {...state.getPreferencesForModule('sqleditor'), ...FIXED_PREF},
|
||||
graphs: state.getPreferencesForModule('graphs'),
|
||||
misc: state.getPreferencesForModule('misc'),
|
||||
}});
|
||||
|
||||
@@ -48,87 +48,6 @@ const useStyles = makeStyles((theme)=>({
|
||||
},
|
||||
}));
|
||||
|
||||
const FIXED_PREF = {
|
||||
find: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 70,
|
||||
'char': 'F',
|
||||
},
|
||||
},
|
||||
replace: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': true,
|
||||
'key': {
|
||||
'key_code': 70,
|
||||
'char': 'F',
|
||||
},
|
||||
},
|
||||
gotolinecol: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 76,
|
||||
'char': 'L',
|
||||
},
|
||||
},
|
||||
indent: {
|
||||
'control': false,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 9,
|
||||
'char': 'Tab',
|
||||
},
|
||||
},
|
||||
unindent: {
|
||||
'control': false,
|
||||
'shift': true,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 9,
|
||||
'char': 'Tab',
|
||||
},
|
||||
},
|
||||
comment: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 191,
|
||||
'char': '/',
|
||||
},
|
||||
},
|
||||
uncomment: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 190,
|
||||
'char': '.',
|
||||
},
|
||||
},
|
||||
format_sql: {
|
||||
'control': true,
|
||||
ctrl_is_meta: true,
|
||||
'shift': false,
|
||||
'alt': false,
|
||||
'key': {
|
||||
'key_code': 75,
|
||||
'char': 'k',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function autoCommitRollback(type, api, transId, value) {
|
||||
let url = url_for(`sqleditor.${type}`, {
|
||||
'trans_id': transId,
|
||||
@@ -451,7 +370,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
|
||||
}
|
||||
},
|
||||
{
|
||||
shortcut: FIXED_PREF.format_sql,
|
||||
shortcut: queryToolPref.format_sql,
|
||||
options: {
|
||||
callback: ()=>{formatSQL();}
|
||||
}
|
||||
@@ -596,25 +515,25 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
|
||||
onClose={onMenuClose}
|
||||
label={gettext('Edit Menu')}
|
||||
>
|
||||
<PgMenuItem shortcut={FIXED_PREF.find}
|
||||
<PgMenuItem shortcut={queryToolPref.find}
|
||||
onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, false);}}>{gettext('Find')}</PgMenuItem>
|
||||
<PgMenuItem shortcut={FIXED_PREF.replace}
|
||||
<PgMenuItem shortcut={queryToolPref.replace}
|
||||
onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, true);}}>{gettext('Replace')}</PgMenuItem>
|
||||
<PgMenuItem shortcut={FIXED_PREF.gotolinecol}
|
||||
<PgMenuItem shortcut={queryToolPref.gotolinecol}
|
||||
onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_EXEC_CMD, 'gotoLineCol');}}>{gettext('Go to Line/Column')}</PgMenuItem>
|
||||
<PgMenuDivider />
|
||||
<PgMenuItem shortcut={FIXED_PREF.indent}
|
||||
<PgMenuItem shortcut={queryToolPref.indent}
|
||||
onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_EXEC_CMD, 'indentMore');}}>{gettext('Indent Selection')}</PgMenuItem>
|
||||
<PgMenuItem shortcut={FIXED_PREF.unindent}
|
||||
<PgMenuItem shortcut={queryToolPref.unindent}
|
||||
onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_EXEC_CMD, 'indentLess');}}>{gettext('Unindent Selection')}</PgMenuItem>
|
||||
<PgMenuItem shortcut={FIXED_PREF.comment}
|
||||
<PgMenuItem shortcut={queryToolPref.comment}
|
||||
onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_EXEC_CMD, 'toggleComment');}}>{gettext('Toggle Comment')}</PgMenuItem>
|
||||
<PgMenuItem shortcut={queryToolPref.toggle_case}
|
||||
onClick={toggleCase}>{gettext('Toggle Case Of Selected Text')}</PgMenuItem>
|
||||
<PgMenuItem shortcut={queryToolPref.clear_query}
|
||||
onClick={clearQuery}>{gettext('Clear Query')}</PgMenuItem>
|
||||
<PgMenuDivider />
|
||||
<PgMenuItem shortcut={FIXED_PREF.format_sql}onClick={formatSQL}>{gettext('Format SQL')}</PgMenuItem>
|
||||
<PgMenuItem shortcut={queryToolPref.format_sql}onClick={formatSQL}>{gettext('Format SQL')}</PgMenuItem>
|
||||
</PgMenu>
|
||||
<PgMenu
|
||||
anchorRef={filterMenuRef}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
import React, {useContext, useCallback, useEffect } from 'react';
|
||||
import React, {useContext, useCallback, useEffect, useMemo } from 'react';
|
||||
import { format } from 'sql-formatter';
|
||||
import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
|
||||
import CodeMirror from '../../../../../../static/js/components/ReactCodeMirror';
|
||||
@@ -17,7 +17,7 @@ import { LayoutDockerContext, LAYOUT_EVENTS } from '../../../../../../static/js/
|
||||
import ConfirmSaveContent from '../../../../../../static/js/Dialogs/ConfirmSaveContent';
|
||||
import gettext from 'sources/gettext';
|
||||
import { isMac } from '../../../../../../static/js/keyboard_shortcuts';
|
||||
import { checkTrojanSource } from '../../../../../../static/js/utils';
|
||||
import { checkTrojanSource, isShortcutValue, toCodeMirrorKey } from '../../../../../../static/js/utils';
|
||||
import { parseApiError } from '../../../../../../static/js/api_instance';
|
||||
import { usePgAdmin } from '../../../../../../static/js/BrowserComponent';
|
||||
import ConfirmPromotionContent from '../dialogs/ConfirmPromotionContent';
|
||||
@@ -70,6 +70,8 @@ export default function Query() {
|
||||
const pgAdmin = usePgAdmin();
|
||||
const preferencesStore = usePreferences();
|
||||
|
||||
const queryToolPref = queryToolCtx.preferences.sqleditor;
|
||||
|
||||
const highlightError = (cmObj, {errormsg: result, data})=>{
|
||||
let errorLineNo = 0,
|
||||
startMarker = 0,
|
||||
@@ -255,38 +257,6 @@ export default function Query() {
|
||||
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_QUERY_CHANGE, ()=>{
|
||||
change();
|
||||
});
|
||||
eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_FORMAT_SQL, ()=>{
|
||||
let selection = true, sql = editor.current?.getSelection();
|
||||
let sqlEditorPref = preferencesStore.getPreferencesForModule('sqleditor');
|
||||
/* New library does not support capitalize casing
|
||||
so if a user has set capitalize casing we will
|
||||
use preserve casing which is default for the library.
|
||||
*/
|
||||
let formatPrefs = {
|
||||
language: 'postgresql',
|
||||
keywordCase: sqlEditorPref.keyword_case === 'capitalize' ? 'preserve' : sqlEditorPref.keyword_case,
|
||||
identifierCase: sqlEditorPref.identifier_case === 'capitalize' ? 'preserve' : sqlEditorPref.identifier_case,
|
||||
dataTypeCase: sqlEditorPref.data_type_case,
|
||||
functionCase: sqlEditorPref.function_case,
|
||||
logicalOperatorNewline: sqlEditorPref.logical_operator_new_line,
|
||||
expressionWidth: sqlEditorPref.expression_width,
|
||||
linesBetweenQueries: sqlEditorPref.lines_between_queries,
|
||||
tabWidth: sqlEditorPref.tab_size,
|
||||
useTabs: !sqlEditorPref.use_spaces,
|
||||
denseOperators: !sqlEditorPref.spaces_around_operators,
|
||||
newlineBeforeSemicolon: sqlEditorPref.new_line_before_semicolon
|
||||
};
|
||||
if(sql == '') {
|
||||
sql = editor.current.getValue();
|
||||
selection = false;
|
||||
}
|
||||
let formattedSql = format(sql,formatPrefs);
|
||||
if(selection) {
|
||||
editor.current.replaceSelection(formattedSql, 'around');
|
||||
} else {
|
||||
editor.current.setValue(formattedSql);
|
||||
}
|
||||
});
|
||||
eventBus.registerListener(QUERY_TOOL_EVENTS.EDITOR_TOGGLE_CASE, ()=>{
|
||||
let selectedText = editor.current?.getSelection();
|
||||
if (!selectedText) return;
|
||||
@@ -334,7 +304,46 @@ export default function Query() {
|
||||
/>
|
||||
));
|
||||
};
|
||||
return eventBus.registerListener(QUERY_TOOL_EVENTS.WARN_SAVE_TEXT_CLOSE, warnSaveTextClose);
|
||||
|
||||
const formatSQL = ()=>{
|
||||
let selection = true, sql = editor.current?.getSelection();
|
||||
/* New library does not support capitalize casing
|
||||
so if a user has set capitalize casing we will
|
||||
use preserve casing which is default for the library.
|
||||
*/
|
||||
let formatPrefs = {
|
||||
language: 'postgresql',
|
||||
keywordCase: queryToolPref.keyword_case === 'capitalize' ? 'preserve' : queryToolPref.keyword_case,
|
||||
identifierCase: queryToolPref.identifier_case === 'capitalize' ? 'preserve' : queryToolPref.identifier_case,
|
||||
dataTypeCase: queryToolPref.data_type_case,
|
||||
functionCase: queryToolPref.function_case,
|
||||
logicalOperatorNewline: queryToolPref.logical_operator_new_line,
|
||||
expressionWidth: queryToolPref.expression_width,
|
||||
linesBetweenQueries: queryToolPref.lines_between_queries,
|
||||
tabWidth: queryToolPref.tab_size,
|
||||
useTabs: !queryToolPref.use_spaces,
|
||||
denseOperators: !queryToolPref.spaces_around_operators,
|
||||
newlineBeforeSemicolon: queryToolPref.new_line_before_semicolon
|
||||
};
|
||||
if(sql == '') {
|
||||
sql = editor.current.getValue();
|
||||
selection = false;
|
||||
}
|
||||
let formattedSql = format(sql,formatPrefs);
|
||||
if(selection) {
|
||||
editor.current.replaceSelection(formattedSql, 'around');
|
||||
} else {
|
||||
editor.current.setValue(formattedSql);
|
||||
}
|
||||
};
|
||||
|
||||
const unregisterFormatSQL = eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_FORMAT_SQL, formatSQL);
|
||||
const unregisterWarn = eventBus.registerListener(QUERY_TOOL_EVENTS.WARN_SAVE_TEXT_CLOSE, warnSaveTextClose);
|
||||
|
||||
return ()=>{
|
||||
unregisterFormatSQL();
|
||||
unregisterWarn();
|
||||
};
|
||||
}, [queryToolCtx.preferences]);
|
||||
|
||||
useEffect(()=>{
|
||||
@@ -400,6 +409,33 @@ export default function Query() {
|
||||
}
|
||||
};
|
||||
|
||||
const shortcutOverrideKeys = useMemo(
|
||||
()=>{
|
||||
const queryToolPref = queryToolCtx.preferences.sqleditor;
|
||||
return Object.values(queryToolPref)
|
||||
.filter((p)=>isShortcutValue(p))
|
||||
.map((p)=>({
|
||||
key: toCodeMirrorKey(p), run: (_v, e)=>{
|
||||
queryToolCtx.mainContainerRef?.current?.dispatchEvent(new KeyboardEvent('keydown', {
|
||||
which: e.which,
|
||||
keyCode: e.keyCode,
|
||||
altKey: e.altKey,
|
||||
shiftKey: e.shiftKey,
|
||||
ctrlKey: e.ctrlKey,
|
||||
metaKey: e.metaKey,
|
||||
}));
|
||||
if(toCodeMirrorKey(p) == 'Mod-k') {
|
||||
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_FORMAT_SQL);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
preventDefault: true,
|
||||
stopPropagation: true,
|
||||
}));
|
||||
},
|
||||
[queryToolCtx.preferences]
|
||||
);
|
||||
|
||||
return <CodeMirror
|
||||
currEditor={(obj)=>{
|
||||
editor.current=obj;
|
||||
@@ -409,14 +445,6 @@ export default function Query() {
|
||||
onCursorActivity={cursorActivity}
|
||||
onChange={change}
|
||||
autocomplete={true}
|
||||
customKeyMap={[
|
||||
{
|
||||
key: 'Mod-k', run: (_view, e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_FORMAT_SQL);
|
||||
},
|
||||
}
|
||||
]}
|
||||
customKeyMap={shortcutOverrideKeys}
|
||||
/>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user