@@ -5,6 +5,7 @@ import * as rpc from "vscode-jsonrpc/node";
55import * as path from "path" ;
66import semver from "semver" ;
77import fs from "fs" ;
8+ import fsAsync from "fs/promises" ;
89import {
910 DidChangeWatchedFilesNotification ,
1011 DidOpenTextDocumentNotification ,
@@ -1260,6 +1261,80 @@ function openCompiledFile(msg: p.RequestMessage): p.Message {
12601261 return response ;
12611262}
12621263
1264+ async function dumpServerState (
1265+ msg : p . RequestMessage ,
1266+ ) : Promise < p . ResponseMessage > {
1267+ // Custom debug endpoint: dump current server state (config + projectsFiles)
1268+ try {
1269+ // Read the server version from package.json
1270+ let serverVersion : string | undefined ;
1271+ try {
1272+ const packageJsonPath = path . join ( __dirname , ".." , "package.json" ) ;
1273+ const packageJsonContent = await fsAsync . readFile ( packageJsonPath , {
1274+ encoding : "utf-8" ,
1275+ } ) ;
1276+ const packageJson : { version ?: unknown } = JSON . parse ( packageJsonContent ) ;
1277+ serverVersion =
1278+ typeof packageJson . version === "string"
1279+ ? packageJson . version
1280+ : undefined ;
1281+ } catch ( e ) {
1282+ // If we can't read the version, that's okay - we'll just omit it
1283+ serverVersion = undefined ;
1284+ }
1285+
1286+ const projects = Array . from ( projectsFiles . entries ( ) ) . map (
1287+ ( [ projectRootPath , pf ] ) => ( {
1288+ projectRootPath,
1289+ openFiles : Array . from ( pf . openFiles ) ,
1290+ filesWithDiagnostics : Array . from ( pf . filesWithDiagnostics ) ,
1291+ filesDiagnostics : pf . filesDiagnostics ,
1292+ rescriptVersion : pf . rescriptVersion ,
1293+ bscBinaryLocation : pf . bscBinaryLocation ,
1294+ editorAnalysisLocation : pf . editorAnalysisLocation ,
1295+ namespaceName : pf . namespaceName ,
1296+ hasPromptedToStartBuild : pf . hasPromptedToStartBuild ,
1297+ bsbWatcherByEditor :
1298+ pf . bsbWatcherByEditor != null
1299+ ? { pid : pf . bsbWatcherByEditor . pid ?? null }
1300+ : null ,
1301+ } ) ,
1302+ ) ;
1303+
1304+ const state = {
1305+ lspServerVersion : serverVersion ,
1306+ config : config . extensionConfiguration ,
1307+ projects,
1308+ workspaceFolders : Array . from ( workspaceFolders ) ,
1309+ runtimePathCache : utils . getRuntimePathCacheSnapshot ( ) ,
1310+ } ;
1311+
1312+ // Format JSON with pretty-printing (2-space indent) on the server side
1313+ // This ensures consistent formatting and handles any Maps/Sets that might
1314+ // have been converted to plain objects/arrays above
1315+ const formattedJson = JSON . stringify ( state , null , 2 ) ;
1316+
1317+ // Return the content so the client can create an unsaved document
1318+ // This avoids creating temporary files that would never be cleaned up
1319+ let response : p . ResponseMessage = {
1320+ jsonrpc : c . jsonrpcVersion ,
1321+ id : msg . id ,
1322+ result : { content : formattedJson } ,
1323+ } ;
1324+ return response ;
1325+ } catch ( e ) {
1326+ let response : p . ResponseMessage = {
1327+ jsonrpc : c . jsonrpcVersion ,
1328+ id : msg . id ,
1329+ error : {
1330+ code : p . ErrorCodes . InternalError ,
1331+ message : `Failed to dump server state: ${ String ( e ) } ` ,
1332+ } ,
1333+ } ;
1334+ return response ;
1335+ }
1336+ }
1337+
12631338async function onMessage ( msg : p . Message ) {
12641339 if ( p . Message . isNotification ( msg ) ) {
12651340 // notification message, aka the client ends it and doesn't want a reply
@@ -1458,6 +1533,9 @@ async function onMessage(msg: p.Message) {
14581533 retriggerCharacters : [ "=" , "," ] ,
14591534 }
14601535 : undefined ,
1536+ executeCommandProvider : {
1537+ commands : [ "rescript/dumpServerState" ] ,
1538+ } ,
14611539 } ,
14621540 } ;
14631541 let response : p . ResponseMessage = {
@@ -1555,47 +1633,18 @@ async function onMessage(msg: p.Message) {
15551633 if ( extName === c . resExt ) {
15561634 send ( await signatureHelp ( msg ) ) ;
15571635 }
1558- } else if ( msg . method === "rescript/dumpServerState" ) {
1559- // Custom debug endpoint: dump current server state (config + projectsFiles)
1560- try {
1561- const projects = Array . from ( projectsFiles . entries ( ) ) . map (
1562- ( [ projectRootPath , pf ] ) => ( {
1563- projectRootPath,
1564- openFiles : Array . from ( pf . openFiles ) ,
1565- filesWithDiagnostics : Array . from ( pf . filesWithDiagnostics ) ,
1566- filesDiagnostics : pf . filesDiagnostics ,
1567- rescriptVersion : pf . rescriptVersion ,
1568- bscBinaryLocation : pf . bscBinaryLocation ,
1569- editorAnalysisLocation : pf . editorAnalysisLocation ,
1570- namespaceName : pf . namespaceName ,
1571- hasPromptedToStartBuild : pf . hasPromptedToStartBuild ,
1572- bsbWatcherByEditor :
1573- pf . bsbWatcherByEditor != null
1574- ? { pid : pf . bsbWatcherByEditor . pid ?? null }
1575- : null ,
1576- } ) ,
1577- ) ;
1578-
1579- const result = {
1580- config : config . extensionConfiguration ,
1581- projects,
1582- workspaceFolders : Array . from ( workspaceFolders ) ,
1583- runtimePathCache : utils . getRuntimePathCacheSnapshot ( ) ,
1584- } ;
1585-
1586- let response : p . ResponseMessage = {
1587- jsonrpc : c . jsonrpcVersion ,
1588- id : msg . id ,
1589- result,
1590- } ;
1591- send ( response ) ;
1592- } catch ( e ) {
1636+ } else if ( msg . method === p . ExecuteCommandRequest . method ) {
1637+ // Standard LSP executeCommand - supports editor-agnostic command execution
1638+ const params = msg . params as p . ExecuteCommandParams ;
1639+ if ( params . command === "rescript/dumpServerState" ) {
1640+ send ( await dumpServerState ( msg ) ) ;
1641+ } else {
15931642 let response : p . ResponseMessage = {
15941643 jsonrpc : c . jsonrpcVersion ,
15951644 id : msg . id ,
15961645 error : {
1597- code : p . ErrorCodes . InternalError ,
1598- message : `Failed to dump server state : ${ String ( e ) } ` ,
1646+ code : p . ErrorCodes . InvalidRequest ,
1647+ message : `Unknown command : ${ params . command } ` ,
15991648 } ,
16001649 } ;
16011650 send ( response ) ;
0 commit comments