michael-s-molina commented on code in PR #37499: URL: https://github.com/apache/superset/pull/37499#discussion_r2737961287
########## superset-frontend/src/SqlLab/components/EditorWrapper/index.tsx: ########## @@ -0,0 +1,363 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { useState, useEffect, useRef, useCallback, useMemo } from 'react'; +import { shallowEqual, useDispatch, useSelector } from 'react-redux'; +import { usePrevious } from '@superset-ui/core'; +import { css, useTheme } from '@apache-superset/core/ui'; +import { Global } from '@emotion/react'; +import type { editors } from '@apache-superset/core'; + +import { SQL_EDITOR_LEFTBAR_WIDTH } from 'src/SqlLab/constants'; +import { queryEditorSetSelectedText } from 'src/SqlLab/actions/sqlLab'; +import type { KeyboardShortcut } from 'src/SqlLab/components/KeyboardShortcutButton'; +import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor'; +import { SqlLabRootState, type CursorPosition } from 'src/SqlLab/types'; +import { EditorHost } from 'src/core/editors'; +import { useAnnotations } from './useAnnotations'; +import { useKeywords } from './useKeywords'; + +type EditorHandle = editors.EditorHandle; +type EditorHotkey = editors.EditorHotkey; +type EditorAnnotation = editors.EditorAnnotation; + +type HotKey = { + key: KeyboardShortcut; + descr?: string; + name: string; + func: (editor: EditorHandle) => void; +}; + +type EditorWrapperProps = { + autocomplete: boolean; + onBlur: (sql: string) => void; + onChange: (sql: string) => void; + queryEditorId: string; + onCursorPositionChange: (position: CursorPosition) => void; + height: string; + hotkeys: HotKey[]; +}; + +/** + * Convert legacy HotKey format to EditorHotkey format. + */ +const convertHotkeys = ( + hotkeys: HotKey[], + onRunQuery: () => void, +): EditorHotkey[] => { + const result: EditorHotkey[] = [ + // Add the built-in run query hotkey + { + name: 'runQuery', + key: 'Alt-enter', + description: 'Run query', + exec: () => onRunQuery(), + }, + ]; + + hotkeys.forEach(keyConfig => { + result.push({ + name: keyConfig.name, + key: keyConfig.key, + description: keyConfig.descr, + exec: keyConfig.func, + }); + }); + + return result; +}; + +/** + * Ace annotation format returned from useAnnotations when data is available. + */ +type AceAnnotation = { + row: number; + column: number; + text: string | null; + type: string; +}; + +/** + * Type guard to check if an annotation is in Ace format. + */ +const isAceAnnotation = (ann: unknown): ann is AceAnnotation => + typeof ann === 'object' && + ann !== null && + 'row' in ann && + 'column' in ann && + 'text' in ann && + 'type' in ann; + +/** + * Convert annotation array to EditorAnnotation format. + * Handles the union type returned from useAnnotations. + */ +const convertAnnotations = ( + annotations?: unknown[], +): EditorAnnotation[] | undefined => { + if (!annotations || annotations.length === 0) return undefined; + // Check if first item is in Ace format (has row, column, text, type) + if (!isAceAnnotation(annotations[0])) return undefined; + return (annotations as AceAnnotation[]).map(ann => ({ + line: ann.row, + column: ann.column, + message: ann.text ?? '', + severity: ann.type as EditorAnnotation['severity'], + })); +}; + +/** + * EditorWrapper component that renders the SQL editor using EditorHost. + * Uses the default Ace editor or an extension-provided editor based on + * what's registered with the editors API. + */ +const EditorWrapper = ({ + autocomplete, + onBlur = () => {}, + onChange = () => {}, + queryEditorId, + onCursorPositionChange, + height, + hotkeys, +}: EditorWrapperProps) => { + const dispatch = useDispatch(); + const queryEditor = useQueryEditor(queryEditorId, [ + 'id', + 'dbId', + 'sql', + 'catalog', + 'schema', + 'templateParams', + 'tabViewId', + ]); + // Prevent a maximum update depth exceeded error + // by skipping access the unsaved query editor state + const cursorPosition = useSelector<SqlLabRootState, CursorPosition>( + ({ sqlLab: { queryEditors } }) => { + const editor = queryEditors.find(({ id }) => id === queryEditorId); + return editor?.cursorPosition ?? { row: 0, column: 0 }; + }, Review Comment: The .find() in the selector is a common Redux pattern with shallowEqual comparison and typically small arrays (few query editors). -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
