diff --git a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx index 29cd198ad0..986dde6433 100644 --- a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx +++ b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx @@ -42,6 +42,7 @@ import { import {useChangedQuerySettings} from '../../../../utils/hooks/useChangedQuerySettings'; import {useLastQueryExecutionSettings} from '../../../../utils/hooks/useLastQueryExecutionSettings'; import {YQL_LANGUAGE_ID} from '../../../../utils/monaco/constats'; +import {useUpdateErrorsHighlighting} from '../../../../utils/monaco/highlightErrors'; import {QUERY_ACTIONS} from '../../../../utils/query'; import type {InitialPaneState} from '../../utils/paneVisibilityToggleHelpers'; import { @@ -89,6 +90,8 @@ export default function QueryEditor(props: QueryEditorProps) { const input = useTypedSelector(selectUserInput); const showPreview = useTypedSelector(selectShowPreview); + const updateErrorsHighlighting = useUpdateErrorsHighlighting(); + const isResultLoaded = Boolean(result); const [querySettings] = useQueryExecutionSettings(); @@ -294,6 +297,7 @@ export default function QueryEditor(props: QueryEditorProps) { }; const onChange = (newValue: string) => { + updateErrorsHighlighting(); changeUserInput({input: newValue}); }; diff --git a/src/containers/Tenant/Query/QueryEditor/helpers.ts b/src/containers/Tenant/Query/QueryEditor/helpers.ts index d1207d33c7..990ded87b5 100644 --- a/src/containers/Tenant/Query/QueryEditor/helpers.ts +++ b/src/containers/Tenant/Query/QueryEditor/helpers.ts @@ -13,6 +13,7 @@ const EDITOR_OPTIONS: EditorOptions = { minimap: { enabled: false, }, + fixedOverflowWidgets: true, }; export function useEditorOptions() { diff --git a/src/utils/hooks/useCancellable.ts b/src/utils/hooks/useCancellable.ts new file mode 100644 index 0000000000..5c80d6b4ae --- /dev/null +++ b/src/utils/hooks/useCancellable.ts @@ -0,0 +1,10 @@ +import React from 'react'; + +export function useCancellableFunction void}>(cancellable: T) { + React.useEffect(() => { + return () => { + cancellable.cancel(); + }; + }, [cancellable]); + return cancellable; +} diff --git a/src/utils/monaco/highlightErrors.ts b/src/utils/monaco/highlightErrors.ts new file mode 100644 index 0000000000..08c7dfe5bb --- /dev/null +++ b/src/utils/monaco/highlightErrors.ts @@ -0,0 +1,54 @@ +import React from 'react'; + +import {parseYqlQueryWithoutCursor} from '@gravity-ui/websql-autocomplete/yql'; +import {debounce} from 'lodash'; +import {MarkerSeverity, editor} from 'monaco-editor'; + +import {useCancellableFunction} from '../hooks/useCancellable'; + +import i18n from './i18n'; + +const owner = 'ydb'; + +const debouncedHighlightErrors = debounce(highlightErrors, 500); + +export function useUpdateErrorsHighlighting() { + const highlightErrors = useCancellableFunction(debouncedHighlightErrors); + const updateErrorsHighlighting = React.useCallback(() => { + unHighlightErrors(); + + highlightErrors(); + }, [highlightErrors]); + return updateErrorsHighlighting; +} + +function highlightErrors() { + const model = window.ydbEditor?.getModel(); + if (!model) { + console.error('unable to retrieve model when highlighting errors'); + return; + } + + const errors = parseYqlQueryWithoutCursor(model.getValue()).errors; + if (!errors.length) { + unHighlightErrors(); + return; + } + + const markers = errors.map( + (error): editor.IMarkerData => ({ + message: i18n('context_syntax-error'), + source: error.message, + severity: MarkerSeverity.Error, + startLineNumber: error.startLine, + startColumn: error.startColumn + 1, + endLineNumber: error.endLine, + endColumn: error.endColumn + 1, + }), + ); + editor.setModelMarkers(model, owner, markers); +} + +function unHighlightErrors(): void { + editor.removeAllMarkers(owner); +} diff --git a/src/utils/monaco/i18n/en.json b/src/utils/monaco/i18n/en.json new file mode 100644 index 0000000000..d4b173c360 --- /dev/null +++ b/src/utils/monaco/i18n/en.json @@ -0,0 +1,3 @@ +{ + "context_syntax-error": "Syntax error" +} diff --git a/src/utils/monaco/i18n/index.ts b/src/utils/monaco/i18n/index.ts new file mode 100644 index 0000000000..0061fd6709 --- /dev/null +++ b/src/utils/monaco/i18n/index.ts @@ -0,0 +1,7 @@ +import {registerKeysets} from '../../i18n'; + +import en from './en.json'; + +const COMPONENT = 'ydb-monaco'; + +export default registerKeysets(COMPONENT, {en});