diff --git a/server/src/server.ts b/server/src/server.ts index 411c4a8f8..855a7d84a 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -20,6 +20,7 @@ import { assert } from "console"; import { fileURLToPath } from "url"; import { ChildProcess } from "child_process"; import { WorkspaceEdit } from "vscode-languageserver"; +import { filesDiagnostics } from "./utils"; interface extensionConfiguration { askToStartBuild: boolean; @@ -41,6 +42,8 @@ let projectsFiles: Map< { openFiles: Set; filesWithDiagnostics: Set; + filesDiagnostics: filesDiagnostics; + bsbWatcherByEditor: null | ChildProcess; // This keeps track of whether we've prompted the user to start a build @@ -79,8 +82,22 @@ let openCompiledFileRequest = new v.RequestType< void >("rescript-vscode.open_compiled"); +let getCurrentCompilerDiagnosticsForFile = ( + fileUri: string +): p.Diagnostic[] => { + let diagnostics: p.Diagnostic[] | null = null; + + projectsFiles.forEach((projectFile, _projectRootPath) => { + if (diagnostics == null && projectFile.filesDiagnostics[fileUri] != null) { + diagnostics = projectFile.filesDiagnostics[fileUri].slice(); + } + }); + + return diagnostics ?? []; +}; let sendUpdatedDiagnostics = () => { - projectsFiles.forEach(({ filesWithDiagnostics }, projectRootPath) => { + projectsFiles.forEach((projectFile, projectRootPath) => { + let { filesWithDiagnostics } = projectFile; let content = fs.readFileSync( path.join(projectRootPath, c.compilerLogPartialPath), { encoding: "utf-8" } @@ -91,6 +108,7 @@ let sendUpdatedDiagnostics = () => { codeActions, } = utils.parseCompilerLogOutput(content); + projectFile.filesDiagnostics = filesAndErrors; codeActionsFromDiagnostics = codeActions; // diff @@ -188,6 +206,7 @@ let openedFile = (fileUri: string, fileContent: string) => { projectRootState = { openFiles: new Set(), filesWithDiagnostics: new Set(), + filesDiagnostics: {}, bsbWatcherByEditor: null, hasPromptedToStartBuild: /(\/|\\)node_modules(\/|\\)/.test( projectRootPath @@ -608,17 +627,22 @@ let updateDiagnosticSyntax = (fileUri: string, fileContent: string) => { let tmpname = utils.createFileInTempDir(extension); fs.writeFileSync(tmpname, fileContent, { encoding: "utf-8" }); - let items: p.Diagnostic[] | [] = utils.runAnalysisAfterSanityCheck(filePath, [ - "diagnosticSyntax", - tmpname, - ]); + // We need to account for any existing diagnostics from the compiler for this + // file. If we don't we might accidentally clear the current file's compiler + // diagnostics if there's no syntax diagostics to send. This is because + // publishing an empty diagnostics array is equivalent to saying "clear all + // errors". + let compilerDiagnosticsForFile = + getCurrentCompilerDiagnosticsForFile(fileUri); + let syntaxDiagnosticsForFile: p.Diagnostic[] = + utils.runAnalysisAfterSanityCheck(filePath, ["diagnosticSyntax", tmpname]); let notification: p.NotificationMessage = { jsonrpc: c.jsonrpcVersion, method: "textDocument/publishDiagnostics", params: { uri: fileUri, - diagnostics: items, + diagnostics: [...syntaxDiagnosticsForFile, ...compilerDiagnosticsForFile], }, }; diff --git a/server/src/utils.ts b/server/src/utils.ts index c67fb5fd7..0e814e470 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -474,7 +474,7 @@ let parseFileAndRange = (fileAndRange: string) => { }; // main parsing logic -type filesDiagnostics = { +export type filesDiagnostics = { [key: string]: p.Diagnostic[]; }; type parsedCompilerLogResult = {