Skip to content

Commit f3eb068

Browse files
committed
The binaryPath option was added to the rescript.settings
1 parent 10a5b8d commit f3eb068

File tree

4 files changed

+103
-22
lines changed

4 files changed

+103
-22
lines changed

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@
131131
"type": "boolean",
132132
"default": false,
133133
"description": "Automatically start ReScript's code analysis."
134+
},
135+
"rescript.settings.binaryPath": {
136+
"type": ["string", "null"],
137+
"default": null,
138+
"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."
134139
}
135140
}
136141
},

server/src/constants.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,19 @@ export let analysisProdPath = path.join(
3232
"rescript-editor-analysis.exe"
3333
);
3434

35+
export let rescriptBinName = "rescript";
36+
37+
export let bsbBinName = "bsb";
38+
39+
export let bscBinName = "bsc";
40+
3541
// can't use the native bsb/rescript since we might need the watcher -w flag, which is only in the JS wrapper
3642
export let rescriptNodePartialPath = path.join(
3743
"node_modules",
3844
".bin",
39-
"rescript"
45+
rescriptBinName,
4046
);
41-
export let bsbNodePartialPath = path.join("node_modules", ".bin", "bsb");
47+
export let bsbNodePartialPath = path.join("node_modules", ".bin", bsbBinName);
4248

4349
export let bsbLock = ".bsb.lock";
4450
export let bsconfigPartialPath = "bsconfig.json";

