Skip to content

Commit

Permalink
Added feature to auto detect the OS end-of-line (EOL) character.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rohit Bhati committed Sep 24, 2024
1 parent f451f89 commit 246dd8a
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 21 deletions.
Binary file modified docs/en_US/images/query_status_bar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion docs/en_US/query_tool_toolbar.rst
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ The status bar shows the following information:
* **Total rows**: The total number of rows returned by the query.
* **Query complete**: The time is taken by the query to complete.
* **Rows selected**: The number of rows selected in the data output panel.
* **Changes staged**: This information showed the number of rows added, deleted, and updated.
* **Changes staged**: This information shows the number of rows added, deleted, and updated.
* **LF/CRLF**: It shows the end of line sequence to be used for the editor. When opening an empty editor, it will be decided based on OS.
And when opening an existing file, it will be based on file end of lines. One can change the EOL by clicking on any of the options.
* **Ln**: In the Query tab, it is the line number at which the cursor is positioned.
* **Col**: In the Query tab, it is the column number at which the cursor is positioned
3 changes: 2 additions & 1 deletion web/pgadmin/static/js/components/Menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ export function usePgMenuGroup() {
const prevMenuOpenIdRef = useRef(null);

const toggleMenu = React.useCallback((e)=>{
const name = e.currentTarget?.getAttribute('name') || e.currentTarget?.name;
setOpenMenuName(()=>{
return prevMenuOpenIdRef.current == e.currentTarget?.name ? null : e.currentTarget?.name;
return prevMenuOpenIdRef.current == name ? null : name;
});
prevMenuOpenIdRef.current = null;
}, []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { errorMarkerEffect } from './extensions/errorMarker';
import { currentQueryHighlighterEffect } from './extensions/currentQueryHighlighter';
import { activeLineEffect, activeLineField } from './extensions/activeLineMarker';
import { clearBreakpoints, hasBreakpoint, toggleBreakpoint } from './extensions/breakpointGutter';
import { autoCompleteCompartment } from './extensions/extraStates';
import { autoCompleteCompartment, eol, eolCompartment } from './extensions/extraStates';


function getAutocompLoading({ bottom, left }, dom) {
Expand All @@ -30,11 +30,13 @@ export default class CustomEditorView extends EditorView {
this._cleanDoc = this.state.doc;
}

getValue(tillCursor=false) {
getValue(tillCursor=false, useLineSep=false) {
if(tillCursor) {
return this.state.sliceDoc(0, this.state.selection.main.head);
} else if (useLineSep) {
return this.state.doc.sliceString(0, this.state.doc.length, this.getEOL());
}
return this.state.doc.toString();
return this.state.sliceDoc();
}

/* Function to extract query based on position passed */
Expand Down Expand Up @@ -328,4 +330,14 @@ export default class CustomEditorView extends EditorView {
setQueryHighlightMark(from,to) {
this.dispatch({ effects: currentQueryHighlighterEffect.of({ from, to }) });
}

getEOL(){
return this.state.facet(eol);
}

setEOL(val){
this.dispatch({
effects: eolCompartment.reconfigure(eol.of(val))
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ import CustomEditorView from '../CustomEditorView';
import breakpointGutter, { breakpointEffect } from '../extensions/breakpointGutter';
import activeLineExtn from '../extensions/activeLineMarker';
import currentQueryHighlighterExtn from '../extensions/currentQueryHighlighter';
import { autoCompleteCompartment, indentNewLine } from '../extensions/extraStates';
import { autoCompleteCompartment, eolCompartment, indentNewLine, eol } from '../extensions/extraStates';
import { OS_EOL } from '../../../../../tools/sqleditor/static/js/components/QueryToolConstants';

const arrowRightHtml = ReactDOMServer.renderToString(<KeyboardArrowRightRoundedIcon style={{width: '16px'}} />);
const arrowDownHtml = ReactDOMServer.renderToString(<ExpandMoreRoundedIcon style={{width: '16px'}} />);
Expand Down Expand Up @@ -144,6 +145,10 @@ const defaultExtensions = [
return 0;
}),
autoCompleteCompartment.of([]),
EditorView.clipboardOutputFilter.of((text, state)=>{
const lineSep = state.facet(eol);
return state.doc.sliceString(0, text.length, lineSep);
})
];

export default function Editor({
Expand All @@ -167,6 +172,7 @@ export default function Editor({

useEffect(() => {
const finalOptions = { ...defaultOptions, ...options };
const osEOL = OS_EOL === 'crlf' ? '\r\n' : '\n';
const finalExtns = [
(language == 'json') ? json() : sql({dialect: PgSQL}),
...defaultExtensions,
Expand All @@ -191,6 +197,7 @@ export default function Editor({
const state = EditorState.create({
extensions: [
...finalExtns,
eolCompartment.of([eol.of(osEOL)]),
shortcuts.current.of([]),
configurables.current.of([]),
editableConfig.current.of([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,10 @@ export const indentNewLine = Facet.define({
combine: values => values.length ? values[0] : true,
});

export const eol = Facet.define({
combine: values => values.length ? values[0] : '\n',
});

export const autoCompleteCompartment = new Compartment();
export const eolCompartment = new Compartment();

Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { MainToolBar } from './sections/MainToolBar';
import { Messages } from './sections/Messages';
import getApiInstance, {callFetch, parseApiError} from '../../../../../static/js/api_instance';
import url_for from 'sources/url_for';
import { PANELS, QUERY_TOOL_EVENTS, CONNECTION_STATUS, MAX_QUERY_LENGTH } from './QueryToolConstants';
import { PANELS, QUERY_TOOL_EVENTS, CONNECTION_STATUS, MAX_QUERY_LENGTH, OS_EOL } from './QueryToolConstants';
import { useBeforeUnload, useInterval } from '../../../../../static/js/custom_hooks';
import { Box } from '@mui/material';
import { getDatabaseLabel, getTitle, setQueryToolDockerTitle } from '../sqleditor_title';
Expand Down Expand Up @@ -202,7 +202,8 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
database_name: _.unescape(params.database_name) || getDatabaseLabel(selectedNodeInfo),
is_selected: true,
}],
editor_disabled:true
editor_disabled:true,
eol:OS_EOL
});
const [selectedText, setSelectedText] = useState('');

Expand All @@ -224,6 +225,14 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|| !qtState.is_visible) {
pollTime = -1;
}

const handleEndOfLineChange = useCallback((e)=>{
const val = e.value || e;
const lineSep = val === 'crlf' ? '\r\n' : '\n';
setQtStatePartial({ eol: val });
eventBus.current.fireEvent(QUERY_TOOL_EVENTS.CHANGE_EOL, lineSep);
}, []);

useInterval(async ()=>{
try {
let {data: respData} = await fetchConnectionStatus(api, qtState.params.trans_id);
Expand Down Expand Up @@ -262,7 +271,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
{
maximizable: true,
tabs: [
LayoutDocker.getPanel({id: PANELS.QUERY, title: gettext('Query'), content: <Query onTextSelect={(text) => setSelectedText(text)}/>}),
LayoutDocker.getPanel({id: PANELS.QUERY, title: gettext('Query'), content: <Query onTextSelect={(text) => setSelectedText(text)} handleEndOfLineChange={handleEndOfLineChange}/>}),
LayoutDocker.getPanel({id: PANELS.HISTORY, title: gettext('Query History'), content: <QueryHistory />,
cached: undefined}),
],
Expand Down Expand Up @@ -869,6 +878,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
preferences: qtState.preferences,
mainContainerRef: containerRef,
editor_disabled: qtState.editor_disabled,
eol: qtState.eol,
toggleQueryTool: () => setQtStatePartial((prev)=>{
return {
...prev,
Expand Down Expand Up @@ -899,7 +909,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
};
});
},
}), [qtState.params, qtState.preferences, containerRef.current, qtState.editor_disabled]);
}), [qtState.params, qtState.preferences, containerRef.current, qtState.editor_disabled, qtState.eol]);

const queryToolConnContextValue = React.useMemo(()=>({
connected: qtState.connected,
Expand Down Expand Up @@ -940,7 +950,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
savedLayout={params.layout}
resetToTabPanel={PANELS.MESSAGES}
/>
<StatusBar />
<StatusBar eol={qtState.eol} handleEndOfLineChange={handleEndOfLineChange}/>
</Box>
</QueryToolEventsContext.Provider>
</QueryToolConnectionContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export const QUERY_TOOL_EVENTS = {
RESET_GRAPH_VISUALISER: 'RESET_GRAPH_VISUALISER',

GOTO_LAST_SCROLL: 'GOTO_LAST_SCROLL',
CHANGE_EOL: 'CHANGE_EOL'
};

export const CONNECTION_STATUS = {
Expand Down Expand Up @@ -105,4 +106,6 @@ export const PANELS = {
GRAPH_VISUALISER: 'id-graph-visualiser',
};

export const MAX_QUERY_LENGTH = 1000000;
export const MAX_QUERY_LENGTH = 1000000;

export const OS_EOL = navigator.platform === 'win32' ? 'crlf' : 'lf';
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async function registerAutocomplete(editor, api, transId) {
});
}

export default function Query({onTextSelect}) {
export default function Query({onTextSelect, handleEndOfLineChange}) {
const editor = React.useRef();
const eventBus = useContext(QueryToolEventsContext);
const queryToolCtx = useContext(QueryToolContext);
Expand All @@ -65,7 +65,6 @@ export default function Query({onTextSelect}) {
const pgAdmin = usePgAdmin();
const preferencesStore = usePreferences();
const queryToolPref = queryToolCtx.preferences.sqleditor;

const highlightError = (cmObj, {errormsg: result, data}, executeCursor)=>{
let errorLineNo = 0,
startMarker = 0,
Expand Down Expand Up @@ -175,7 +174,6 @@ export default function Query({onTextSelect}) {
}
});


eventBus.registerListener(QUERY_TOOL_EVENTS.LOAD_FILE, (fileName, storage)=>{
queryToolCtx.api.post(url_for('sqleditor.load_file'), {
'file_name': decodeURI(fileName),
Expand All @@ -191,6 +189,8 @@ export default function Query({onTextSelect}) {
checkTrojanSource(res.data);
editor.current.markClean();
eventBus.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE_DONE, fileName, true);
const lineSep = res.data.includes('\r\n') ? 'crlf' : 'lf';
handleEndOfLineChange(lineSep);
}).catch((err)=>{
eventBus.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE_DONE, null, false);
pgAdmin.Browser.notifier.error(parseApiError(err));
Expand All @@ -200,7 +200,7 @@ export default function Query({onTextSelect}) {
eventBus.registerListener(QUERY_TOOL_EVENTS.SAVE_FILE, (fileName)=>{
queryToolCtx.api.post(url_for('sqleditor.save_file'), {
'file_name': decodeURI(fileName),
'file_content': editor.current.getValue(),
'file_content': editor.current.getValue(false, true),
}).then(()=>{
editor.current.markClean();
eventBus.fireEvent(QUERY_TOOL_EVENTS.SAVE_FILE_DONE, fileName, true);
Expand Down Expand Up @@ -288,6 +288,12 @@ export default function Query({onTextSelect}) {
editor.current.setValue(formattedSql);
}
});

eventBus.registerListener(QUERY_TOOL_EVENTS.CHANGE_EOL, (lineSep)=>{
editor.current?.setEOL(lineSep);
eventBus.fireEvent(QUERY_TOOL_EVENTS.QUERY_CHANGED, true);
});

eventBus.registerListener(QUERY_TOOL_EVENTS.EDITOR_TOGGLE_CASE, ()=>{
let selectedText = editor.current?.getSelection();
if (!selectedText) return;
Expand Down Expand Up @@ -518,4 +524,5 @@ export default function Query({onTextSelect}) {

Query.propTypes = {
onTextSelect: PropTypes.func,
handleEndOfLineChange: PropTypes.func
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
//////////////////////////////////////////////////////////////
import React, { useEffect, useState, useContext } from 'react';
import { styled } from '@mui/material/styles';
import { Box } from '@mui/material';
import { Box, Tooltip } from '@mui/material';
import _ from 'lodash';
import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';
import { useStopwatch } from '../../../../../../static/js/custom_hooks';
import { QueryToolEventsContext } from '../QueryToolComponent';
import gettext from 'sources/gettext';
import { PgMenu, PgMenuItem, usePgMenuGroup } from '../../../../../../static/js/components/Menu';


const StyledBox = styled(Box)(({theme}) => ({
Expand All @@ -26,17 +27,17 @@ const StyledBox = styled(Box)(({theme}) => ({
userSelect: 'text',
'& .StatusBar-padding': {
padding: '2px 12px',
'& .StatusBar-mlAuto': {
'&.StatusBar-mlAuto': {
marginLeft: 'auto',
},
'& .StatusBar-divider': {
'&.StatusBar-divider': {
...theme.mixins.panelBorder.right,
},
},
}));

export function StatusBar() {

export function StatusBar({eol, handleEndOfLineChange}) {
const eventBus = useContext(QueryToolEventsContext);
const [position, setPosition] = useState([1, 1]);
const [lastTaskText, setLastTaskText] = useState(null);
Expand All @@ -49,6 +50,8 @@ export function StatusBar() {
deleted: 0,
});
const {seconds, minutes, hours, msec, start:startTimer, pause:pauseTimer, reset:resetTimer} = useStopwatch({});
const eolMenuRef = React.useRef(null);
const {openMenuName, toggleMenu, onMenuClose} = usePgMenuGroup();

useEffect(()=>{
eventBus.registerListener(QUERY_TOOL_EVENTS.CURSOR_ACTIVITY, (newPos)=>{
Expand Down Expand Up @@ -109,7 +112,36 @@ export function StatusBar() {
<span>{gettext('Changes staged: %s', stagedText)}</span>
</Box>
}
<Box className='StatusBar-padding StatusBar-mlAuto'>{gettext('Ln %s, Col %s', position[0], position[1])}</Box>

<Box className='StatusBar-padding StatusBar-mlAuto' style={{display:'flex'}}>
<Box className="StatusBar-padding StatusBar-divider">
<Tooltip title="Select EOL Sequence" disableInteractive enterDelay={2500}>
<span
onClick={toggleMenu}
ref={eolMenuRef}
name="menu-eoloptions"
style={{
cursor: 'pointer',
display: 'inline-flex',
alignItems: 'center',
fontSize: 'inherit',
}}
>
{eol.toUpperCase()}
</span>
</Tooltip>
<PgMenu
anchorRef={eolMenuRef}
open={openMenuName=='menu-eoloptions'}
onClose={onMenuClose}
label={gettext('EOL Options Menu')}
>
<PgMenuItem hasCheck value="lf" checked={eol === 'lf'} onClick={handleEndOfLineChange}>{gettext('LF')}</PgMenuItem>
<PgMenuItem hasCheck value="crlf" checked={eol === 'crlf'} onClick={handleEndOfLineChange}>{gettext('CRLF')}</PgMenuItem>
</PgMenu>
</Box>
<Box className='StatusBar-padding'>{gettext('Ln %s, Col %s', position[0], position[1])}</Box>
</Box>
</StyledBox>
);
}

0 comments on commit 246dd8a

Please sign in to comment.