Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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.

27 changes: 25 additions & 2 deletions client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
WorkspaceEdit,
CodeActionKind,
Diagnostic,
ViewColumn,
} from "vscode";
import { ThemeColor } from "vscode";

Expand Down Expand Up @@ -373,8 +374,30 @@ 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 () => {
try {
const result = (await client.sendRequest("workspace/executeCommand", {
command: "rescript/dumpServerState",
})) as { content: string };

// Create an unsaved document with the server state content
const document = await workspace.openTextDocument({
content: result.content,
language: "json",
});

// Show the document in the editor
await window.showTextDocument(document, {
viewColumn: ViewColumn.Beside,
preview: false,
});
} catch (e) {
outputChannel.appendLine(`Failed to dump server state: ${String(e)}`);
window.showErrorMessage(
"Failed to dump server state. See 'Output' tab, 'ReScript Language Server' channel for details.",
);
outputChannel.show();
}
});

commands.registerCommand("rescript-vscode.showProblems", async () => {
Expand Down
123 changes: 86 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,80 @@ 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: { version?: unknown } = JSON.parse(packageJsonContent);
serverVersion =
typeof packageJson.version === "string"
? packageJson.version
: undefined;
} 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);

// Return the content so the client can create an unsaved document
// This avoids creating temporary files that would never be cleaned up
let response: p.ResponseMessage = {
jsonrpc: c.jsonrpcVersion,
id: msg.id,
result: { content: formattedJson },
};
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 +1533,9 @@ async function onMessage(msg: p.Message) {
retriggerCharacters: ["=", ","],
}
: undefined,
executeCommandProvider: {
commands: ["rescript/dumpServerState"],
},
},
};
let response: p.ResponseMessage = {
Expand Down Expand Up @@ -1555,47 +1633,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