import { useEffect, useRef, forwardRef, useImperativeHandle } from 'react' import Editor, { Monaco } from '@monaco-editor/react' import { useConfig } from '@/components/ui/ConfigProvider' import type { editor } from 'monaco-editor' interface SqlEditorProps { value: string onChange: (value: string | undefined) => void onExecute?: () => void onSave?: () => void readOnly?: boolean height?: string } export interface SqlEditorRef { getSelectedText: () => string preserveSelection: () => any restoreSelection: (selection: any) => void } const SqlEditor = forwardRef(( { value, onChange, onExecute, onSave, readOnly = false, height = '100%', }: SqlEditorProps, ref ) => { const { mode } = useConfig() const editorRef = useRef(null) const monacoRef = useRef(null) useImperativeHandle(ref, () => ({ getSelectedText: () => { if (!editorRef.current) return '' const selection = editorRef.current.getSelection() if (!selection) return '' return editorRef.current.getModel()?.getValueInRange(selection) || '' }, preserveSelection: () => { if (!editorRef.current) return null return editorRef.current.getSelection() }, restoreSelection: (selection: any) => { if (!editorRef.current || !selection) return editorRef.current.setSelection(selection) editorRef.current.revealRangeInCenter(selection) }, })) const handleEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => { editorRef.current = editor monacoRef.current = monaco // Add keyboard shortcuts editor.addCommand(monaco.KeyCode.F5, () => { if (onExecute) onExecute() }) editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => { if (onSave) onSave() }) // Configure SQL language features monaco.languages.registerCompletionItemProvider('sql', { provideCompletionItems: (model, position) => { const word = model.getWordUntilPosition(position) const range = { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: word.startColumn, endColumn: word.endColumn, } const suggestions = [ // SQL Keywords { label: 'SELECT', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'SELECT ', range: range, }, { label: 'FROM', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'FROM ', range: range, }, { label: 'WHERE', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'WHERE ', range: range, }, { label: 'INSERT INTO', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'INSERT INTO ', range: range, }, { label: 'UPDATE', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'UPDATE ', range: range, }, { label: 'DELETE FROM', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'DELETE FROM ', range: range, }, { label: 'CREATE TABLE', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'CREATE TABLE ', range: range, }, { label: 'CREATE PROCEDURE', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'CREATE PROCEDURE ${1:ProcedureName}\nAS\nBEGIN\n\t$0\nEND', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, range: range, }, { label: 'CREATE FUNCTION', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'CREATE FUNCTION ${1:FunctionName}()\nRETURNS ${2:ReturnType}\nAS\nBEGIN\n\t$0\nEND', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, range: range, }, { label: 'CREATE VIEW', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'CREATE VIEW ${1:ViewName}\nAS\n\tSELECT $0', insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, range: range, }, { label: 'JOIN', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'JOIN ', range: range, }, { label: 'LEFT JOIN', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'LEFT JOIN ', range: range, }, { label: 'RIGHT JOIN', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'RIGHT JOIN ', range: range, }, { label: 'INNER JOIN', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'INNER JOIN ', range: range, }, { label: 'GROUP BY', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'GROUP BY ', range: range, }, { label: 'ORDER BY', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'ORDER BY ', range: range, }, { label: 'HAVING', kind: monaco.languages.CompletionItemKind.Keyword, insertText: 'HAVING ', range: range, }, ] return { suggestions: suggestions } }, }) // Focus the editor editor.focus() } const editorOptions: editor.IStandaloneEditorConstructionOptions = { readOnly: readOnly, minimap: { enabled: true }, fontSize: 14, lineNumbers: 'on', roundedSelection: false, scrollBeyondLastLine: false, automaticLayout: true, tabSize: 2, wordWrap: 'on', folding: true, glyphMargin: true, lineDecorationsWidth: 10, lineNumbersMinChars: 3, renderLineHighlight: 'all', scrollbar: { vertical: 'visible', horizontal: 'visible', useShadows: false, verticalHasArrows: false, horizontalHasArrows: false, verticalScrollbarSize: 10, horizontalScrollbarSize: 10, }, suggest: { showKeywords: true, showSnippets: true, }, } return (
) }) SqlEditor.displayName = 'SqlEditor' export default SqlEditor