Skip to content
Merged
6 changes: 2 additions & 4 deletions lsp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,10 @@ Language Server for [SimplicityHL language](https://simplicity-lang.org/).

## Installation

Clone this repository and install using Cargo:
Install Language Server using `cargo`:

```bash
https://github.com/distributed-lab/simplicityhl-lsp
cd simplicityhl-lsp
cargo install --path .
cargo install simplicityhl-lsp
```

## Integration with editors
Expand Down
4 changes: 2 additions & 2 deletions vscode/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "simplicityhl",
"displayName": "SimplicityHL Language Support",
"description": "Syntax highlighting and autocompletion for SimplicityHL (Simfony) language",
"version": "0.2.1",
"version": "0.2.2",
"publisher": "Blockstream",
"repository": {
"type": "git",
Expand Down Expand Up @@ -67,6 +67,11 @@
"type": "boolean",
"default": false,
"description": "Do not show missing LSP executable warning."
},
"simplicityhl.disableAutoupdate": {
"type": "boolean",
"default": false,
"description": "Do not autoupdate LSP server."
}
}
},
Expand Down
11 changes: 6 additions & 5 deletions vscode/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ export class LspClient {
}
}

public stop(): Thenable<void> | undefined {
public async stop(): Promise<void> {
if (!this.client) {
return undefined;
return;
}
return this.client.stop();
await this.client.stop();
this.client = undefined;
}

public async restart(): Promise<void> {
Expand All @@ -67,8 +68,8 @@ export class LspClient {
}

try {
await this.client.stop();
await this.client.start();
await this.stop();
await this.start();
window.showInformationMessage("SimplicityHL Language Server restarted successfully!");
} catch (e) {
window.showErrorMessage(`Failed to restart LSP: ${e}`);
Expand Down
102 changes: 89 additions & 13 deletions vscode/src/find_server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as cp from "child_process";
import * as os from "os";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import { env, Uri, window, workspace } from "vscode";

import process from "node:process";
import * as cp from "child_process";

import { env, ProgressLocation, Uri, window, workspace } from "vscode";

function findExecutable(command: string): string | null {
try {
Expand Down Expand Up @@ -51,27 +53,81 @@ function findExecutable(command: string): string | null {
return null;
}

async function installServer(command: string) {
const cargoPath = findExecutable("cargo");
if (!cargoPath) {
throw new Error("Unable to find 'cargo'. Please ensure Rust is installed and in your PATH.");
}

const action = findExecutable(command) ? "Updating" : "Installing";

return window.withProgress({
location: ProgressLocation.Notification,
title: `${action} ${command}`,
cancellable: true
}, async (progress, token) => {
return new Promise<void>((resolve, reject) => {
const installProcess = cp.spawn(cargoPath!, ["install", "--color", "never", command]);

token.onCancellationRequested(() => {
installProcess.kill("SIGTERM");
reject(new Error("Installation canceled"));
});

const reportProgress = (data: Buffer) => {
const lines = data.toString()
.split(/\r?\n/)
.map(l => l.trim())

for (const line of lines) {
if (line.startsWith("Compiling") && line !== "Compiling") {
progress.report({ message: line });
}
}
};

installProcess.stderr?.on('data', reportProgress);

installProcess.on('close', (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Installation failed with exit code ${code}`));
}
});

installProcess.on('error', (err) => {
reject(new Error(`Failed to start cargo process: ${err.message}`));
});
});
});
}

export async function ensureExecutable(
command: string,
): Promise<string | null> {
const exePath = findExecutable(command);
const cargoPath = findExecutable("cargo");
const config = workspace.getConfiguration("simplicityhl");

const suppressWarning = config.get<boolean>(
"suppressMissingLspWarning",
false,
);
let serverPath = findExecutable(command);

if (!cargoPath && !serverPath) {
const suppressWarning = config.get<boolean>(
"suppressMissingLspWarning",
false,
);
if (suppressWarning) {
return null;
}

if (!exePath && !suppressWarning) {
const choice = await window.showWarningMessage(
`LSP server "${command}" was not found in PATH or common locations. To use language server feautures, please install server to PATH`,
`To use SimplicityHL language server, please install cargo`,
"Learn more",
"Don't show again",
"Close",
);

if (choice === "Learn more") {
const url = "https://github.com/distributed-lab/simplicityhl-lsp";
const url = "https://rust-lang.org/tools/install";
await env.openExternal(Uri.parse(url));
} else if (choice === "Don't show again") {
const config = workspace.getConfiguration("simplicityhl");
Expand All @@ -80,5 +136,25 @@ export async function ensureExecutable(

return null;
}
return exePath;

if (!cargoPath) {
return serverPath;
}

const disableAutoupdate = config.get<boolean>("disableAutoupdate", false);

if (serverPath && disableAutoupdate) {
return serverPath;
}

try {
await installServer(command);

serverPath = findExecutable(command);
} catch (err) {
window.showErrorMessage(err);
return null;
}

return serverPath;
}