Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion client/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export { createInterface } from "./commands/create_interface";
export { openCompiled } from "./commands/open_compiled";
export { switchImplIntf } from "./commands/switch_impl_intf";
export { dumpDebug, dumpDebugRetrigger } from "./commands/dump_debug";
export { dumpServerState } from "./commands/dump_server_state";
export { pasteAsRescriptJson } from "./commands/paste_as_rescript_json";
export { pasteAsRescriptJsx } from "./commands/paste_as_rescript_jsx";

Expand Down
38 changes: 0 additions & 38 deletions client/src/commands/dump_server_state.ts

This file was deleted.

11 changes: 9 additions & 2 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,8 +373,15 @@ export function activate(context: ExtensionContext) {
customCommands.dumpDebug(context, debugDumpStatusBarItem);
});

commands.registerCommand("rescript-vscode.dump-server-state", () => {
customCommands.dumpServerState(client, context, debugDumpStatusBarItem);
commands.registerCommand("rescript-vscode.dump-server-state", async () => {
// Server handles everything: writing to disk and opening the file via window/showDocument
try {
await client.sendRequest("workspace/executeCommand", {
command: "rescript/dumpServerState",
});
} catch (e) {
console.error("Failed to dump server state:", e);
}
});

commands.registerCommand("rescript-vscode.showProblems", async () => {
Expand Down
136 changes: 99 additions & 37 deletions server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as rpc from "vscode-jsonrpc/node";
import * as path from "path";
import semver from "semver";
import fs from "fs";
import fsAsync from "fs/promises";
import {
DidChangeWatchedFilesNotification,
DidOpenTextDocumentNotification,
Expand Down Expand Up @@ -1260,6 +1261,93 @@ function openCompiledFile(msg: p.RequestMessage): p.Message {
return response;
}

async function dumpServerState(
msg: p.RequestMessage,
): Promise<p.ResponseMessage> {
// Custom debug endpoint: dump current server state (config + projectsFiles)
try {
// Read the server version from package.json
let serverVersion: string | undefined;
try {
const packageJsonPath = path.join(__dirname, "..", "package.json");
const packageJsonContent = await fsAsync.readFile(packageJsonPath, {
encoding: "utf-8",
});
const packageJson = JSON.parse(packageJsonContent);
serverVersion = packageJson.version;
} catch (e) {
// If we can't read the version, that's okay - we'll just omit it
serverVersion = undefined;
}

const projects = Array.from(projectsFiles.entries()).map(
([projectRootPath, pf]) => ({
projectRootPath,
openFiles: Array.from(pf.openFiles),
filesWithDiagnostics: Array.from(pf.filesWithDiagnostics),
filesDiagnostics: pf.filesDiagnostics,
rescriptVersion: pf.rescriptVersion,
bscBinaryLocation: pf.bscBinaryLocation,
editorAnalysisLocation: pf.editorAnalysisLocation,
namespaceName: pf.namespaceName,
hasPromptedToStartBuild: pf.hasPromptedToStartBuild,
bsbWatcherByEditor:
pf.bsbWatcherByEditor != null
? { pid: pf.bsbWatcherByEditor.pid ?? null }
: null,
}),
);

const state = {
lspServerVersion: serverVersion,
config: config.extensionConfiguration,
projects,
workspaceFolders: Array.from(workspaceFolders),
runtimePathCache: utils.getRuntimePathCacheSnapshot(),
};

// Format JSON with pretty-printing (2-space indent) on the server side
// This ensures consistent formatting and handles any Maps/Sets that might
// have been converted to plain objects/arrays above
const formattedJson = JSON.stringify(state, null, 2);

// Write the file to disk on the server side
const outputFile = utils.createFileInTempDir("_server_state.json");
fs.writeFileSync(outputFile, formattedJson, { encoding: "utf-8" });

// Request the client to open the document
const fileUri = utils.pathToURI(outputFile);
const showDocumentRequest: p.RequestMessage = {
jsonrpc: c.jsonrpcVersion,
id: serverSentRequestIdCounter++,
method: "window/showDocument",
params: {
uri: fileUri,
external: false,
takeFocus: true,
},
};
send(showDocumentRequest);

let response: p.ResponseMessage = {
jsonrpc: c.jsonrpcVersion,
id: msg.id,
result: { uri: fileUri },
};
return response;
} catch (e) {
let response: p.ResponseMessage = {
jsonrpc: c.jsonrpcVersion,
id: msg.id,
error: {
code: p.ErrorCodes.InternalError,
message: `Failed to dump server state: ${String(e)}`,
},
};
return response;
}
}

async function onMessage(msg: p.Message) {
if (p.Message.isNotification(msg)) {
// notification message, aka the client ends it and doesn't want a reply
Expand Down Expand Up @@ -1458,6 +1546,9 @@ async function onMessage(msg: p.Message) {
retriggerCharacters: ["=", ","],
}
: undefined,
executeCommandProvider: {
commands: ["rescript/dumpServerState"],
},
},
};
let response: p.ResponseMessage = {
Expand Down Expand Up @@ -1555,47 +1646,18 @@ async function onMessage(msg: p.Message) {
if (extName === c.resExt) {
send(await signatureHelp(msg));
}
} else if (msg.method === "rescript/dumpServerState") {
// Custom debug endpoint: dump current server state (config + projectsFiles)
try {
const projects = Array.from(projectsFiles.entries()).map(
([projectRootPath, pf]) => ({
projectRootPath,
openFiles: Array.from(pf.openFiles),
filesWithDiagnostics: Array.from(pf.filesWithDiagnostics),
filesDiagnostics: pf.filesDiagnostics,
rescriptVersion: pf.rescriptVersion,
bscBinaryLocation: pf.bscBinaryLocation,
editorAnalysisLocation: pf.editorAnalysisLocation,
namespaceName: pf.namespaceName,
hasPromptedToStartBuild: pf.hasPromptedToStartBuild,
bsbWatcherByEditor:
pf.bsbWatcherByEditor != null
? { pid: pf.bsbWatcherByEditor.pid ?? null }
: null,
}),
);

const result = {
config: config.extensionConfiguration,
projects,
workspaceFolders: Array.from(workspaceFolders),
runtimePathCache: utils.getRuntimePathCacheSnapshot(),
};

let response: p.ResponseMessage = {
jsonrpc: c.jsonrpcVersion,
id: msg.id,
result,
};
send(response);
} catch (e) {
} else if (msg.method === p.ExecuteCommandRequest.method) {
// Standard LSP executeCommand - supports editor-agnostic command execution
const params = msg.params as p.ExecuteCommandParams;
if (params.command === "rescript/dumpServerState") {
send(await dumpServerState(msg));
} else {
let response: p.ResponseMessage = {
jsonrpc: c.jsonrpcVersion,
id: msg.id,
error: {
code: p.ErrorCodes.InternalError,
message: `Failed to dump server state: ${String(e)}`,
code: p.ErrorCodes.InvalidRequest,
message: `Unknown command: ${params.command}`,
},
};
send(response);
Expand Down