diff --git a/README.md b/README.md index 66c007084..e67a69018 100644 --- a/README.md +++ b/README.md @@ -104,14 +104,15 @@ Even if the pre-release channel seems too experimental to you, we still suggest You'll find all ReScript specific settings under the scope `rescript.settings`. -| Setting | Description | -| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Prompt to Start Build | If there's no ReScript build running already in the opened project, the extension will prompt you and ask if you want to start a build automatically. You can turn off this automatic prompt via the setting `rescript.settings.askToStartBuild`. | -| ReScript Binary Path | The extension will look for the existence of a `/node_modules/.bin/rescript` file and use its directory as the `binaryPath`. If it does not find it at the project root (which is where the nearest `bsconfig.json` resides), it goes up folders in the filesystem recursively until it either finds it (often the case in monorepos) or hits the top level. To override this lookup process, the path can be configured explicitly using the setting `rescript.settings.binaryPath` | -| Inlay Hints (experimental) | This allows an editor to place annotations inline with text to display type hints. Enable using `rescript.settings.inlayHints.enable: true` | -| Code Lens (experimental) | This tells the editor to add code lenses to function definitions, showing its full type above the definition. Enable using `rescript.settings.codeLens: true` | -| Signature Help (experimental) | This tells the editor to show signature help when you're writing function calls. Enable using `rescript.settings.signatureHelp.enable: true` | -| Autostarting the Code Analyzer | The Code Analyzer needs to be started manually by default. However, you can configure the extension to start the Code Analyzer automatically via the setting `rescript.settings.autoRunCodeAnalysis`. | +| Setting | Description | +| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Prompt to Start Build | If there's no ReScript build running already in the opened project, the extension will prompt you and ask if you want to start a build automatically. You can turn off this automatic prompt via the setting `rescript.settings.askToStartBuild`. | +| ReScript Binary Path | The extension will look for the existence of a `node_modules/.bin/rescript` file and use its directory as the `binaryPath`. If it does not find it at the project root (which is where the nearest `bsconfig.json` resides), it goes up folders in the filesystem recursively until it either finds it (often the case in monorepos) or hits the top level. To override this lookup process, the path can be configured explicitly using the setting `rescript.settings.binaryPath` | +| ReScript Platform Path | The extension will look for the existence of a `node_modules/rescript` directory and use the subdirectory corresponding to the current platform as the `platformPath`. If it does not find it at the project root (which is where the nearest `bsconfig.json` resides), it goes up folders in the filesystem recursively until it either finds it (often the case in monorepos) or hits the top level. To override this lookup process, the path can be configured explicitly using the setting `rescript.settings.platformPath` | +| Inlay Hints (experimental) | This allows an editor to place annotations inline with text to display type hints. Enable using `rescript.settings.inlayHints.enable: true` | +| Code Lens (experimental) | This tells the editor to add code lenses to function definitions, showing its full type above the definition. Enable using `rescript.settings.codeLens: true` | +| Signature Help (experimental) | This tells the editor to show signature help when you're writing function calls. Enable using `rescript.settings.signatureHelp.enable: true` | +| Autostarting the Code Analyzer | The Code Analyzer needs to be started manually by default. However, you can configure the extension to start the Code Analyzer automatically via the setting `rescript.settings.autoRunCodeAnalysis`. | **Default settings:** @@ -122,9 +123,12 @@ You'll find all ReScript specific settings under the scope `rescript.settings`. // Automatically start ReScript's code analysis. "rescript.settings.autoRunCodeAnalysis": false, -// Path to the directory where ReScript binaries are. You can use it if you haven't or don't want to use the installed ReScript from node_modules in your project. +// Path to the directory where cross-platform ReScript binaries are. You can use it if you haven't or don't want to use the installed ReScript from node_modules in your project. "rescript.settings.binaryPath": null +// Path to the directory where platform-specific ReScript binaries are. You can use it if you haven't or don't want to use the installed ReScript from node_modules in your project. +"rescript.settings.platformPath": null + // Enable (experimental) inlay hints. "rescript.settings.inlayHints.enable": true diff --git a/package.json b/package.json index 3a4144a4a..910d974ed 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,12 @@ "rescript.settings.binaryPath": { "type": ["string", "null"], "default": null, - "description": "Path to the directory where ReScript binaries are. You can use it if you haven't or don't want to use the installed ReScript from node_modules in your project." + "description": "Path to the directory where cross-platform ReScript binaries are. You can use it if you haven't or don't want to use the installed ReScript from node_modules in your project." + }, + "rescript.settings.platformPath": { + "type": ["string", "null"], + "default": null, + "description": "Path to the directory where platform-specific ReScript binaries are. You can use it if you haven't or don't want to use the installed ReScript from node_modules in your project." } } }, diff --git a/server/src/server.ts b/server/src/server.ts index 92c79d670..7a1e374ea 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -34,6 +34,7 @@ interface extensionConfiguration { }; codeLens: boolean; binaryPath: string | null; + platformPath: string | null; signatureHelp: { enable: boolean; }; @@ -58,6 +59,7 @@ let extensionConfiguration: extensionConfiguration = { }, codeLens: false, binaryPath: null, + platformPath: null, signatureHelp: { enable: false, }, @@ -98,60 +100,37 @@ let codeActionsFromDiagnostics: codeActions.filesCodeActions = {}; // will be properly defined later depending on the mode (stdio/node-rpc) let send: (msg: p.Message) => void = (_) => {}; -// Check if the rescript binary is available at node_modules/.bin/rescript, -// otherwise recursively check parent directories for it. -let findBinaryPathFromProjectRoot = ( - directory: p.DocumentUri // This must be a directory and not a file! -): null | p.DocumentUri => { - let binaryDirPath = path.join(directory, c.nodeModulesBinDir); - let binaryPath = path.join(binaryDirPath, c.rescriptBinName); +let findRescriptBinary = (projectRootPath: p.DocumentUri | null) => + extensionConfiguration.binaryPath == null + ? utils.findFilePathFromProjectRoot(projectRootPath, path.join(c.nodeModulesBinDir, c.rescriptBinName)) + : utils.findBinary(extensionConfiguration.binaryPath, c.rescriptBinName); - if (fs.existsSync(binaryPath)) { - return binaryPath; +let findPlatformPath = (projectRootPath: p.DocumentUri | null) => { + if (extensionConfiguration.platformPath != null) { + return extensionConfiguration.platformPath; } - let parentDir = path.dirname(directory); - if (parentDir === directory) { - // reached the top + let rescriptDir = utils.findFilePathFromProjectRoot(projectRootPath, path.join("node_modules", "rescript")); + if (rescriptDir == null) { return null; } - return findBinaryPathFromProjectRoot(parentDir); -}; - -let findRescriptBinary = (projectRootPath: p.DocumentUri) => - extensionConfiguration.binaryPath == null - ? findBinaryPathFromProjectRoot(projectRootPath) - : utils.findRescriptBinary(extensionConfiguration.binaryPath); - -let findBscBinary = (projectRootPath: p.DocumentUri) => { - let rescriptBinaryPath = findRescriptBinary(projectRootPath); - if (rescriptBinaryPath !== null) { - let rescriptDirPath = path.join( - path.dirname(rescriptBinaryPath), - "..", - "rescript" - ); + let platformPath = path.join(rescriptDir, c.platformDir) - let bscBinaryPath = path.join(rescriptDirPath, c.platformDir, c.bscExeName); + // Workaround for darwinarm64 which has no folder yet in ReScript <= 9.1.4 + if ( + process.platform == "darwin" && + process.arch == "arm64" && + !fs.existsSync(platformPath) + ) { + platformPath = path.join(rescriptDir, process.platform); + } - // Workaround for darwinarm64 which has no folder yet in ReScript <= 9.1.4 - if ( - process.platform == "darwin" && - process.arch == "arm64" && - !fs.existsSync(bscBinaryPath) - ) { - bscBinaryPath = path.join( - rescriptDirPath, - process.platform, - c.bscExeName - ); - } + return platformPath; +} - return bscBinaryPath; - } - return null; -}; +let findBscExeBinary = (projectRootPath: p.DocumentUri | null) => + utils.findBinary(findPlatformPath(projectRootPath), c.bscExeName) interface CreateInterfaceRequestParams { uri: string; @@ -786,12 +765,12 @@ function format(msg: p.RequestMessage): Array { } else { // code will always be defined here, even though technically it can be undefined let code = getOpenedFileContent(params.textDocument.uri); + let projectRootPath = utils.findProjectRootOfFile(filePath); - let bscBinaryPath = - projectRootPath === null ? null : findBscBinary(projectRootPath); + let bscExeBinaryPath = findBscExeBinary(projectRootPath); let formattedResult = utils.formatCode( - bscBinaryPath, + bscExeBinaryPath, filePath, code, extensionConfiguration.allowBuiltInFormatter diff --git a/server/src/utils.ts b/server/src/utils.ts index d92ad1a81..5360e10ec 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -38,6 +38,31 @@ export let findProjectRootOfFile = ( } }; +// Check if filePartialPath exists at directory and return the joined path, +// otherwise recursively check parent directories for it. +export let findFilePathFromProjectRoot = ( + directory: p.DocumentUri | null, // This must be a directory and not a file! + filePartialPath: string +): null | p.DocumentUri => { + if (directory == null) { + return null; + } + + let filePath: p.DocumentUri = path.join(directory, filePartialPath); + if (fs.existsSync(filePath)) { + return filePath; + } + + let parentDir: p.DocumentUri = path.dirname(directory); + if (parentDir === directory) { + // reached the top + return null; + } + + return findFilePathFromProjectRoot(parentDir, filePartialPath); +}; + +// Check if binaryName exists inside binaryDirPath and return the joined path. export let findBinary = ( binaryDirPath: p.DocumentUri | null, binaryName: string @@ -53,18 +78,6 @@ export let findBinary = ( } }; -export let findRescriptBinary = ( - binaryDirPath: p.DocumentUri | null -): p.DocumentUri | null => { - return findBinary(binaryDirPath, c.rescriptBinName); -}; - -export let findBscBinary = ( - binaryDirPath: p.DocumentUri | null -): p.DocumentUri | null => { - return findBinary(binaryDirPath, c.bscBinName); -}; - type execResult = | { kind: "success";