"use client"; import dynamic from "next/dynamic"; import { highlighter } from "@/lib/shiki"; import type { editor } from "monaco-editor"; import { Loading } from "@/components/loading"; import { shikiToMonaco } from "@shikijs/monaco"; import { useProblem } from "@/hooks/use-problem"; import type { Monaco } from "@monaco-editor/react"; import { useCallback, useEffect, useRef } from "react"; import { connectToLanguageServer } from "@/lib/language-server"; import type { MonacoLanguageClient } from "monaco-languageclient"; import { DefaultEditorOptionConfig } from "@/config/editor-option"; // Dynamically import Monaco Editor with SSR disabled const Editor = dynamic( async () => { await import("vscode"); const monaco = await import("monaco-editor"); const { loader } = await import("@monaco-editor/react"); loader.config({ monaco }); return (await import("@monaco-editor/react")).Editor; }, { ssr: false, loading: () => , } ); export function CodeEditor() { const { hydrated, editor, setEditor, setWebSocket, currentLang, currentPath, currentTheme, currentValue, changeValue, currentEditorLanguageConfig, currentLanguageServerConfig, } = useProblem(); const monacoLanguageClientRef = useRef(null); // Connect to LSP only if enabled const connectLSP = useCallback(async () => { if (!(currentLang && editor)) return; // If there's an existing language client, stop it first if (monacoLanguageClientRef.current) { monacoLanguageClientRef.current.stop(); monacoLanguageClientRef.current = null; setWebSocket(null); } if (!currentEditorLanguageConfig || !currentLanguageServerConfig) return; // Create a new language client try { const { client: monacoLanguageClient, webSocket } = await connectToLanguageServer( currentEditorLanguageConfig, currentLanguageServerConfig ); monacoLanguageClientRef.current = monacoLanguageClient; setWebSocket(webSocket); } catch (error) { console.error("Failed to connect to LSP:", error); } }, [ currentEditorLanguageConfig, currentLang, currentLanguageServerConfig, editor, setWebSocket, ]); // Reconnect to the LSP whenever language or lspConfig changes useEffect(() => { connectLSP(); }, [connectLSP]); // Cleanup the LSP connection when the component unmounts useEffect(() => { return () => { if (monacoLanguageClientRef.current) { monacoLanguageClientRef.current.stop(); monacoLanguageClientRef.current = null; setWebSocket(null); } }; }, [setWebSocket]); const handleEditorWillMount = useCallback((monaco: Monaco) => { shikiToMonaco(highlighter, monaco); }, []); const handleOnMount = useCallback( async (editor: editor.IStandaloneCodeEditor) => { setEditor(editor); await connectLSP(); }, [setEditor, connectLSP] ); const handleEditorChange = useCallback( (value: string | undefined) => { if (value !== undefined) { changeValue(value); } }, [changeValue] ); if (!hydrated) { return ; } return ( } className="h-full w-full py-2" /> ); }