2025-03-04 12:58:06 +00:00
|
|
|
import normalizeUrl from "normalize-url";
|
|
|
|
import type { MessageTransports } from "vscode-languageclient";
|
|
|
|
import type { MonacoLanguageClient } from "monaco-languageclient";
|
2025-04-01 03:46:07 +00:00
|
|
|
import { EditorLanguageConfig, LanguageServerConfig } from "@/generated/client";
|
2025-03-04 12:58:06 +00:00
|
|
|
import { toSocket, WebSocketMessageReader, WebSocketMessageWriter } from "vscode-ws-jsonrpc";
|
|
|
|
|
|
|
|
// Create the WebSocket URL based on the protocol and port
|
2025-03-16 07:49:46 +00:00
|
|
|
function createUrl(languageServerConfig: LanguageServerConfig): string {
|
|
|
|
return normalizeUrl(
|
2025-03-23 15:51:12 +00:00
|
|
|
`${languageServerConfig.protocol}://${languageServerConfig.hostname}${languageServerConfig.port ? `:${languageServerConfig.port}` : ""
|
2025-03-16 07:49:46 +00:00
|
|
|
}${languageServerConfig.path || ""}`
|
|
|
|
);
|
2025-03-04 12:58:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the language client with the given transports
|
2025-03-16 07:49:46 +00:00
|
|
|
async function createLanguageClient(
|
|
|
|
transports: MessageTransports,
|
|
|
|
editorLanguageConfig: EditorLanguageConfig
|
|
|
|
): Promise<MonacoLanguageClient> {
|
2025-03-04 12:58:06 +00:00
|
|
|
const { MonacoLanguageClient } = await import("monaco-languageclient");
|
|
|
|
const { CloseAction, ErrorAction } = await import("vscode-languageclient");
|
|
|
|
|
|
|
|
return new MonacoLanguageClient({
|
2025-03-16 07:49:46 +00:00
|
|
|
name: `${editorLanguageConfig.label} Language Client`,
|
2025-03-04 12:58:06 +00:00
|
|
|
clientOptions: {
|
|
|
|
// use a language id as a document selector
|
2025-03-16 07:49:46 +00:00
|
|
|
documentSelector: [editorLanguageConfig.language],
|
2025-03-04 12:58:06 +00:00
|
|
|
// disable the default error handler
|
|
|
|
errorHandler: {
|
|
|
|
error: () => ({ action: ErrorAction.Continue }),
|
|
|
|
closed: () => ({ action: CloseAction.DoNotRestart }),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// create a language client connection from the JSON RPC connection on demand
|
|
|
|
connectionProvider: {
|
2025-03-23 15:51:12 +00:00
|
|
|
get: () => Promise.resolve(transports),
|
2025-03-16 07:49:46 +00:00
|
|
|
},
|
2025-03-04 12:58:06 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Connect to the WebSocket and create the language client
|
2025-03-16 07:49:46 +00:00
|
|
|
export function connectToLanguageServer(
|
|
|
|
editorLanguageConfig: EditorLanguageConfig,
|
|
|
|
languageServerConfig: LanguageServerConfig
|
2025-03-23 15:51:12 +00:00
|
|
|
): Promise<{ client: MonacoLanguageClient; webSocket: WebSocket }> {
|
2025-03-16 07:49:46 +00:00
|
|
|
const url = createUrl(languageServerConfig);
|
2025-03-04 12:58:06 +00:00
|
|
|
const webSocket = new WebSocket(url);
|
|
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
// Handle the WebSocket opening event
|
|
|
|
webSocket.onopen = async () => {
|
|
|
|
const socket = toSocket(webSocket);
|
|
|
|
const reader = new WebSocketMessageReader(socket);
|
|
|
|
const writer = new WebSocketMessageWriter(socket);
|
|
|
|
|
|
|
|
try {
|
2025-03-23 15:51:12 +00:00
|
|
|
const languageClient = await createLanguageClient(
|
|
|
|
{ reader, writer },
|
|
|
|
editorLanguageConfig
|
|
|
|
);
|
|
|
|
|
2025-03-04 12:58:06 +00:00
|
|
|
// Start the language client
|
|
|
|
languageClient.start();
|
|
|
|
|
|
|
|
// Stop the language client when the reader closes
|
|
|
|
reader.onClose(() => languageClient.stop());
|
|
|
|
|
2025-03-23 15:51:12 +00:00
|
|
|
resolve({ client: languageClient, webSocket });
|
2025-03-04 12:58:06 +00:00
|
|
|
} catch (error) {
|
|
|
|
reject(error);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Handle WebSocket errors
|
|
|
|
webSocket.onerror = (error) => {
|
|
|
|
reject(error);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|