aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/installation
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/installation')
-rw-r--r--editors/code/src/installation/server.ts168
1 files changed, 72 insertions, 96 deletions
diff --git a/editors/code/src/installation/server.ts b/editors/code/src/installation/server.ts
index 406e2299c..80cb719e3 100644
--- a/editors/code/src/installation/server.ts
+++ b/editors/code/src/installation/server.ts
@@ -1,63 +1,15 @@
1import * as vscode from "vscode"; 1import * as vscode from "vscode";
2import * as path from "path"; 2import * as path from "path";
3import { strict as assert } from "assert"; 3import { strict as assert } from "assert";
4import { promises as fs } from "fs";
5import { promises as dns } from "dns"; 4import { promises as dns } from "dns";
6import { spawnSync } from "child_process"; 5import { spawnSync } from "child_process";
7import { throttle } from "throttle-debounce";
8 6
9import { BinarySource } from "./interfaces"; 7import { BinarySource } from "./interfaces";
10import { fetchLatestArtifactReleaseInfo } from "./fetch_latest_artifact_release_info"; 8import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info";
11import { downloadFile } from "./download_file"; 9import { downloadArtifact } from "./download_artifact";
12
13export async function downloadLatestServer(
14 {file: artifactFileName, dir: installationDir, repo}: BinarySource.GithubRelease
15) {
16 const { releaseName, downloadUrl } = (await fetchLatestArtifactReleaseInfo(
17 repo, artifactFileName
18 ))!;
19
20 await fs.mkdir(installationDir).catch(err => assert.strictEqual(
21 err?.code,
22 "EEXIST",
23 `Couldn't create directory "${installationDir}" to download `+
24 `language server binary: ${err.message}`
25 ));
26
27 const installationPath = path.join(installationDir, artifactFileName);
28
29 console.time("Downloading ra_lsp_server");
30 await vscode.window.withProgress(
31 {
32 location: vscode.ProgressLocation.Notification,
33 cancellable: false, // FIXME: add support for canceling download?
34 title: `Downloading language server (${releaseName})`
35 },
36 async (progress, _cancellationToken) => {
37 let lastPrecentage = 0;
38 const filePermissions = 0o755; // (rwx, r_x, r_x)
39 await downloadFile(downloadUrl, installationPath, filePermissions, throttle(
40 200,
41 /* noTrailing: */ true,
42 (readBytes, totalBytes) => {
43 const newPercentage = (readBytes / totalBytes) * 100;
44 progress.report({
45 message: newPercentage.toFixed(0) + "%",
46 increment: newPercentage - lastPrecentage
47 });
48
49 lastPrecentage = newPercentage;
50 })
51 );
52 }
53 );
54 console.timeEnd("Downloading ra_lsp_server");
55}
56export async function ensureServerBinary(
57 serverSource: null | BinarySource
58): Promise<null | string> {
59 10
60 if (!serverSource) { 11export async function ensureServerBinary(source: null | BinarySource): Promise<null | string> {
12 if (!source) {
61 vscode.window.showErrorMessage( 13 vscode.window.showErrorMessage(
62 "Unfortunately we don't ship binaries for your platform yet. " + 14 "Unfortunately we don't ship binaries for your platform yet. " +
63 "You need to manually clone rust-analyzer repository and " + 15 "You need to manually clone rust-analyzer repository and " +
@@ -69,80 +21,104 @@ export async function ensureServerBinary(
69 return null; 21 return null;
70 } 22 }
71 23
72 switch (serverSource.type) { 24 switch (source.type) {
73 case BinarySource.Type.ExplicitPath: { 25 case BinarySource.Type.ExplicitPath: {
74 if (isBinaryAvailable(serverSource.path)) { 26 if (isBinaryAvailable(source.path)) {
75 return serverSource.path; 27 return source.path;
76 } 28 }
77 29
78 vscode.window.showErrorMessage( 30 vscode.window.showErrorMessage(
79 `Unable to run ${serverSource.path} binary. ` + 31 `Unable to run ${source.path} binary. ` +
80 `To use the pre-built language server, set "rust-analyzer.raLspServerPath" ` + 32 `To use the pre-built language server, set "rust-analyzer.raLspServerPath" ` +
81 "value to `null` or remove it from the settings to use it by default." 33 "value to `null` or remove it from the settings to use it by default."
82 ); 34 );
83 return null; 35 return null;
84 } 36 }
85 case BinarySource.Type.GithubRelease: { 37 case BinarySource.Type.GithubRelease: {
86 const prebuiltBinaryPath = path.join(serverSource.dir, serverSource.file); 38 const prebuiltBinaryPath = path.join(source.dir, source.file);
39
40 const installedVersion: null | string = getServerVersion(source.storage);
41 const requiredVersion: string = source.version;
87 42
88 if (isBinaryAvailable(prebuiltBinaryPath)) { 43 console.log("Installed version:", installedVersion, "required:", requiredVersion);
44
45 if (isBinaryAvailable(prebuiltBinaryPath) && installedVersion == requiredVersion) {
46 // FIXME: check for new releases and notify the user to update if possible
89 return prebuiltBinaryPath; 47 return prebuiltBinaryPath;
90 } 48 }
91 49
92 const userResponse = await vscode.window.showInformationMessage( 50 const userResponse = await vscode.window.showInformationMessage(
93 "Language server binary for rust-analyzer was not found. " + 51 `Language server version ${source.version} for rust-analyzer is not installed. ` +
94 "Do you want to download it now?", 52 "Do you want to download it now?",
95 "Download now", "Cancel" 53 "Download now", "Cancel"
96 ); 54 );
97 if (userResponse !== "Download now") return null; 55 if (userResponse !== "Download now") return null;
98 56
99 try { 57 if (!await downloadServer(source)) return null;
100 await downloadLatestServer(serverSource);
101 } catch (err) {
102 vscode.window.showErrorMessage(
103 `Failed to download language server from ${serverSource.repo.name} ` +
104 `GitHub repository: ${err.message}`
105 );
106 58
107 console.error(err); 59 return prebuiltBinaryPath;
60 }
61 }
62}
108 63
109 dns.resolve('example.com').then( 64async function downloadServer(source: BinarySource.GithubRelease): Promise<boolean> {
110 addrs => console.log("DNS resolution for example.com was successful", addrs), 65 try {
111 err => { 66 const releaseInfo = (await fetchArtifactReleaseInfo(source.repo, source.file, source.version))!;
112 console.error( 67
113 "DNS resolution for example.com failed, " + 68 await downloadArtifact(releaseInfo, source.file, source.dir, "language server");
114 "there might be an issue with Internet availability" 69 await setServerVersion(source.storage, releaseInfo.releaseName);
115 ); 70 } catch (err) {
116 console.error(err); 71 vscode.window.showErrorMessage(
117 } 72 `Failed to download language server from ${source.repo.name} ` +
118 ); 73 `GitHub repository: ${err.message}`
74 );
75
76 console.error(err);
119 77
120 return null; 78 dns.resolve('example.com').then(
79 addrs => console.log("DNS resolution for example.com was successful", addrs),
80 err => {
81 console.error(
82 "DNS resolution for example.com failed, " +
83 "there might be an issue with Internet availability"
84 );
85 console.error(err);
121 } 86 }
87 );
88 return false;
89 }
122 90
123 if (!isBinaryAvailable(prebuiltBinaryPath)) assert(false, 91 if (!isBinaryAvailable(path.join(source.dir, source.file))) assert(false,
124 `Downloaded language server binary is not functional.` + 92 `Downloaded language server binary is not functional.` +
125 `Downloaded from: ${JSON.stringify(serverSource)}` 93 `Downloaded from: ${JSON.stringify(source, null, 4)}`
126 ); 94 );
127 95
96 vscode.window.showInformationMessage(
97 "Rust analyzer language server was successfully installed 🦀"
98 );
128 99
129 vscode.window.showInformationMessage( 100 return true;
130 "Rust analyzer language server was successfully installed 🦀" 101}
131 );
132 102
133 return prebuiltBinaryPath; 103function isBinaryAvailable(binaryPath: string): boolean {
134 } 104 const res = spawnSync(binaryPath, ["--version"]);
135 }
136 105
137 function isBinaryAvailable(binaryPath: string) { 106 // ACHTUNG! `res` type declaration is inherently wrong, see
138 const res = spawnSync(binaryPath, ["--version"]); 107 // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42221
139 108
140 // ACHTUNG! `res` type declaration is inherently wrong, see 109 console.log("Checked binary availablity via --version", res);
141 // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42221 110 console.log(binaryPath, "--version output:", res.output?.map(String));
142 111
143 console.log("Checked binary availablity via --version", res); 112 return res.status === 0;
144 console.log(binaryPath, "--version output:", res.output?.map(String)); 113}
145 114
146 return res.status === 0; 115function getServerVersion(storage: vscode.Memento): null | string {
147 } 116 const version = storage.get<null | string>("server-version", null);
117 console.log("Get server-version:", version);
118 return version;
119}
120
121async function setServerVersion(storage: vscode.Memento, version: string): Promise<void> {
122 console.log("Set server-version:", version);
123 await storage.update("server-version", version.toString());
148} 124}