diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f528e2f0..84b2353f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ #### :house: Internal - Auto-format vendored OCaml sources like in compiler repo. https://github.com/rescript-lang/rescript-vscode/pull/1053 +- All OCaml sources in this repo is now considered "legacy", as the OCaml parts of the editor integration are now shipped with the compiler instead. ## 1.58.0 diff --git a/client/src/commands/code_analysis.ts b/client/src/commands/code_analysis.ts index 322844d33..d8eeaec20 100644 --- a/client/src/commands/code_analysis.ts +++ b/client/src/commands/code_analysis.ts @@ -14,7 +14,7 @@ import { OutputChannel, StatusBarItem, } from "vscode"; -import { analysisProdPath, getAnalysisBinaryPath } from "../utils"; +import { findProjectRootOfFileInDir, getBinaryPath } from "../utils"; export let statusBarItem = { setToStopText: (codeAnalysisRunningStatusBarItem: StatusBarItem) => { @@ -208,9 +208,19 @@ export const runCodeAnalysisWithReanalyze = ( let currentDocument = window.activeTextEditor.document; let cwd = targetDir ?? path.dirname(currentDocument.uri.fsPath); - let binaryPath = getAnalysisBinaryPath(); + let projectRootPath: string | null = findProjectRootOfFileInDir( + currentDocument.uri.fsPath + ); + + // This little weird lookup is because in the legacy setup reanalyze needs to be + // run from the analysis binary, whereas in the new setup it's run from the tools + // binary. + let binaryPath = + getBinaryPath("rescript-tools.exe", projectRootPath) ?? + getBinaryPath("rescript-editor-analysis.exe"); + if (binaryPath === null) { - window.showErrorMessage("Binary executable not found.", analysisProdPath); + window.showErrorMessage("Binary executable not found."); return; } diff --git a/client/src/commands/dump_debug.ts b/client/src/commands/dump_debug.ts index ff1e3cbd0..168d87a22 100644 --- a/client/src/commands/dump_debug.ts +++ b/client/src/commands/dump_debug.ts @@ -7,7 +7,11 @@ import { ViewColumn, window, } from "vscode"; -import { createFileInTempDir, getAnalysisBinaryPath } from "../utils"; +import { + createFileInTempDir, + findProjectRootOfFileInDir, + getBinaryPath, +} from "../utils"; import * as path from "path"; // Maps to Cli.ml @@ -132,7 +136,11 @@ export const dumpDebug = async ( const { line: endLine, character: endChar } = editor.selection.end; const filePath = editor.document.uri.fsPath; - const binaryPath = getAnalysisBinaryPath(); + let projectRootPath: string | null = findProjectRootOfFileInDir(filePath); + const binaryPath = getBinaryPath( + "rescript-editor-analysis.exe", + projectRootPath + ); if (binaryPath === null) { window.showErrorMessage("Binary executable not found."); return; diff --git a/client/src/utils.ts b/client/src/utils.ts index 0c6ba3ea7..397c1a593 100644 --- a/client/src/utils.ts +++ b/client/src/utils.ts @@ -1,31 +1,50 @@ import * as path from "path"; import * as fs from "fs"; import * as os from "os"; +import { DocumentUri } from "vscode-languageclient"; + +/* + * Much of the code in here is duplicated from the server code. + * At some point we should move the functionality powered by this + * to the server itself. + */ + +type binaryName = "rescript-editor-analysis.exe" | "rescript-tools.exe"; const platformDir = process.arch === "arm64" ? process.platform + process.arch : process.platform; -const analysisDevPath = path.join( - path.dirname(__dirname), - "..", - "analysis", - "rescript-editor-analysis.exe" -); - -export const analysisProdPath = path.join( - path.dirname(__dirname), - "..", - "server", - "analysis_binaries", - platformDir, - "rescript-editor-analysis.exe" -); - -export const getAnalysisBinaryPath = (): string | null => { - if (fs.existsSync(analysisDevPath)) { - return analysisDevPath; - } else if (fs.existsSync(analysisProdPath)) { - return analysisProdPath; +const getLegacyBinaryDevPath = (b: binaryName) => + path.join(path.dirname(__dirname), "..", "analysis", b); + +export const getLegacyBinaryProdPath = (b: binaryName) => + path.join( + path.dirname(__dirname), + "..", + "server", + "analysis_binaries", + platformDir, + b + ); + +export const getBinaryPath = ( + binaryName: "rescript-editor-analysis.exe" | "rescript-tools.exe", + projectRootPath: string | null = null +): string | null => { + const binaryFromCompilerPackage = path.join( + projectRootPath ?? "", + "node_modules", + "rescript", + platformDir, + binaryName + ); + + if (projectRootPath != null && fs.existsSync(binaryFromCompilerPackage)) { + return binaryFromCompilerPackage; + } else if (fs.existsSync(getLegacyBinaryDevPath(binaryName))) { + return getLegacyBinaryDevPath(binaryName); + } else if (fs.existsSync(getLegacyBinaryProdPath(binaryName))) { + return getLegacyBinaryProdPath(binaryName); } else { return null; } @@ -39,3 +58,22 @@ export const createFileInTempDir = (prefix = "", extension = "") => { tempFileId = tempFileId + 1; return path.join(os.tmpdir(), tempFileName); }; + +export let findProjectRootOfFileInDir = ( + source: DocumentUri +): null | DocumentUri => { + let dir = path.dirname(source); + if ( + fs.existsSync(path.join(dir, "rescript.json")) || + fs.existsSync(path.join(dir, "bsconfig.json")) + ) { + return dir; + } else { + if (dir === source) { + // reached top + return null; + } else { + return findProjectRootOfFileInDir(dir); + } + } +}; diff --git a/server/src/constants.ts b/server/src/constants.ts index a08525523..fe569b1cd 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -10,21 +10,22 @@ export let jsonrpcVersion = "2.0"; export let platformPath = path.join("rescript", platformDir); export let nodeModulesPlatformPath = path.join("node_modules", platformPath); export let bscExeName = "bsc.exe"; +export let editorAnalysisName = "rescript-editor-analysis.exe"; export let bscNativeReScriptPartialPath = path.join( nodeModulesPlatformPath, bscExeName ); -export let analysisDevPath = path.join( +export let builtinAnalysisDevPath = path.join( path.dirname(__dirname), "..", - "rescript-editor-analysis.exe" + editorAnalysisName ); -export let analysisProdPath = path.join( +export let builtinAnalysisProdPath = path.join( path.dirname(__dirname), "analysis_binaries", platformDir, - "rescript-editor-analysis.exe" + editorAnalysisName ); export let rescriptBinName = "rescript"; diff --git a/server/src/projectFiles.ts b/server/src/projectFiles.ts index 5520a479b..4a1787a0c 100644 --- a/server/src/projectFiles.ts +++ b/server/src/projectFiles.ts @@ -11,6 +11,7 @@ interface projectFiles { filesDiagnostics: filesDiagnostics; rescriptVersion: string | undefined; bscBinaryLocation: string | null; + editorAnalysisLocation: string | null; namespaceName: string | null; bsbWatcherByEditor: null | cp.ChildProcess; diff --git a/server/src/server.ts b/server/src/server.ts index c7e0cf2c0..f41c13219 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -272,6 +272,7 @@ let openedFile = (fileUri: string, fileContent: string) => { rescriptVersion: utils.findReScriptVersion(projectRootPath), bsbWatcherByEditor: null, bscBinaryLocation: utils.findBscExeBinary(projectRootPath), + editorAnalysisLocation: utils.findEditorAnalysisBinary(projectRootPath), hasPromptedToStartBuild: /(\/|\\)node_modules(\/|\\)/.test( projectRootPath ) diff --git a/server/src/utils.ts b/server/src/utils.ts index 0b8d3ef3d..6f386cc83 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -192,12 +192,12 @@ export let findReScriptVersion = ( } }; -let binaryPath: string | null = null; -if (fs.existsSync(c.analysisDevPath)) { - binaryPath = c.analysisDevPath; -} else if (fs.existsSync(c.analysisProdPath)) { - binaryPath = c.analysisProdPath; -} else { +// This is the path for the _builtin_ legacy analysis, that works for versions 11 and below. +let builtinBinaryPath: string | null = null; +if (fs.existsSync(c.builtinAnalysisDevPath)) { + builtinBinaryPath = c.builtinAnalysisDevPath; +} else if (fs.existsSync(c.builtinAnalysisProdPath)) { + builtinBinaryPath = c.builtinAnalysisProdPath; } export let runAnalysisAfterSanityCheck = ( @@ -205,10 +205,6 @@ export let runAnalysisAfterSanityCheck = ( args: Array, projectRequired = false ) => { - if (binaryPath == null) { - return null; - } - let projectRootPath = findProjectRootOfFile(filePath); if (projectRootPath == null && projectRequired) { return null; @@ -217,6 +213,35 @@ export let runAnalysisAfterSanityCheck = ( projectsFiles.get(projectRootPath ?? "")?.rescriptVersion ?? findReScriptVersion(filePath); + let binaryPath = builtinBinaryPath; + + let project = projectRootPath ? projectsFiles.get(projectRootPath) : null; + + /** + * All versions including 12.0.0-alpha.5 and above should use the analysis binary + * that now ships with the compiler. Previous versions use the legacy one we ship + * with the extension itself. + */ + let shouldUseBuiltinAnalysis = + rescriptVersion?.startsWith("9.") || + rescriptVersion?.startsWith("10.") || + rescriptVersion?.startsWith("11.") || + [ + "12.0.0-alpha.1", + "12.0.0-alpha.2", + "12.0.0-alpha.3", + "12.0.0-alpha.4", + ].includes(rescriptVersion ?? ""); + + if (!shouldUseBuiltinAnalysis && project != null) { + binaryPath = project.editorAnalysisLocation; + } else if (!shouldUseBuiltinAnalysis && project == null) { + // TODO: Warn user about broken state? + return null; + } else { + binaryPath = builtinBinaryPath; + } + let options: childProcess.ExecFileSyncOptions = { cwd: projectRootPath || undefined, maxBuffer: Infinity, @@ -233,6 +258,11 @@ export let runAnalysisAfterSanityCheck = ( : undefined, }, }; + + if (binaryPath == null) { + return null; + } + let stdout = ""; try { stdout = childProcess.execFileSync(binaryPath, args, options).toString(); @@ -737,3 +767,6 @@ let findPlatformPath = (projectRootPath: p.DocumentUri | null) => { export let findBscExeBinary = (projectRootPath: p.DocumentUri | null) => findBinary(findPlatformPath(projectRootPath), c.bscExeName); + +export let findEditorAnalysisBinary = (projectRootPath: p.DocumentUri | null) => + findBinary(findPlatformPath(projectRootPath), c.editorAnalysisName);