From 81aaa113b5dff9e985fecf98efc5db45fd2f1316 Mon Sep 17 00:00:00 2001 From: Anton Tuzhik Date: Sun, 10 Jul 2022 22:26:28 +0200 Subject: [PATCH 1/9] code formatting --- server/src/utils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/utils.ts b/server/src/utils.ts index 0e814e470..01ed4c8af 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -589,7 +589,7 @@ export let parseCompilerLogOutput = ( code: undefined, severity: t.DiagnosticSeverity.Error, tag: undefined, - content: [lines[i], lines[i+1]], + content: [lines[i], lines[i + 1]], }); i++; } else if (/^ +([0-9]+| +|\.) (│|┆)/.test(line)) { @@ -603,9 +603,9 @@ export let parseCompilerLogOutput = ( // 10 ┆ } else if (line.startsWith(" ")) { // part of the actual diagnostics message - parsedDiagnostics[parsedDiagnostics.length - 1].content.push( - line.slice(2) - ); + parsedDiagnostics[parsedDiagnostics.length - 1].content.push( + line.slice(2) + ); } else if (line.trim() != "") { // We'll assume that everything else is also part of the diagnostics too. // Most of these should have been indented 2 spaces; sadly, some of them From 7e21142f54f3a99d4a45deafc6f93d22f2b32ecd Mon Sep 17 00:00:00 2001 From: Anton Tuzhik Date: Sun, 10 Jul 2022 22:29:58 +0200 Subject: [PATCH 2/9] binaryPath was added to the settings --- package.json | 5 +++++ server/src/server.ts | 2 ++ 2 files changed, 7 insertions(+) diff --git a/package.json b/package.json index 950827877..8f7972c98 100644 --- a/package.json +++ b/package.json @@ -131,6 +131,11 @@ "type": "boolean", "default": false, "description": "Automatically start ReScript's code analysis." + }, + "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." } } }, diff --git a/server/src/server.ts b/server/src/server.ts index 855a7d84a..f3d9ed84c 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -24,9 +24,11 @@ import { filesDiagnostics } from "./utils"; interface extensionConfiguration { askToStartBuild: boolean; + binaryPath: string | null; } let extensionConfiguration: extensionConfiguration = { askToStartBuild: true, + binaryPath: null, }; let pullConfigurationPeriodically: NodeJS.Timeout | null = null; From 4deba1e98fc0005335eecd2a27d14a89746b53ea Mon Sep 17 00:00:00 2001 From: Anton Tuzhik Date: Sun, 10 Jul 2022 22:32:11 +0200 Subject: [PATCH 3/9] The handling of the paths which start with the home directory symbol was added. --- server/src/server.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/src/server.ts b/server/src/server.ts index f3d9ed84c..ac40415d2 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -4,6 +4,7 @@ import * as v from "vscode-languageserver"; import * as rpc from "vscode-jsonrpc/node"; import * as path from "path"; import fs from "fs"; +import os from "os"; // TODO: check DidChangeWatchedFilesNotification. import { DidOpenTextDocumentNotification, @@ -935,6 +936,17 @@ function onMessage(msg: p.Message) { if (initialConfiguration != null) { extensionConfiguration = initialConfiguration; + if ( + extensionConfiguration.binaryPath !== null && + extensionConfiguration.binaryPath[0] === "~" + ) { + // What should happen if the path contains the home directory symbol? + // This situation is handled below, but maybe it isn't the best option. + extensionConfiguration.binaryPath = path.join( + os.homedir(), + extensionConfiguration.binaryPath.slice(1) + ); + } } send(response); From 810809837498cc64bbe7854666918da4e7dc4c47 Mon Sep 17 00:00:00 2001 From: Anton Tuzhik Date: Sun, 10 Jul 2022 22:46:36 +0200 Subject: [PATCH 4/9] The binaryPath setting was added to the formatCode function --- server/src/constants.ts | 2 ++ server/src/server.ts | 6 +++++- server/src/utils.ts | 34 ++++++++++++++++++++++++++++------ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/server/src/constants.ts b/server/src/constants.ts index 223a00f7b..da494746d 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -32,6 +32,8 @@ export let analysisProdPath = path.join( "rescript-editor-analysis.exe" ); +export let bscBinName = "bsc"; + // can't use the native bsb/rescript since we might need the watcher -w flag, which is only in the JS wrapper export let rescriptNodePartialPath = path.join( "node_modules", diff --git a/server/src/server.ts b/server/src/server.ts index ac40415d2..af54f0782 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -596,7 +596,11 @@ 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 formattedResult = utils.formatCode(filePath, code); + let formattedResult = utils.formatCode( + extensionConfiguration.binaryPath, + filePath, + code + ); if (formattedResult.kind === "success") { let max = code.length; let result: p.TextEdit[] = [ diff --git a/server/src/utils.ts b/server/src/utils.ts index 01ed4c8af..de450c90f 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -70,6 +70,19 @@ export let findBscNativeOfFile = ( } }; +let findBscBinFromConfig = ( + pathToBinFromConfig: p.DocumentUri | null +): null | p.DocumentUri => { + if (pathToBinFromConfig === null) { + return null; + } + let bscPath = path.join(pathToBinFromConfig, c.bscBinName); + if (fs.existsSync(bscPath)) { + return bscPath; + } + return null; +}; + // TODO: this doesn't handle file:/// scheme export let findNodeBuildOfProjectRoot = ( projectRootPath: p.DocumentUri @@ -94,21 +107,30 @@ type execResult = kind: "error"; error: string; }; -export let formatCode = (filePath: string, code: string): execResult => { + +export let formatCode = ( + pathToBinFromConfig: p.DocumentUri | null, + filePath: string, + code: string +): execResult => { let extension = path.extname(filePath); let formatTempFileFullPath = createFileInTempDir(extension); fs.writeFileSync(formatTempFileFullPath, code, { encoding: "utf-8", }); try { + // Try to find the bsc bin from the binaryPath setting from the configuration. + let bscPath = findBscBinFromConfig(pathToBinFromConfig); + // See comment on findBscNativeDirOfFile for why we need // to recursively search for bsc.exe upward - let bscNativePath = findBscNativeOfFile(filePath); + bscPath = bscPath == null ? findBscNativeOfFile(filePath) : bscPath; - // Default to using the project formatter. If not, use the one we ship with - // the analysis binary in the extension itself. - if (bscNativePath != null) { - let result = childProcess.execFileSync(bscNativePath, [ + // Default to using the formatter from the binaryPath setting from the configuration + // or the project formatter. + // If not, use the one we ship with the analysis binary in the extension itself. + if (bscPath != null) { + let result = childProcess.execFileSync(bscPath, [ "-color", "never", "-format", From 6828f96db27c7cbe73ac1ae3dcb8392f5f5f7d93 Mon Sep 17 00:00:00 2001 From: Anton Tuzhik Date: Sun, 10 Jul 2022 22:51:33 +0200 Subject: [PATCH 5/9] The binaryPath setting is using to find resctipt binary --- server/src/constants.ts | 8 ++++++-- server/src/server.ts | 9 +++++++-- server/src/utils.ts | 33 +++++++++++++++++++++++---------- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/server/src/constants.ts b/server/src/constants.ts index da494746d..bd073f1aa 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -32,15 +32,19 @@ export let analysisProdPath = path.join( "rescript-editor-analysis.exe" ); +export let rescriptBinName = "rescript"; + +export let bsbBinName = "bsb"; + export let bscBinName = "bsc"; // can't use the native bsb/rescript since we might need the watcher -w flag, which is only in the JS wrapper export let rescriptNodePartialPath = path.join( "node_modules", ".bin", - "rescript" + rescriptBinName, ); -export let bsbNodePartialPath = path.join("node_modules", ".bin", "bsb"); +export let bsbNodePartialPath = path.join("node_modules", ".bin", bsbBinName); export let bsbLock = ".bsb.lock"; export let bsconfigPartialPath = "bsconfig.json"; diff --git a/server/src/server.ts b/server/src/server.ts index af54f0782..e185b9a16 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -65,6 +65,11 @@ let codeActionsFromDiagnostics: codeActions.filesCodeActions = {}; // will be properly defined later depending on the mode (stdio/node-rpc) let send: (msg: p.Message) => void = (_) => {}; +let findBinary = (projectRootPath: p.DocumentUri) => + extensionConfiguration.binaryPath === null + ? utils.findNodeBuildOfProjectRoot(projectRootPath) + : utils.findBinaryFromConfig(extensionConfiguration.binaryPath); + interface CreateInterfaceRequestParams { uri: string; } @@ -235,7 +240,7 @@ let openedFile = (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 (utils.findNodeBuildOfProjectRoot(projectRootPath) != null) { + if (findBinary(projectRootPath) != null) { let payload: clientSentBuildAction = { title: c.startBuildAction, projectRootPath: projectRootPath, @@ -1059,7 +1064,7 @@ function onMessage(msg: p.Message) { // TODO: close watcher when lang-server shuts down. However, by Node's // default, these subprocesses are automatically killed when this // language-server process exits - let found = utils.findNodeBuildOfProjectRoot(projectRootPath); + let found = findBinary(projectRootPath); if (found != null) { let bsbProcess = utils.runBuildWatcherUsingValidBuildPath( found.buildPath, diff --git a/server/src/utils.ts b/server/src/utils.ts index de450c90f..49b1d31d1 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -84,20 +84,33 @@ let findBscBinFromConfig = ( }; // TODO: this doesn't handle file:/// scheme -export let findNodeBuildOfProjectRoot = ( - projectRootPath: p.DocumentUri -): null | { buildPath: p.DocumentUri; isReScript: boolean } => { - let rescriptNodePath = path.join(projectRootPath, c.rescriptNodePartialPath); - let bsbNodePath = path.join(projectRootPath, c.bsbNodePartialPath); - - if (fs.existsSync(rescriptNodePath)) { - return { buildPath: rescriptNodePath, isReScript: true }; - } else if (fs.existsSync(bsbNodePath)) { - return { buildPath: bsbNodePath, isReScript: false }; +let findBinaryBase = ({ + rescriptPath, + bsbPath, +}: { + rescriptPath: p.DocumentUri; + bsbPath: p.DocumentUri; +}): null | { buildPath: p.DocumentUri; isReScript: boolean } => { + if (fs.existsSync(rescriptPath)) { + return { buildPath: rescriptPath, isReScript: true }; + } else if (fs.existsSync(bsbPath)) { + return { buildPath: bsbPath, isReScript: false }; } return null; }; +export let findBinaryFromConfig = (pathToBinFromConfig: p.DocumentUri) => + findBinaryBase({ + rescriptPath: path.join(pathToBinFromConfig, c.rescriptBinName), + bsbPath: path.join(pathToBinFromConfig, c.bsbBinName), + }); + +export let findNodeBuildOfProjectRoot = (projectRootPath: p.DocumentUri) => + findBinaryBase({ + rescriptPath: path.join(projectRootPath, c.rescriptNodePartialPath), + bsbPath: path.join(projectRootPath, c.bsbNodePartialPath), + }); + type execResult = | { kind: "success"; From 505d3cbd8f76dbc4eb4e88cc1cd26866f790fec0 Mon Sep 17 00:00:00 2001 From: Anton Tuzhik Date: Sun, 10 Jul 2022 22:56:26 +0200 Subject: [PATCH 6/9] The notification about the rescript binary absent was added --- server/src/server.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/server/src/server.ts b/server/src/server.ts index e185b9a16..2b2a5ae63 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -262,7 +262,20 @@ let openedFile = (fileUri: string, fileContent: string) => { // handle in the isResponseMessage check in the message handling way // below } else { - // we should send something to say that we can't find bsb.exe. But right now we'll silently not do anything + let binaryPath = + extensionConfiguration.binaryPath === null + ? path.join(projectRootPath, "node_modules", ".bin") + : extensionConfiguration.binaryPath; + + let request: p.NotificationMessage = { + jsonrpc: c.jsonrpcVersion, + method: "window/showMessage", + params: { + type: p.MessageType.Error, + message: `Can't find ReScript binary on path ${binaryPath}`, + }, + }; + send(request); } } From 99e9f191946ff8f4a54e2168dd940b4f59a59f6d Mon Sep 17 00:00:00 2001 From: Anton Tuzhik Date: Tue, 12 Jul 2022 10:03:01 +0200 Subject: [PATCH 7/9] Fix findBuildBinary naming and fix comments --- server/src/server.ts | 10 +++++----- server/src/utils.ts | 16 +++++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index 2b2a5ae63..c5420c0a1 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -65,10 +65,10 @@ let codeActionsFromDiagnostics: codeActions.filesCodeActions = {}; // will be properly defined later depending on the mode (stdio/node-rpc) let send: (msg: p.Message) => void = (_) => {}; -let findBinary = (projectRootPath: p.DocumentUri) => +let findBuildBinary = (projectRootPath: p.DocumentUri) => extensionConfiguration.binaryPath === null - ? utils.findNodeBuildOfProjectRoot(projectRootPath) - : utils.findBinaryFromConfig(extensionConfiguration.binaryPath); + ? utils.findBuildBinaryFromProjectRoot(projectRootPath) + : utils.findBuildBinaryFromConfig(extensionConfiguration.binaryPath); interface CreateInterfaceRequestParams { uri: string; @@ -240,7 +240,7 @@ let openedFile = (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 (findBinary(projectRootPath) != null) { + if (findBuildBinary(projectRootPath) != null) { let payload: clientSentBuildAction = { title: c.startBuildAction, projectRootPath: projectRootPath, @@ -1077,7 +1077,7 @@ function onMessage(msg: p.Message) { // TODO: close watcher when lang-server shuts down. However, by Node's // default, these subprocesses are automatically killed when this // language-server process exits - let found = findBinary(projectRootPath); + let found = findBuildBinary(projectRootPath); if (found != null) { let bsbProcess = utils.runBuildWatcherUsingValidBuildPath( found.buildPath, diff --git a/server/src/utils.ts b/server/src/utils.ts index 49b1d31d1..94ce70f27 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -83,8 +83,10 @@ let findBscBinFromConfig = ( return null; }; +// The function is to check that the build binary file exists +// and also determine what exactly the user is using ReScript or BuckleScript. // TODO: this doesn't handle file:/// scheme -let findBinaryBase = ({ +let findBuildBinaryBase = ({ rescriptPath, bsbPath, }: { @@ -99,14 +101,14 @@ let findBinaryBase = ({ return null; }; -export let findBinaryFromConfig = (pathToBinFromConfig: p.DocumentUri) => - findBinaryBase({ - rescriptPath: path.join(pathToBinFromConfig, c.rescriptBinName), - bsbPath: path.join(pathToBinFromConfig, c.bsbBinName), +export let findBuildBinaryFromConfig = (pathToBinaryDirFromConfig: p.DocumentUri) => + findBuildBinaryBase({ + rescriptPath: path.join(pathToBinaryDirFromConfig, c.rescriptBinName), + bsbPath: path.join(pathToBinaryDirFromConfig, c.bsbBinName), }); -export let findNodeBuildOfProjectRoot = (projectRootPath: p.DocumentUri) => - findBinaryBase({ +export let findBuildBinaryFromProjectRoot = (projectRootPath: p.DocumentUri) => + findBuildBinaryBase({ rescriptPath: path.join(projectRootPath, c.rescriptNodePartialPath), bsbPath: path.join(projectRootPath, c.bsbNodePartialPath), }); From 15897000c6ba023b73490d877d00765397e45b2c Mon Sep 17 00:00:00 2001 From: Anton Tuzhik Date: Tue, 12 Jul 2022 10:06:08 +0200 Subject: [PATCH 8/9] The search for the path to the bsc is taken out of the formatCode function --- server/src/server.ts | 12 +++++++----- server/src/utils.ts | 25 +++++++------------------ 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/server/src/server.ts b/server/src/server.ts index c5420c0a1..328efa2df 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -70,6 +70,11 @@ let findBuildBinary = (projectRootPath: p.DocumentUri) => ? utils.findBuildBinaryFromProjectRoot(projectRootPath) : utils.findBuildBinaryFromConfig(extensionConfiguration.binaryPath); +let findBscBinary = (filePath: p.DocumentUri) => + extensionConfiguration.binaryPath === null + ? utils.findBscBinaryFromProjectRoot(filePath) + : utils.findBscBinaryFromConfig(extensionConfiguration.binaryPath); + interface CreateInterfaceRequestParams { uri: string; } @@ -614,11 +619,8 @@ 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 formattedResult = utils.formatCode( - extensionConfiguration.binaryPath, - filePath, - code - ); + let bscBinaryPath = findBscBinary(filePath); + let formattedResult = utils.formatCode(bscBinaryPath, filePath, code); if (formattedResult.kind === "success") { let max = code.length; let result: p.TextEdit[] = [ diff --git a/server/src/utils.ts b/server/src/utils.ts index 94ce70f27..344c37879 100644 --- a/server/src/utils.ts +++ b/server/src/utils.ts @@ -48,7 +48,7 @@ export let findProjectRootOfFile = ( // Also, if someone's ever formatting a regular project setup's dependency // (which is weird but whatever), they'll at least find an upward bs-platform // from the dependent. -export let findBscNativeOfFile = ( +export let findBscBinaryFromProjectRoot = ( source: p.DocumentUri ): null | p.DocumentUri => { let dir = path.dirname(source); @@ -66,17 +66,14 @@ export let findBscNativeOfFile = ( // reached the top return null; } else { - return findBscNativeOfFile(dir); + return findBscBinaryFromProjectRoot(dir); } }; -let findBscBinFromConfig = ( - pathToBinFromConfig: p.DocumentUri | null +export let findBscBinaryFromConfig = ( + pathToBinaryDirFromConfig: p.DocumentUri ): null | p.DocumentUri => { - if (pathToBinFromConfig === null) { - return null; - } - let bscPath = path.join(pathToBinFromConfig, c.bscBinName); + let bscPath = path.join(pathToBinaryDirFromConfig, c.bscBinName); if (fs.existsSync(bscPath)) { return bscPath; } @@ -124,7 +121,7 @@ type execResult = }; export let formatCode = ( - pathToBinFromConfig: p.DocumentUri | null, + bscPath: p.DocumentUri | null, filePath: string, code: string ): execResult => { @@ -134,15 +131,7 @@ export let formatCode = ( encoding: "utf-8", }); try { - // Try to find the bsc bin from the binaryPath setting from the configuration. - let bscPath = findBscBinFromConfig(pathToBinFromConfig); - - // See comment on findBscNativeDirOfFile for why we need - // to recursively search for bsc.exe upward - bscPath = bscPath == null ? findBscNativeOfFile(filePath) : bscPath; - - // Default to using the formatter from the binaryPath setting from the configuration - // or the project formatter. + // It will try to use the user formatting binary. // If not, use the one we ship with the analysis binary in the extension itself. if (bscPath != null) { let result = childProcess.execFileSync(bscPath, [ From b6cfb98df78286b444f46d909ac0210ea417a564 Mon Sep 17 00:00:00 2001 From: Anton Tuzhik Date: Tue, 12 Jul 2022 10:11:45 +0200 Subject: [PATCH 9/9] Fix notification about buildBinary absent --- server/src/constants.ts | 9 +++++---- server/src/server.ts | 14 ++++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/server/src/constants.ts b/server/src/constants.ts index bd073f1aa..f6e34e327 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -38,13 +38,14 @@ export let bsbBinName = "bsb"; export let bscBinName = "bsc"; +export let nodeModulesBinDir = path.join("node_modules", ".bin"); + // can't use the native bsb/rescript since we might need the watcher -w flag, which is only in the JS wrapper export let rescriptNodePartialPath = path.join( - "node_modules", - ".bin", - rescriptBinName, + nodeModulesBinDir, + rescriptBinName ); -export let bsbNodePartialPath = path.join("node_modules", ".bin", bsbBinName); +export let bsbNodePartialPath = path.join(nodeModulesBinDir, bsbBinName); export let bsbLock = ".bsb.lock"; export let bsconfigPartialPath = "bsconfig.json"; diff --git a/server/src/server.ts b/server/src/server.ts index 328efa2df..5f6d9ecf3 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -75,6 +75,11 @@ let findBscBinary = (filePath: p.DocumentUri) => ? utils.findBscBinaryFromProjectRoot(filePath) : utils.findBscBinaryFromConfig(extensionConfiguration.binaryPath); +let getConjecturalDirOfBuildBinary = (projectRootPath: p.DocumentUri) => + extensionConfiguration.binaryPath === null + ? path.join(projectRootPath, c.nodeModulesBinDir) + : extensionConfiguration.binaryPath; + interface CreateInterfaceRequestParams { uri: string; } @@ -267,17 +272,14 @@ let openedFile = (fileUri: string, fileContent: string) => { // handle in the isResponseMessage check in the message handling way // below } else { - let binaryPath = - extensionConfiguration.binaryPath === null - ? path.join(projectRootPath, "node_modules", ".bin") - : extensionConfiguration.binaryPath; - let request: p.NotificationMessage = { jsonrpc: c.jsonrpcVersion, method: "window/showMessage", params: { type: p.MessageType.Error, - message: `Can't find ReScript binary on path ${binaryPath}`, + message: `Can't find ReScript binary in the directory ${getConjecturalDirOfBuildBinary( + projectRootPath + )}`, }, }; send(request);