aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/installation/language_server.ts
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/installation/language_server.ts')
-rw-r--r--editors/code/src/installation/language_server.ts119
1 files changed, 119 insertions, 0 deletions
diff --git a/editors/code/src/installation/language_server.ts b/editors/code/src/installation/language_server.ts
new file mode 100644
index 000000000..2b3ce6621
--- /dev/null
+++ b/editors/code/src/installation/language_server.ts
@@ -0,0 +1,119 @@
1import { unwrapNotNil } from "ts-not-nil";
2import { spawnSync } from "child_process";
3import * as vscode from "vscode";
4import * as path from "path";
5import { strict as assert } from "assert";
6import { promises as fs } from "fs";
7
8import { BinarySource, BinarySourceType, GithubBinarySource } from "./interfaces";
9import { fetchLatestArtifactMetadata } from "./fetch_latest_artifact_metadata";
10import { downloadFile } from "./download_file";
11
12export async function downloadLatestLanguageServer(
13 {file: artifactFileName, dir: installationDir, repo}: GithubBinarySource
14) {
15 const binaryMetadata = await fetchLatestArtifactMetadata({ artifactFileName, repo });
16
17 const {
18 releaseName,
19 downloadUrl
20 } = unwrapNotNil(binaryMetadata, `Latest GitHub release lacks "${artifactFileName}" file`);
21
22 await fs.mkdir(installationDir).catch(err => assert.strictEqual(
23 err && err.code,
24 "EEXIST",
25 `Couldn't create directory "${installationDir}" to download `+
26 `language server binary: ${err.message}`
27 ));
28
29 const installationPath = path.join(installationDir, artifactFileName);
30
31 await vscode.window.withProgress(
32 {
33 location: vscode.ProgressLocation.Notification,
34 cancellable: false, // FIXME: add support for canceling download?
35 title: `Downloading language server ${releaseName}`
36 },
37 async (progress, _) => {
38 let lastPrecentage = 0;
39 await downloadFile(downloadUrl, installationPath, (readBytes, totalBytes) => {
40 const newPercentage = (readBytes / totalBytes) * 100;
41 progress.report({
42 message: newPercentage.toFixed(0) + "%",
43 increment: newPercentage - lastPrecentage
44 });
45
46 lastPrecentage = newPercentage;
47 });
48 }
49 );
50
51 await fs.chmod(installationPath, 111); // Set xxx permissions
52}
53export async function ensureLanguageServerBinary(
54 langServerSource: null | BinarySource
55): Promise<null | string> {
56
57 if (!langServerSource) {
58 vscode.window.showErrorMessage(
59 "Unfortunately we don't ship binaries for your platform yet. " +
60 "You need to manually clone rust-analyzer repository and " +
61 "run `cargo xtask install --server` to build the language server from sources. " +
62 "If you feel that your platform should be supported, please create an issue " +
63 "about that [here](https://github.com/rust-analyzer/rust-analyzer/issues) and we " +
64 "will consider it."
65 );
66 return null;
67 }
68
69 switch (langServerSource.type) {
70 case BinarySourceType.ExplicitPath: {
71 if (isBinaryAvailable(langServerSource.path)) {
72 return langServerSource.path;
73 }
74 vscode.window.showErrorMessage(
75 `Unable to execute ${'`'}${langServerSource.path} --version${'`'}. ` +
76 "To use the bundled language server, set `rust-analyzer.raLspServerPath` " +
77 "value to `null` or remove it from the settings to use it by default."
78 );
79 return null;
80 }
81 case BinarySourceType.GithubBinary: {
82 const bundledBinaryPath = path.join(langServerSource.dir, langServerSource.file);
83
84 if (!isBinaryAvailable(bundledBinaryPath)) {
85 const userResponse = await vscode.window.showInformationMessage(
86 `Language server binary for rust-analyzer was not found. ` +
87 `Do you want to download it now?`,
88 "Download now", "Cancel"
89 );
90 if (userResponse !== "Download now") return null;
91
92 try {
93 await downloadLatestLanguageServer(langServerSource);
94 } catch (err) {
95 await vscode.window.showErrorMessage(
96 `Failed to download language server from ${langServerSource.repo.name} ` +
97 `GitHub repository: ${err.message}`
98 );
99 return null;
100 }
101
102
103 assert(
104 isBinaryAvailable(bundledBinaryPath),
105 "Downloaded language server binary is not functional"
106 );
107
108 vscode.window.showInformationMessage(
109 "Rust analyzer language server was successfully installed"
110 );
111 }
112 return bundledBinaryPath;
113 }
114 }
115
116 function isBinaryAvailable(binaryPath: string) {
117 return spawnSync(binaryPath, ["--version"]).status === 0;
118 }
119}