fix(vscode): ignore incompatible versions of the binary and improve the restart logic
MODIFY
editors/vscode/src/main.ts
editors/vscode/src/main.ts
import { type ChildProcess, spawn } from "child_process";
import { connect, type Socket } from "net";
-import { promisify } from "util";
+import { promisify, TextDecoder } from "util";
import {
ExtensionContext,
languages,
@@ -36,7 +36,10 @@ let client: LanguageClient;
const IN_ROME_PROJECT = "inRomeProject";
export async function activate(context: ExtensionContext) {
- const command = await getServerPath(context);
+ const outputChannel = window.createOutputChannel("Rome");
+ const traceOutputChannel = window.createOutputChannel("Rome Trace");
+
+ const command = await getServerPath(context, outputChannel);
if (!command) {
await window.showErrorMessage(
@@ -49,9 +52,6 @@ export async function activate(context: ExtensionContext) {
const statusBar = new StatusBar();
- const outputChannel = window.createOutputChannel("Rome");
- const traceOutputChannel = window.createOutputChannel("Rome Trace");
-
const serverOptions: ServerOptions = createMessageTransports.bind(
undefined,
outputChannel,
@@ -85,10 +85,16 @@ export async function activate(context: ExtensionContext) {
session.registerCommand(Commands.ServerStatus, () => {
traceOutputChannel.show();
});
- session.registerCommand(Commands.RestartLspServer, () => {
- client.restart().catch((error) => {
+ session.registerCommand(Commands.RestartLspServer, async () => {
+ try {
+ if (client.isRunning()) {
+ await client.restart();
+ } else {
+ await client.start();
+ }
+ } catch (error) {
client.error("Restarting client failed", error, "force");
- });
+ }
});
context.subscriptions.push(
@@ -130,45 +136,46 @@ const PLATFORMS: PlatformTriplets = {
win32: {
x64: {
triplet: "x86_64-pc-windows-msvc",
- package: "@rometools/cli-win32-x64/rome.exe",
+ package: "@rometools/cli-win32-x64",
},
arm64: {
triplet: "aarch64-pc-windows-msvc",
- package: "@rometools/cli-win32-arm64/rome.exe",
+ package: "@rometools/cli-win32-arm64",
},
},
darwin: {
x64: {
triplet: "x86_64-apple-darwin",
- package: "@rometools/cli-darwin-x64/rome",
+ package: "@rometools/cli-darwin-x64",
},
arm64: {
triplet: "aarch64-apple-darwin",
- package: "@rometools/cli-darwin-arm64/rome",
+ package: "@rometools/cli-darwin-arm64",
},
},
linux: {
x64: {
triplet: "x86_64-unknown-linux-gnu",
- package: "@rometools/cli-linux-x64/rome",
+ package: "@rometools/cli-linux-x64",
},
arm64: {
triplet: "aarch64-unknown-linux-gnu",
- package: "@rometools/cli-linux-arm64/rome",
+ package: "@rometools/cli-linux-arm64",
},
},
};
async function getServerPath(
context: ExtensionContext,
+ outputChannel: OutputChannel,
): Promise<string | undefined> {
// Only allow the bundled Rome binary in untrusted workspaces
if (!workspace.isTrusted) {
- return getBundledBinary(context);
+ return getBundledBinary(context, outputChannel);
}
if (process.env.DEBUG_SERVER_PATH) {
- window.showInformationMessage(
+ outputChannel.appendLine(
`Rome DEBUG_SERVER_PATH detected: ${process.env.DEBUG_SERVER_PATH}`,
);
return process.env.DEBUG_SERVER_PATH;
@@ -180,7 +187,10 @@ async function getServerPath(
return getWorkspaceRelativePath(explicitPath);
}
- return (await getWorkspaceDependency()) ?? getBundledBinary(context);
+ return (
+ (await getWorkspaceDependency(outputChannel)) ??
+ (await getBundledBinary(context, outputChannel))
+ );
}
// Resolve `path` as relative to the workspace root
@@ -200,18 +210,54 @@ async function getWorkspaceRelativePath(path: string) {
}
// Tries to resolve a path to `@rometools/cli-*` binary package from the root of the workspace
-async function getWorkspaceDependency(): Promise<string | undefined> {
+async function getWorkspaceDependency(
+ outputChannel: OutputChannel,
+): Promise<string | undefined> {
const packageName = PLATFORMS[process.platform]?.[process.arch]?.package;
+ const manifestName = `${packageName}/package.json`;
+ const binaryName =
+ process.platform === "win32"
+ ? `${packageName}/rome.exe`
+ : `${packageName}/rome`;
+
for (const workspaceFolder of workspace.workspaceFolders) {
try {
- const result = await resolveAsync(packageName, {
+ const options = {
basedir: workspaceFolder.uri.fsPath,
- });
+ };
- if (result) {
- return result;
+ const [manifestPath, binaryPath] = await Promise.all([
+ resolveAsync(manifestName, options),
+ resolveAsync(binaryName, options),
+ ]);
+
+ if (!(manifestPath && binaryPath)) {
+ continue;
+ }
+
+ // Load the package.json manifest of the resolved package
+ const manifestUri = Uri.file(manifestPath);
+ const manifestData = await workspace.fs.readFile(manifestUri);
+
+ const { version } = JSON.parse(new TextDecoder().decode(manifestData));
+ if (typeof version !== "string") {
+ continue;
+ }
+
+ // Ignore versions lower than "0.9.0" as they did not embed the language server
+ if (version.startsWith("0.")) {
+ const [minor] = version.substring(2).split(".");
+ const minorVal = parseInt(minor);
+ if (minorVal < 9) {
+ outputChannel.appendLine(
+ `Ignoring incompatible Rome version "${version}"`,
+ );
+ continue;
+ }
}
+
+ return binaryPath;
} catch {}
}
@@ -219,9 +265,15 @@ async function getWorkspaceDependency(): Promise<string | undefined> {
}
// Returns the path of the binary distribution of Rome included in the bundle of the extension
-async function getBundledBinary(context: ExtensionContext) {
+async function getBundledBinary(
+ context: ExtensionContext,
+ outputChannel: OutputChannel,
+) {
const triplet = PLATFORMS[process.platform]?.[process.arch]?.triplet;
if (!triplet) {
+ outputChannel.appendLine(
+ `Unsupported platform ${process.platform} ${process.arch}`,
+ );
return undefined;
}
@@ -230,8 +282,14 @@ async function getBundledBinary(context: ExtensionContext) {
const bundlePath = Uri.joinPath(context.extensionUri, "server", binaryName);
const bundleExists = await fileExists(bundlePath);
+ if (!bundleExists) {
+ outputChannel.appendLine(
+ "Extension bundle does not include the prebuilt binary",
+ );
+ return undefined;
+ }
- return bundleExists ? bundlePath.fsPath : undefined;
+ return bundlePath.fsPath;
}
async function fileExists(path: Uri) {
@@ -239,7 +297,7 @@ async function fileExists(path: Uri) {
await workspace.fs.stat(path);
return true;
} catch (err) {
- if (err.code === "ENOENT") {
+ if (err.code === "ENOENT" || err.code === "FileNotFound") {
return false;
} else {
throw err;

fix(vscode): ignore incompatible versions of the binary and improve the restart logic (#4022)