diff options
Diffstat (limited to 'editors/code/src/installation')
-rw-r--r-- | editors/code/src/installation/server.ts | 168 |
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 @@ | |||
1 | import * as vscode from "vscode"; | 1 | import * as vscode from "vscode"; |
2 | import * as path from "path"; | 2 | import * as path from "path"; |
3 | import { strict as assert } from "assert"; | 3 | import { strict as assert } from "assert"; |
4 | import { promises as fs } from "fs"; | ||
5 | import { promises as dns } from "dns"; | 4 | import { promises as dns } from "dns"; |
6 | import { spawnSync } from "child_process"; | 5 | import { spawnSync } from "child_process"; |
7 | import { throttle } from "throttle-debounce"; | ||
8 | 6 | ||
9 | import { BinarySource } from "./interfaces"; | 7 | import { BinarySource } from "./interfaces"; |
10 | import { fetchLatestArtifactReleaseInfo } from "./fetch_latest_artifact_release_info"; | 8 | import { fetchArtifactReleaseInfo } from "./fetch_artifact_release_info"; |
11 | import { downloadFile } from "./download_file"; | 9 | import { downloadArtifact } from "./download_artifact"; |
12 | |||
13 | export 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 | } | ||
56 | export async function ensureServerBinary( | ||
57 | serverSource: null | BinarySource | ||
58 | ): Promise<null | string> { | ||
59 | 10 | ||
60 | if (!serverSource) { | 11 | export 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( | 64 | async 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; | 103 | function 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; | 115 | function 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 | |||
121 | async 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 | } |