feat(playground): integrate WebSocket support and enhance language client in code editor

This commit is contained in:
ngc2207 2024-12-17 20:59:55 +08:00
parent 1afa7a1a5d
commit f0a2010985
3 changed files with 83 additions and 1 deletions

View File

@ -2,6 +2,7 @@
"extends": ["next/core-web-vitals", "next/typescript"], "extends": ["next/core-web-vitals", "next/typescript"],
"rules": { "rules": {
"@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/no-empty-object-type": "warn" "@typescript-eslint/no-empty-object-type": "warn",
"@typescript-eslint/no-require-imports": "warn"
} }
} }

View File

@ -30,14 +30,18 @@
"gitea-js": "^1.22.0", "gitea-js": "^1.22.0",
"jotai": "^2.10.3", "jotai": "^2.10.3",
"lucide-react": "^0.468.0", "lucide-react": "^0.468.0",
"monaco-languageclient": "^8.8.3",
"next": "15.0.4", "next": "15.0.4",
"next-auth": "^5.0.0-beta.25", "next-auth": "^5.0.0-beta.25",
"next-intl": "^3.26.1", "next-intl": "^3.26.1",
"next-themes": "^0.4.4", "next-themes": "^0.4.4",
"normalize-url": "^8.0.1",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"reconnecting-websocket": "^4.4.0",
"tailwind-merge": "^2.5.5", "tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"vscode-ws-jsonrpc": "^3.3.2",
"zustand": "^5.0.2" "zustand": "^5.0.2"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,12 +1,27 @@
"use client"; "use client";
import {
toSocket,
WebSocketMessageReader,
WebSocketMessageWriter,
} from "vscode-ws-jsonrpc";
import {
CloseAction,
ErrorAction,
MessageTransports,
} from "vscode-languageclient";
import "@fontsource-variable/fira-code"; import "@fontsource-variable/fira-code";
import normalizeUrl from "normalize-url";
import { createHighlighter } from "shiki"; import { createHighlighter } from "shiki";
import type { editor } from "monaco-editor"; import type { editor } from "monaco-editor";
import { shikiToMonaco } from "@shikijs/monaco"; import { shikiToMonaco } from "@shikijs/monaco";
import { useEffect, useRef, useMemo } from "react"; import { useEffect, useRef, useMemo } from "react";
import { useCodeEditorStore } from "@/store/codeEditorStore"; import { useCodeEditorStore } from "@/store/codeEditorStore";
import { MonacoLanguageClient } from "monaco-languageclient";
import MonacoEditor, { type Monaco } from "@monaco-editor/react"; import MonacoEditor, { type Monaco } from "@monaco-editor/react";
import { initServices } from "monaco-languageclient/vscode/services";
const ReconnectingWebSocket = require("reconnecting-websocket");
const ADDITIONAL_THEMES = [ const ADDITIONAL_THEMES = [
"andromeeda", "andromeeda",
@ -63,6 +78,66 @@ export function CodeEditor() {
[isMinimap, isLigature] [isMinimap, isLigature]
); );
const createLanguageClient = (
transports: MessageTransports
): MonacoLanguageClient => {
return new MonacoLanguageClient({
name: "LSP Language Client",
clientOptions: {
documentSelector: ADDITIONAL_LANGUAGES,
errorHandler: {
error: () => ({ action: ErrorAction.Continue }),
closed: () => ({ action: CloseAction.DoNotRestart }),
},
},
connectionProvider: {
get: () => {
return Promise.resolve(transports);
},
},
});
};
useEffect(() => {
if (!monacoRef.current) return;
const url = normalizeUrl(`ws://172.20.0.13:3000`);
const socketOptions = {
maxReconnectionDelay: 10000,
minReconnectionDelay: 1000,
reconnectionDelayGrowFactor: 1.3,
connectionTimeout: 10000,
maxRetries: Infinity,
debug: true,
};
const webSocket: WebSocket = new ReconnectingWebSocket.default(
url,
[],
socketOptions
);
webSocket.onopen = () => {
console.log("WebSocket connection opened");
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());
};
webSocket.onerror = (error) => {
console.error("WebSocket error:", error);
};
webSocket.onclose = (event) => {
console.log("WebSocket connection closed:", event);
};
return () => {
webSocket.close();
};
}, [monacoRef]);
const handleEditorMount = async ( const handleEditorMount = async (
editor: editor.IStandaloneCodeEditor, editor: editor.IStandaloneCodeEditor,
monaco: Monaco monaco: Monaco
@ -80,6 +155,8 @@ export function CodeEditor() {
}); });
shikiToMonaco(highlighter, monacoRef.current); shikiToMonaco(highlighter, monacoRef.current);
await initServices({});
}; };
return ( return (