From e72872ee1158b54ecac5f2aa49fda21e76523ba5 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 11 Aug 2025 18:27:36 +0200 Subject: [PATCH 1/3] try/catch on trying to read compiler log, since it sometimes fails --- server/src/server.ts | 133 ++++++++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 45 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index e845b14ca..9bec672b5 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -60,14 +60,20 @@ let codeActionsFromDiagnostics: codeActions.filesCodeActions = {}; // will be properly defined later depending on the mode (stdio/node-rpc) let send: (msg: p.Message) => void = (_) => {}; -let findRescriptBinary = async (projectRootPath: p.DocumentUri | null): Promise => { - if (config.extensionConfiguration.binaryPath != null && - fs.existsSync(path.join(config.extensionConfiguration.binaryPath, "rescript"))) { - return path.join(config.extensionConfiguration.binaryPath, "rescript") +let findRescriptBinary = async ( + projectRootPath: p.DocumentUri | null +): Promise => { + if ( + config.extensionConfiguration.binaryPath != null && + fs.existsSync( + path.join(config.extensionConfiguration.binaryPath, "rescript") + ) + ) { + return path.join(config.extensionConfiguration.binaryPath, "rescript"); } - return utils.findRescriptBinary(projectRootPath) -} + return utils.findRescriptBinary(projectRootPath); +}; let createInterfaceRequest = new v.RequestType< p.TextDocumentIdentifier, @@ -99,7 +105,12 @@ let sendUpdatedDiagnostics = async () => { for (const [projectRootPath, projectFile] of projectsFiles) { let { filesWithDiagnostics } = projectFile; let compilerLogPath = path.join(projectRootPath, c.compilerLogPartialPath); - let content = fs.readFileSync(compilerLogPath, { encoding: "utf-8" }); + let content = ""; + try { + content = fs.readFileSync(compilerLogPath, { encoding: "utf-8" }); + } catch (e) { + console.error(`Error reading compiler log file ${compilerLogPath}: ${e}`); + } let { done, result: filesAndErrors, @@ -197,7 +208,10 @@ let debug = false; let syncProjectConfigCache = async (rootPath: string) => { try { if (debug) console.log("syncing project config cache for " + rootPath); - await utils.runAnalysisAfterSanityCheck(rootPath, ["cache-project", rootPath]); + await utils.runAnalysisAfterSanityCheck(rootPath, [ + "cache-project", + rootPath, + ]); if (debug) console.log("OK - synced project config cache for " + rootPath); } catch (e) { if (debug) console.error(e); @@ -207,7 +221,10 @@ let syncProjectConfigCache = async (rootPath: string) => { let deleteProjectConfigCache = async (rootPath: string) => { try { if (debug) console.log("deleting project config cache for " + rootPath); - await utils.runAnalysisAfterSanityCheck(rootPath, ["cache-delete", rootPath]); + await utils.runAnalysisAfterSanityCheck(rootPath, [ + "cache-delete", + rootPath, + ]); if (debug) console.log("OK - deleted project config cache for " + rootPath); } catch (e) { if (debug) console.error(e); @@ -217,31 +234,35 @@ let deleteProjectConfigCache = async (rootPath: string) => { async function onWorkspaceDidChangeWatchedFiles( params: p.DidChangeWatchedFilesParams ) { - await Promise.all(params.changes.map(async (change) => { - if (change.uri.includes("build.ninja")) { - if (config.extensionConfiguration.cache?.projectConfig?.enable === true) { - let projectRoot = utils.findProjectRootOfFile(change.uri); - if (projectRoot != null) { - await syncProjectConfigCache(projectRoot); - } - } - } else if (change.uri.includes("compiler.log")) { - try { - await sendUpdatedDiagnostics(); - sendCompilationFinishedMessage(); - if (config.extensionConfiguration.inlayHints?.enable === true) { - sendInlayHintsRefresh(); + await Promise.all( + params.changes.map(async (change) => { + if (change.uri.includes("build.ninja")) { + if ( + config.extensionConfiguration.cache?.projectConfig?.enable === true + ) { + let projectRoot = utils.findProjectRootOfFile(change.uri); + if (projectRoot != null) { + await syncProjectConfigCache(projectRoot); + } } - if (config.extensionConfiguration.codeLens === true) { - sendCodeLensRefresh(); + } else if (change.uri.includes("compiler.log")) { + try { + await sendUpdatedDiagnostics(); + sendCompilationFinishedMessage(); + if (config.extensionConfiguration.inlayHints?.enable === true) { + sendInlayHintsRefresh(); + } + if (config.extensionConfiguration.codeLens === true) { + sendCodeLensRefresh(); + } + } catch { + console.log("Error while sending updated diagnostics"); } - } catch { - console.log("Error while sending updated diagnostics"); + } else { + ic.incrementalCompilationFileChanged(fileURLToPath(change.uri)); } - } else { - ic.incrementalCompilationFileChanged(fileURLToPath(change.uri)); - } - })); + }) + ); } type clientSentBuildAction = { @@ -269,10 +290,14 @@ let openedFile = async (fileUri: string, fileContent: string) => { filesDiagnostics: {}, namespaceName: namespaceName.kind === "success" ? namespaceName.result : null, - rescriptVersion: await utils.findReScriptVersionForProjectRoot(projectRootPath), + rescriptVersion: await utils.findReScriptVersionForProjectRoot( + projectRootPath + ), bsbWatcherByEditor: null, bscBinaryLocation: await utils.findBscExeBinary(projectRootPath), - editorAnalysisLocation: await utils.findEditorAnalysisBinary(projectRootPath), + editorAnalysisLocation: await utils.findEditorAnalysisBinary( + projectRootPath + ), hasPromptedToStartBuild: /(\/|\\)node_modules(\/|\\)/.test( projectRootPath ) @@ -297,7 +322,7 @@ let openedFile = async (fileUri: string, fileContent: string) => { // TODO: sometime stale .bsb.lock dangling. bsb -w knows .bsb.lock is // stale. Use that logic // TODO: close watcher when lang-server shuts down - if (await findRescriptBinary(projectRootPath) != null) { + if ((await findRescriptBinary(projectRootPath)) != null) { let payload: clientSentBuildAction = { title: c.startBuildAction, projectRootPath: projectRootPath, @@ -536,10 +561,8 @@ async function references(msg: p.RequestMessage) { // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references let params = msg.params as p.ReferenceParams; let filePath = fileURLToPath(params.textDocument.uri); - let result: typeof p.ReferencesRequest.type = await utils.getReferencesForPosition( - filePath, - params.position - ); + let result: typeof p.ReferencesRequest.type = + await utils.getReferencesForPosition(filePath, params.position); let response: p.ResponseMessage = { jsonrpc: c.jsonrpcVersion, id: msg.id, @@ -549,7 +572,9 @@ async function references(msg: p.RequestMessage) { return response; } -async function prepareRename(msg: p.RequestMessage): Promise { +async function prepareRename( + msg: p.RequestMessage +): Promise { // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_prepareRename let params = msg.params as p.PrepareRenameParams; let filePath = fileURLToPath(params.textDocument.uri); @@ -840,7 +865,10 @@ let updateDiagnosticSyntax = async (fileUri: string, fileContent: string) => { let compilerDiagnosticsForFile = getCurrentCompilerDiagnosticsForFile(fileUri); let syntaxDiagnosticsForFile: p.Diagnostic[] = - await utils.runAnalysisAfterSanityCheck(filePath, ["diagnosticSyntax", tmpname]); + await utils.runAnalysisAfterSanityCheck(filePath, [ + "diagnosticSyntax", + tmpname, + ]); let notification: p.NotificationMessage = { jsonrpc: c.jsonrpcVersion, @@ -1051,17 +1079,29 @@ async function onMessage(msg: p.Message) { const watchers = Array.from(workspaceFolders).flatMap( (projectRootPath) => [ { - globPattern: path.join(projectRootPath, '**', c.compilerLogPartialPath), + globPattern: path.join( + projectRootPath, + "**", + c.compilerLogPartialPath + ), kind: p.WatchKind.Change | p.WatchKind.Create | p.WatchKind.Delete, }, { - globPattern: path.join(projectRootPath, '**', c.buildNinjaPartialPath), + globPattern: path.join( + projectRootPath, + "**", + c.buildNinjaPartialPath + ), kind: p.WatchKind.Change | p.WatchKind.Create | p.WatchKind.Delete, }, { - globPattern: `${path.join(projectRootPath, '**', c.compilerDirPartialPath)}/**/*.{cmt,cmi}`, + globPattern: `${path.join( + projectRootPath, + "**", + c.compilerDirPartialPath + )}/**/*.{cmt,cmi}`, kind: p.WatchKind.Change | p.WatchKind.Delete, - } + }, ] ); const registrationParams: p.RegistrationParams = { @@ -1089,7 +1129,10 @@ async function onMessage(msg: p.Message) { let params = msg.params as p.DidOpenTextDocumentParams; await openedFile(params.textDocument.uri, params.textDocument.text); await sendUpdatedDiagnostics(); - await updateDiagnosticSyntax(params.textDocument.uri, params.textDocument.text); + await updateDiagnosticSyntax( + params.textDocument.uri, + params.textDocument.text + ); } else if (msg.method === DidChangeTextDocumentNotification.method) { let params = msg.params as p.DidChangeTextDocumentParams; let extName = path.extname(params.textDocument.uri); From a5b7436186785c2580546ebd779297ac05d9a522 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 11 Aug 2025 18:28:57 +0200 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13e3a8ea1..237abe834 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ ## master +#### :bug: Bug fix + +- Protect against trying to read non-existant `.compiler.log`. https://github.com/rescript-lang/rescript-vscode/pull/1116 + ## 1.64.0 #### :rocket: New Feature From 65f7254cf27482f4d37ebf901c8b54628539a4d6 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 11 Aug 2025 18:30:02 +0200 Subject: [PATCH 3/3] restore formatting --- server/src/server.ts | 126 +++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 82 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index 9bec672b5..d51433822 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -60,20 +60,14 @@ let codeActionsFromDiagnostics: codeActions.filesCodeActions = {}; // will be properly defined later depending on the mode (stdio/node-rpc) let send: (msg: p.Message) => void = (_) => {}; -let findRescriptBinary = async ( - projectRootPath: p.DocumentUri | null -): Promise => { - if ( - config.extensionConfiguration.binaryPath != null && - fs.existsSync( - path.join(config.extensionConfiguration.binaryPath, "rescript") - ) - ) { - return path.join(config.extensionConfiguration.binaryPath, "rescript"); +let findRescriptBinary = async (projectRootPath: p.DocumentUri | null): Promise => { + if (config.extensionConfiguration.binaryPath != null && + fs.existsSync(path.join(config.extensionConfiguration.binaryPath, "rescript"))) { + return path.join(config.extensionConfiguration.binaryPath, "rescript") } - return utils.findRescriptBinary(projectRootPath); -}; + return utils.findRescriptBinary(projectRootPath) +} let createInterfaceRequest = new v.RequestType< p.TextDocumentIdentifier, @@ -208,10 +202,7 @@ let debug = false; let syncProjectConfigCache = async (rootPath: string) => { try { if (debug) console.log("syncing project config cache for " + rootPath); - await utils.runAnalysisAfterSanityCheck(rootPath, [ - "cache-project", - rootPath, - ]); + await utils.runAnalysisAfterSanityCheck(rootPath, ["cache-project", rootPath]); if (debug) console.log("OK - synced project config cache for " + rootPath); } catch (e) { if (debug) console.error(e); @@ -221,10 +212,7 @@ let syncProjectConfigCache = async (rootPath: string) => { let deleteProjectConfigCache = async (rootPath: string) => { try { if (debug) console.log("deleting project config cache for " + rootPath); - await utils.runAnalysisAfterSanityCheck(rootPath, [ - "cache-delete", - rootPath, - ]); + await utils.runAnalysisAfterSanityCheck(rootPath, ["cache-delete", rootPath]); if (debug) console.log("OK - deleted project config cache for " + rootPath); } catch (e) { if (debug) console.error(e); @@ -234,35 +222,31 @@ let deleteProjectConfigCache = async (rootPath: string) => { async function onWorkspaceDidChangeWatchedFiles( params: p.DidChangeWatchedFilesParams ) { - await Promise.all( - params.changes.map(async (change) => { - if (change.uri.includes("build.ninja")) { - if ( - config.extensionConfiguration.cache?.projectConfig?.enable === true - ) { - let projectRoot = utils.findProjectRootOfFile(change.uri); - if (projectRoot != null) { - await syncProjectConfigCache(projectRoot); - } + await Promise.all(params.changes.map(async (change) => { + if (change.uri.includes("build.ninja")) { + if (config.extensionConfiguration.cache?.projectConfig?.enable === true) { + let projectRoot = utils.findProjectRootOfFile(change.uri); + if (projectRoot != null) { + await syncProjectConfigCache(projectRoot); } - } else if (change.uri.includes("compiler.log")) { - try { - await sendUpdatedDiagnostics(); - sendCompilationFinishedMessage(); - if (config.extensionConfiguration.inlayHints?.enable === true) { - sendInlayHintsRefresh(); - } - if (config.extensionConfiguration.codeLens === true) { - sendCodeLensRefresh(); - } - } catch { - console.log("Error while sending updated diagnostics"); + } + } else if (change.uri.includes("compiler.log")) { + try { + await sendUpdatedDiagnostics(); + sendCompilationFinishedMessage(); + if (config.extensionConfiguration.inlayHints?.enable === true) { + sendInlayHintsRefresh(); } - } else { - ic.incrementalCompilationFileChanged(fileURLToPath(change.uri)); + if (config.extensionConfiguration.codeLens === true) { + sendCodeLensRefresh(); + } + } catch { + console.log("Error while sending updated diagnostics"); } - }) - ); + } else { + ic.incrementalCompilationFileChanged(fileURLToPath(change.uri)); + } + })); } type clientSentBuildAction = { @@ -290,14 +274,10 @@ let openedFile = async (fileUri: string, fileContent: string) => { filesDiagnostics: {}, namespaceName: namespaceName.kind === "success" ? namespaceName.result : null, - rescriptVersion: await utils.findReScriptVersionForProjectRoot( - projectRootPath - ), + rescriptVersion: await utils.findReScriptVersionForProjectRoot(projectRootPath), bsbWatcherByEditor: null, bscBinaryLocation: await utils.findBscExeBinary(projectRootPath), - editorAnalysisLocation: await utils.findEditorAnalysisBinary( - projectRootPath - ), + editorAnalysisLocation: await utils.findEditorAnalysisBinary(projectRootPath), hasPromptedToStartBuild: /(\/|\\)node_modules(\/|\\)/.test( projectRootPath ) @@ -322,7 +302,7 @@ let openedFile = async (fileUri: string, fileContent: string) => { // TODO: sometime stale .bsb.lock dangling. bsb -w knows .bsb.lock is // stale. Use that logic // TODO: close watcher when lang-server shuts down - if ((await findRescriptBinary(projectRootPath)) != null) { + if (await findRescriptBinary(projectRootPath) != null) { let payload: clientSentBuildAction = { title: c.startBuildAction, projectRootPath: projectRootPath, @@ -561,8 +541,10 @@ async function references(msg: p.RequestMessage) { // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references let params = msg.params as p.ReferenceParams; let filePath = fileURLToPath(params.textDocument.uri); - let result: typeof p.ReferencesRequest.type = - await utils.getReferencesForPosition(filePath, params.position); + let result: typeof p.ReferencesRequest.type = await utils.getReferencesForPosition( + filePath, + params.position + ); let response: p.ResponseMessage = { jsonrpc: c.jsonrpcVersion, id: msg.id, @@ -572,9 +554,7 @@ async function references(msg: p.RequestMessage) { return response; } -async function prepareRename( - msg: p.RequestMessage -): Promise { +async function prepareRename(msg: p.RequestMessage): Promise { // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_prepareRename let params = msg.params as p.PrepareRenameParams; let filePath = fileURLToPath(params.textDocument.uri); @@ -865,10 +845,7 @@ let updateDiagnosticSyntax = async (fileUri: string, fileContent: string) => { let compilerDiagnosticsForFile = getCurrentCompilerDiagnosticsForFile(fileUri); let syntaxDiagnosticsForFile: p.Diagnostic[] = - await utils.runAnalysisAfterSanityCheck(filePath, [ - "diagnosticSyntax", - tmpname, - ]); + await utils.runAnalysisAfterSanityCheck(filePath, ["diagnosticSyntax", tmpname]); let notification: p.NotificationMessage = { jsonrpc: c.jsonrpcVersion, @@ -1079,29 +1056,17 @@ async function onMessage(msg: p.Message) { const watchers = Array.from(workspaceFolders).flatMap( (projectRootPath) => [ { - globPattern: path.join( - projectRootPath, - "**", - c.compilerLogPartialPath - ), + globPattern: path.join(projectRootPath, '**', c.compilerLogPartialPath), kind: p.WatchKind.Change | p.WatchKind.Create | p.WatchKind.Delete, }, { - globPattern: path.join( - projectRootPath, - "**", - c.buildNinjaPartialPath - ), + globPattern: path.join(projectRootPath, '**', c.buildNinjaPartialPath), kind: p.WatchKind.Change | p.WatchKind.Create | p.WatchKind.Delete, }, { - globPattern: `${path.join( - projectRootPath, - "**", - c.compilerDirPartialPath - )}/**/*.{cmt,cmi}`, + globPattern: `${path.join(projectRootPath, '**', c.compilerDirPartialPath)}/**/*.{cmt,cmi}`, kind: p.WatchKind.Change | p.WatchKind.Delete, - }, + } ] ); const registrationParams: p.RegistrationParams = { @@ -1129,10 +1094,7 @@ async function onMessage(msg: p.Message) { let params = msg.params as p.DidOpenTextDocumentParams; await openedFile(params.textDocument.uri, params.textDocument.text); await sendUpdatedDiagnostics(); - await updateDiagnosticSyntax( - params.textDocument.uri, - params.textDocument.text - ); + await updateDiagnosticSyntax(params.textDocument.uri, params.textDocument.text); } else if (msg.method === DidChangeTextDocumentNotification.method) { let params = msg.params as p.DidChangeTextDocumentParams; let extName = path.extname(params.textDocument.uri);