feat(playground): integrate WebSocket support and enhance language client in code editor
This commit is contained in:
parent
1afa7a1a5d
commit
f0a2010985
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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": {
|
||||||
|
@ -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 (
|
||||||
|
Loading…
Reference in New Issue
Block a user