server/src/server.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as v from "vscode-languageserver";
44
import * as rpc from "vscode-jsonrpc/node";
55
import * as path from "path";
66
import fs from "fs";
7+
import os from "os";
78
// TODO: check DidChangeWatchedFilesNotification.
89
import {
910
DidOpenTextDocumentNotification,
@@ -24,9 +25,11 @@ import { filesDiagnostics } from "./utils";
2425

2526
interface extensionConfiguration {
2627
askToStartBuild: boolean;
28+
binaryPath: string | null;
2729
}
2830
let extensionConfiguration: extensionConfiguration = {
2931
askToStartBuild: true,
32+
binaryPath: null,
3033
};
3134
let pullConfigurationPeriodically: NodeJS.Timeout | null = null;
3235

@@ -62,6 +65,11 @@ let codeActionsFromDiagnostics: codeActions.filesCodeActions = {};
6265
// will be properly defined later depending on the mode (stdio/node-rpc)
6366
let send: (msg: p.Message) => void = (_) => {};
6467

68+
let findBinary = (projectRootPath: p.DocumentUri) =>
69+
extensionConfiguration.binaryPath === null
70+
? utils.findNodeBuildOfProjectRoot(projectRootPath)
71+
: utils.findBinaryFromConfig(extensionConfiguration.binaryPath);
72+
6573
interface CreateInterfaceRequestParams {
6674
uri: string;
6775
}
@@ -232,7 +240,7 @@ let openedFile = (fileUri: string, fileContent: string) => {
232240
// TODO: sometime stale .bsb.lock dangling. bsb -w knows .bsb.lock is
233241
// stale. Use that logic
234242
// TODO: close watcher when lang-server shuts down
235-
if (utils.findNodeBuildOfProjectRoot(projectRootPath) != null) {
243+
if (findBinary(projectRootPath) != null) {
236244
let payload: clientSentBuildAction = {
237245
title: c.startBuildAction,
238246
projectRootPath: projectRootPath,
@@ -254,7 +262,20 @@ let openedFile = (fileUri: string, fileContent: string) => {
254262
// handle in the isResponseMessage check in the message handling way
255263
// below
256264
} else {
257-
// we should send something to say that we can't find bsb.exe. But right now we'll silently not do anything
265+
let binaryPath =
266+
extensionConfiguration.binaryPath === null
267+
? path.join(projectRootPath, "node_modules", ".bin")
268+
: extensionConfiguration.binaryPath;
269+
270+
let request: p.NotificationMessage = {
271+
jsonrpc: c.jsonrpcVersion,
272+
method: "window/showMessage",
273+
params: {
274+
type: p.MessageType.Error,
275+
message: `Can't find ReScript binary on path ${binaryPath}`,
276+
},
277+
};
278+
send(request);
258279
}
259280
}
260281

@@ -593,7 +614,11 @@ function format(msg: p.RequestMessage): Array<p.Message> {
593614
} else {
594615
// code will always be defined here, even though technically it can be undefined
595616
let code = getOpenedFileContent(params.textDocument.uri);
596-
let formattedResult = utils.formatCode(filePath, code);
617+
let formattedResult = utils.formatCode(
618+
extensionConfiguration.binaryPath,
619+
filePath,
620+
code
621+
);
597622
if (formattedResult.kind === "success") {
598623
let max = code.length;
599624
let result: p.TextEdit[] = [
@@ -933,6 +958,17 @@ function onMessage(msg: p.Message) {
933958

934959
if (initialConfiguration != null) {
935960
extensionConfiguration = initialConfiguration;
961+
if (
962+
extensionConfiguration.binaryPath !== null &&
963+
extensionConfiguration.binaryPath[0] === "~"
964+
) {
965+
// What should happen if the path contains the home directory symbol?
966+
// This situation is handled below, but maybe it isn't the best option.
967+
extensionConfiguration.binaryPath = path.join(
968+
os.homedir(),
969+
extensionConfiguration.binaryPath.slice(1)
970+
);
971+
}
936972
}
937973

938974
send(response);
@@ -1041,7 +1077,7 @@ function onMessage(msg: p.Message) {
10411077
// TODO: close watcher when lang-server shuts down. However, by Node's
10421078
// default, these subprocesses are automatically killed when this
10431079
// language-server process exits
1044-
let found = utils.findNodeBuildOfProjectRoot(projectRootPath);
1080+
let found = findBinary(projectRootPath);
10451081
if (found != null) {
10461082
let bsbProcess = utils.runBuildWatcherUsingValidBuildPath(
10471083
found.buildPath,

server/src/utils.ts

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,21 +70,47 @@ export let findBscNativeOfFile = (
7070
}
7171
};
7272

73+
let findBscBinFromConfig = (
74+
pathToBinFromConfig: p.DocumentUri | null
75+
): null | p.DocumentUri => {
76+
if (pathToBinFromConfig === null) {
77+
return null;
78+
}
79+
let bscPath = path.join(pathToBinFromConfig, c.bscBinName);
80+
if (fs.existsSync(bscPath)) {
81+
return bscPath;
82+
}
83+
return null;
84+
};
85+
7386
// TODO: this doesn't handle file:/// scheme
74-
export let findNodeBuildOfProjectRoot = (
75-
projectRootPath: p.DocumentUri
76-
): null | { buildPath: p.DocumentUri; isReScript: boolean } => {
77-
let rescriptNodePath = path.join(projectRootPath, c.rescriptNodePartialPath);
78-
let bsbNodePath = path.join(projectRootPath, c.bsbNodePartialPath);
79-
80-
if (fs.existsSync(rescriptNodePath)) {
81-
return { buildPath: rescriptNodePath, isReScript: true };
82-
} else if (fs.existsSync(bsbNodePath)) {
83-
return { buildPath: bsbNodePath, isReScript: false };
87+
let findBinaryBase = ({
88+
rescriptPath,
89+
bsbPath,
90+
}: {
91+
rescriptPath: p.DocumentUri;
92+
bsbPath: p.DocumentUri;
93+
}): null | { buildPath: p.DocumentUri; isReScript: boolean } => {
94+
if (fs.existsSync(rescriptPath)) {
95+
return { buildPath: rescriptPath, isReScript: true };
96+
} else if (fs.existsSync(bsbPath)) {
97+
return { buildPath: bsbPath, isReScript: false };
8498
}
8599
return null;
86100
};
87101

102+
export let findBinaryFromConfig = (pathToBinFromConfig: p.DocumentUri) =>
103+
findBinaryBase({
104+
rescriptPath: path.join(pathToBinFromConfig, c.rescriptBinName),
105+
bsbPath: path.join(pathToBinFromConfig, c.bsbBinName),
106+
});
107+
108+
export let findNodeBuildOfProjectRoot = (projectRootPath: p.DocumentUri) =>
109+
findBinaryBase({
110+
rescriptPath: path.join(projectRootPath, c.rescriptNodePartialPath),
111+
bsbPath: path.join(projectRootPath, c.bsbNodePartialPath),
112+
});
113+
88114
type execResult =
89115
| {
90116
kind: "success";
@@ -94,21 +120,29 @@ type execResult =
94120
kind: "error";
95121
error: string;
96122
};
97-
export let formatCode = (filePath: string, code: string): execResult => {
123+
export let formatCode = (
124+
pathToBinFromConfig: p.DocumentUri | null,
125+
filePath: string,
126+
code: string
127+
): execResult => {
98128
let extension = path.extname(filePath);
99129
let formatTempFileFullPath = createFileInTempDir(extension);
100130
fs.writeFileSync(formatTempFileFullPath, code, {
101131
encoding: "utf-8",
102132
});
103133
try {
134+
// Try to find the bsc bin from the binaryPath setting from the configuration.
135+
let bscPath = findBscBinFromConfig(pathToBinFromConfig);
136+
104137
// See comment on findBscNativeDirOfFile for why we need
105138
// to recursively search for bsc.exe upward
106-
let bscNativePath = findBscNativeOfFile(filePath);
139+
bscPath = bscPath == null ? findBscNativeOfFile(filePath) : bscPath;
107140

108-
// Default to using the project formatter. If not, use the one we ship with
109-
// the analysis binary in the extension itself.
110-
if (bscNativePath != null) {
111-
let result = childProcess.execFileSync(bscNativePath, [
141+
// Default to using the formatter from the binaryPath setting from the configuration
142+
// or the project formatter.
143+
// If not, use the one we ship with the analysis binary in the extension itself.
144+
if (bscPath != null) {
145+
let result = childProcess.execFileSync(bscPath, [
112146
"-color",
113147
"never",
114148
"-format",

0 commit comments

Comments
 (0)