diff --git a/web/pgadmin/static/js/components/ReactCodeMirror/CustomEditorView.js b/web/pgadmin/static/js/components/ReactCodeMirror/CustomEditorView.js index b48f77a92..5708e1fe5 100644 --- a/web/pgadmin/static/js/components/ReactCodeMirror/CustomEditorView.js +++ b/web/pgadmin/static/js/components/ReactCodeMirror/CustomEditorView.js @@ -27,7 +27,9 @@ function getAutocompLoading({ bottom, left }, dom) { export default class CustomEditorView extends EditorView { constructor(...args) { super(...args); + // Set the initial and clean state for the document and EOL(end of line). this._cleanDoc = this.state.doc; + this._cleanDocEOL = this.getEOL(); } getValue(tillCursor=false, useLineSep=false) { @@ -268,10 +270,12 @@ export default class CustomEditorView extends EditorView { markClean() { this._cleanDoc = this.state.doc; + this._cleanDocEOL = this.getEOL(); // Update the initial EOL value. } isDirty() { - return !this._cleanDoc.eq(this.state.doc); + // Return true if either the document content or the EOL(end of line) has changed. + return !this._cleanDoc.eq(this.state.doc) || this._cleanDocEOL !== this.getEOL(); } fireDOMEvent(event) { @@ -340,4 +344,11 @@ export default class CustomEditorView extends EditorView { effects: eolCompartment.reconfigure(eol.of(val)) }); } + + // Use to detect EOL type. + detectEOL(content) { + const lineSep = content.includes('\r\n') ? '\r\n' : '\n'; + this.setEOL(lineSep); + return lineSep == '\r\n' ? 'crlf' : 'lf'; + } } diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx index 0c7d6bf52..4f0ac6482 100644 --- a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx +++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx @@ -271,7 +271,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN { maximizable: true, tabs: [ - LayoutDocker.getPanel({id: PANELS.QUERY, title: gettext('Query'), content: setSelectedText(text)} handleEndOfLineChange={handleEndOfLineChange}/>}), + LayoutDocker.getPanel({id: PANELS.QUERY, title: gettext('Query'), content: setSelectedText(text)} setQtStatePartial={setQtStatePartial}/>}), LayoutDocker.getPanel({id: PANELS.HISTORY, title: gettext('Query History'), content: , cached: undefined}), ], diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx index c8f650c52..277fdabc2 100644 --- a/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx +++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx @@ -10,7 +10,7 @@ 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'; -import {OS_EOL, PANELS, QUERY_TOOL_EVENTS} from '../QueryToolConstants'; +import { PANELS, QUERY_TOOL_EVENTS} from '../QueryToolConstants'; import url_for from 'sources/url_for'; import { LayoutDockerContext, LAYOUT_EVENTS } from '../../../../../../static/js/helpers/Layout'; import ConfirmSaveContent from '../../../../../../static/js/Dialogs/ConfirmSaveContent'; @@ -56,7 +56,7 @@ async function registerAutocomplete(editor, api, transId) { }); } -export default function Query({onTextSelect, handleEndOfLineChange}) { +export default function Query({onTextSelect, setQtStatePartial}) { const editor = React.useRef(); const eventBus = useContext(QueryToolEventsContext); const queryToolCtx = useContext(QueryToolContext); @@ -187,12 +187,13 @@ export default function Query({onTextSelect, handleEndOfLineChange}) { editor.current.setValue(res.data); //Check the file content for Trojan Source 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'; - if (lineSep !== OS_EOL){ - handleEndOfLineChange(lineSep); - } + // Detect line separator from content and editor's EOL. + const lineSep = editor.current?.detectEOL(res.data); + // Update the EOL if it differs from the current editor EOL + setQtStatePartial({ eol: lineSep }); + // Mark the editor content as clean + editor.current?.markClean(); }).catch((err)=>{ eventBus.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE_DONE, null, false); pgAdmin.Browser.notifier.error(parseApiError(err)); @@ -292,8 +293,9 @@ export default function Query({onTextSelect, handleEndOfLineChange}) { }); eventBus.registerListener(QUERY_TOOL_EVENTS.CHANGE_EOL, (lineSep)=>{ + // Set the new EOL character in the editor. editor.current?.setEOL(lineSep); - eventBus.fireEvent(QUERY_TOOL_EVENTS.QUERY_CHANGED, true); + eventBus.fireEvent(QUERY_TOOL_EVENTS.QUERY_CHANGED, editor.current?.isDirty()); }); eventBus.registerListener(QUERY_TOOL_EVENTS.EDITOR_TOGGLE_CASE, ()=>{ @@ -526,5 +528,5 @@ export default function Query({onTextSelect, handleEndOfLineChange}) { Query.propTypes = { onTextSelect: PropTypes.func, - handleEndOfLineChange: PropTypes.func + setQtStatePartial: PropTypes.func };