feat: integrate language server support with WebSocket connection for C/C++ languages

This commit is contained in:
ngc2207 2025-01-10 18:35:33 +08:00
parent 104e66d4d0
commit 937330ac3d
4 changed files with 113 additions and 7 deletions

Binary file not shown.

View File

@ -18,10 +18,15 @@
"clsx": "^2.1.1",
"devicons-react": "^1.4.0",
"lucide-react": "^0.469.0",
"monaco-editor": "0.36.1",
"monaco-languageclient": "5.0.1",
"normalize-url": "~8.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"vscode-languageclient": "~8.1.0",
"vscode-ws-jsonrpc": "3.0.0"
},
"devDependencies": {
"@eslint/js": "^9.17.0",

View File

@ -3,19 +3,38 @@ import {
DEFAULT_EDITOR_THEME,
DEFAULT_EDITOR_LANGUAGE,
} from "@/config";
import { useState } from "react";
import * as monaco from "monaco-editor";
import { highlighter } from "@/lib/shiki";
import { Bot, CodeXml } from "lucide-react";
import { shikiToMonaco } from "@shikijs/monaco";
import { DiffEditor, Editor, Monaco } from "@monaco-editor/react";
import { connectToLanguageServer } from "@/lib/lsp";
import { useEffect, useRef, useState } from "react";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import { DiffEditor, Editor, Monaco, loader } from "@monaco-editor/react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
loader.config({ monaco });
function App() {
const [language, setLanguage] = useState(DEFAULT_EDITOR_LANGUAGE);
const [theme, setTheme] = useState(DEFAULT_EDITOR_THEME);
const file = DEFAULT_FILES[language];
const webSocketRef = useRef<WebSocket | null>(null);
useEffect(() => {
if (webSocketRef.current) {
webSocketRef.current.close();
}
connectToLanguageServer(language).then((webSocket) => {
webSocketRef.current = webSocket;
});
return () => {
if (webSocketRef.current) {
webSocketRef.current.close();
}
};
}, [language]);
return (
<div className="h-screen bg-[#282c34] dark overflow-hidden">
@ -71,10 +90,7 @@ function App() {
beforeMount={async (monaco: Monaco) => {
shikiToMonaco(await highlighter, monaco);
}}
onMount={(
editor: monaco.editor.IStandaloneCodeEditor,
monaco: Monaco
) => {
onMount={(editor: monaco.editor.IStandaloneCodeEditor) => {
const value = editor.getModel()?.getValue();
if (value !== undefined) {
window.parent.postMessage(
@ -82,6 +98,9 @@ function App() {
"*"
);
}
connectToLanguageServer(language).then((webSocket) => {
webSocketRef.current = webSocket;
});
}}
onChange={(value) => {
if (value !== undefined) {

View File

@ -0,0 +1,82 @@
import {
toSocket,
WebSocketMessageReader,
WebSocketMessageWriter,
} from "vscode-ws-jsonrpc";
import {
CloseAction,
ErrorAction,
MessageTransports,
} from "vscode-languageclient";
import normalizeUrl from "normalize-url";
import { MonacoLanguageClient } from "monaco-languageclient";
export const SUPPORTED_LSP_LANGUAGES: {
[key: string]: { hostname: string; port: number | null; path: string };
} = {
c: {
hostname: "c.litchi.icu",
port: null,
path: "/clangd",
},
cpp: {
hostname: "cpp.litchi.icu",
port: null,
path: "/clangd",
},
};
function createUrl(
hostname: string,
port: number | null,
path: string
): string {
const protocol = location.protocol === "https:" ? "wss" : "ws";
return port !== null
? normalizeUrl(`${protocol}://${hostname}:${port}${path}`)
: normalizeUrl(`${protocol}://${hostname}${path}`);
}
function createLanguageClient(
transports: MessageTransports
): MonacoLanguageClient {
return new MonacoLanguageClient({
name: "Judge4c Language Client",
clientOptions: {
documentSelector: ["c", "cpp"],
errorHandler: {
error: () => ({ action: ErrorAction.Continue }),
closed: () => ({ action: CloseAction.DoNotRestart }),
},
},
connectionProvider: {
get: () => {
return Promise.resolve(transports);
},
},
});
}
function createWebSocket(url: string) {
const webSocket = new WebSocket(url);
webSocket.onopen = () => {
const socket = toSocket(webSocket);
const reader = new WebSocketMessageReader(socket);
const writer = new WebSocketMessageWriter(socket);
const languageClient = createLanguageClient({
reader,
writer,
});
languageClient.start();
reader.onClose(() => languageClient.stop());
};
return webSocket;
}
export async function connectToLanguageServer(
language: string
): Promise<WebSocket> {
const { hostname, port, path } = SUPPORTED_LSP_LANGUAGES[language];
const url = createUrl(hostname, port, path);
return createWebSocket(url);
}