Unnamed repository; edit this file 'description' to name the repository.
Merge #11106
11106: internal: Remove network access from Code extension r=lnicola a=lnicola Co-authored-by: LaurenČ›iu Nicola <[email protected]>
bors[bot] 2021-12-23
parent 4ea1f58 · parent af2bbce · commit cb0e270
-rw-r--r--PRIVACY.md2
-rw-r--r--docs/user/manual.adoc4
-rw-r--r--editors/code/package-lock.json115
-rw-r--r--editors/code/package.json20
-rw-r--r--editors/code/src/config.ts22
-rw-r--r--editors/code/src/main.ts292
-rw-r--r--editors/code/src/net.ts228
-rw-r--r--editors/code/src/persistent_state.ts38
8 files changed, 54 insertions, 667 deletions
diff --git a/PRIVACY.md b/PRIVACY.md
index 718fbed120..89e252be73 100644
--- a/PRIVACY.md
+++ b/PRIVACY.md
@@ -1 +1 @@
-See the [Privacy](https://rust-analyzer.github.io/manual.html#security) section of the user manual.
+See the [Privacy](https://rust-analyzer.github.io/manual.html#privacy) section of the user manual.
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index 1335f8df7c..13165e8d3f 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -653,10 +653,12 @@ Here is a **non-exhaustive** list of ways to make rust-analyzer execute arbitrar
The LSP server performs no network access in itself, but runs `cargo metadata` which will update or download the crate registry and the source code of the project dependencies.
If enabled (the default), build scripts and procedural macros can do anything.
-The Code extension automatically connects to GitHub to download updated LSP binaries and, if the nightly channel is selected, to perform update checks using the GitHub API. For `rust-analyzer` developers, using `cargo xtask release` uses the same API to put together the release notes.
+The Code extension does not access the network.
Any other editor plugins are not under the control of the `rust-analyzer` developers. For any privacy concerns, you should check with their respective developers.
+For `rust-analyzer` developers, `cargo xtask release` uses the GitHub API to put together the release notes.
+
== Features
include::./generated_features.adoc[]
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 8728654662..4aa9ac9d43 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -11,8 +11,6 @@
"dependencies": {
"d3": "^7.2.0",
"d3-graphviz": "^4.0.0",
- "https-proxy-agent": "^5.0.0",
- "node-fetch": "^3.0.3",
"vscode-languageclient": "8.0.0-next.2"
},
"devDependencies": {
@@ -345,6 +343,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
"dependencies": {
"debug": "4"
},
@@ -1274,18 +1273,11 @@
"node": ">=12"
}
},
- "node_modules/data-uri-to-buffer": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
- "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==",
- "engines": {
- "node": ">= 12"
- }
- },
"node_modules/debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+ "dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -2059,27 +2051,6 @@
"pend": "~1.2.0"
}
},
- "node_modules/fetch-blob": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.3.tgz",
- "integrity": "sha512-ax1Y5I9w+9+JiM+wdHkhBoxew+zG4AJ2SvAD1v1szpddUIiPERVGBxrMcB2ZqW0Y3PP8bOWYv2zqQq1Jp2kqUQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/jimmywarting"
- },
- {
- "type": "paypal",
- "url": "https://paypal.me/jimmywarting"
- }
- ],
- "dependencies": {
- "web-streams-polyfill": "^3.0.3"
- },
- "engines": {
- "node": "^12.20 || >= 14.13"
- }
- },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -2123,17 +2094,6 @@
"integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==",
"dev": true
},
- "node_modules/formdata-polyfill": {
- "version": "4.0.10",
- "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
- "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
- "dependencies": {
- "fetch-blob": "^3.1.2"
- },
- "engines": {
- "node": ">=12.20.0"
- }
- },
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -2403,6 +2363,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+ "dev": true,
"dependencies": {
"agent-base": "6",
"debug": "4"
@@ -2770,7 +2731,8 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
},
"node_modules/mute-stream": {
"version": "0.0.8",
@@ -2814,23 +2776,6 @@
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
"dev": true
},
- "node_modules/node-fetch": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.1.0.tgz",
- "integrity": "sha512-QU0WbIfMUjd5+MUzQOYhenAazakV7Irh1SGkWCsRzBwvm4fAhzEUaHMJ6QLP7gWT6WO9/oH2zhKMMGMuIrDyKw==",
- "dependencies": {
- "data-uri-to-buffer": "^4.0.0",
- "fetch-blob": "^3.1.2",
- "formdata-polyfill": "^4.0.10"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/node-fetch"
- }
- },
"node_modules/npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
@@ -3866,14 +3811,6 @@
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.0-next.3.tgz",
"integrity": "sha512-VQcXnhKYxUW6OiRMhG++SzmZYMJwusXknJGd+FfdOnS1yHAo734OHyR0e2eEHDlv0/oWc8RZPgx/VKSKyondVg=="
},
- "node_modules/web-streams-polyfill": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
- "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==",
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -4170,6 +4107,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
"requires": {
"debug": "4"
}
@@ -4874,15 +4812,11 @@
"d3-transition": "2 - 3"
}
},
- "data-uri-to-buffer": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz",
- "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA=="
- },
"debug": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
+ "dev": true,
"requires": {
"ms": "2.1.2"
}
@@ -5434,14 +5368,6 @@
"pend": "~1.2.0"
}
},
- "fetch-blob": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.3.tgz",
- "integrity": "sha512-ax1Y5I9w+9+JiM+wdHkhBoxew+zG4AJ2SvAD1v1szpddUIiPERVGBxrMcB2ZqW0Y3PP8bOWYv2zqQq1Jp2kqUQ==",
- "requires": {
- "web-streams-polyfill": "^3.0.3"
- }
- },
"file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -5476,14 +5402,6 @@
"integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw==",
"dev": true
},
- "formdata-polyfill": {
- "version": "4.0.10",
- "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
- "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
- "requires": {
- "fetch-blob": "^3.1.2"
- }
- },
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -5696,6 +5614,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz",
"integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==",
+ "dev": true,
"requires": {
"agent-base": "6",
"debug": "4"
@@ -5978,7 +5897,8 @@
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
},
"mute-stream": {
"version": "0.0.8",
@@ -6021,16 +5941,6 @@
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
"dev": true
},
- "node-fetch": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.1.0.tgz",
- "integrity": "sha512-QU0WbIfMUjd5+MUzQOYhenAazakV7Irh1SGkWCsRzBwvm4fAhzEUaHMJ6QLP7gWT6WO9/oH2zhKMMGMuIrDyKw==",
- "requires": {
- "data-uri-to-buffer": "^4.0.0",
- "fetch-blob": "^3.1.2",
- "formdata-polyfill": "^4.0.10"
- }
- },
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
@@ -6827,11 +6737,6 @@
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.0-next.3.tgz",
"integrity": "sha512-VQcXnhKYxUW6OiRMhG++SzmZYMJwusXknJGd+FfdOnS1yHAo734OHyR0e2eEHDlv0/oWc8RZPgx/VKSKyondVg=="
},
- "web-streams-polyfill": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
- "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA=="
- },
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index 9ab57d7dc5..550b28e1cd 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -36,8 +36,6 @@
"test": "node ./out/tests/runTests.js"
},
"dependencies": {
- "https-proxy-agent": "^5.0.0",
- "node-fetch": "^3.0.3",
"vscode-languageclient": "8.0.0-next.2",
"d3": "^7.2.0",
"d3-graphviz": "^4.0.0"
@@ -305,24 +303,6 @@
"default": true,
"description": "Whether inlay hints font size should be smaller than editor's font size."
},
- "rust-analyzer.updates.channel": {
- "type": "string",
- "enum": [
- "stable",
- "nightly"
- ],
- "default": "stable",
- "markdownEnumDescriptions": [
- "`stable` updates are shipped weekly, they don't contain cutting-edge features from VSCode proposed APIs but have less bugs in general.",
- "`nightly` updates are shipped daily (extension updates automatically by downloading artifacts directly from GitHub), they contain cutting-edge features and latest bug fixes. These releases help us get your feedback very quickly and speed up rust-analyzer development **drastically**."
- ],
- "markdownDescription": "Choose `nightly` updates to get the latest features and bug fixes every day. While `stable` releases occur weekly and don't contain cutting-edge features from VSCode proposed APIs."
- },
- "rust-analyzer.updates.askBeforeDownload": {
- "type": "boolean",
- "default": false,
- "description": "Whether to ask for permission before downloading any files from the Internet."
- },
"rust-analyzer.server.path": {
"type": [
"null",
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index cb0868db59..cdbaa67fe2 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -8,11 +8,6 @@ const NIGHTLY_TAG = "nightly";
export type RunnableEnvCfg = undefined | Record<string, string> | { mask?: string; env: Record<string, string> }[];
-export class ProxySettings {
- proxy?: string = undefined;
- strictSSL: boolean = true;
-}
-
export class Config {
readonly extensionId = "matklad.rust-analyzer";
@@ -24,7 +19,6 @@ export class Config {
"procMacro",
"files",
"highlighting",
- "updates.channel",
"lens", // works as lens.*
]
.map(opt => `${this.rootSection}.${opt}`);
@@ -36,11 +30,9 @@ export class Config {
} = vscode.extensions.getExtension(this.extensionId)!.packageJSON;
readonly globalStorageUri: vscode.Uri;
- readonly installUri: vscode.Uri;
constructor(ctx: vscode.ExtensionContext) {
this.globalStorageUri = ctx.globalStorageUri;
- this.installUri = ctx.extensionUri;
vscode.workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, ctx.subscriptions);
this.refreshLogging();
}
@@ -103,21 +95,7 @@ export class Config {
return this.get<null | string>("server.path") ?? this.get<null | string>("serverPath");
}
get serverExtraEnv() { return this.get<Env | null>("server.extraEnv") ?? {}; }
- get channel() { return this.get<UpdatesChannel>("updates.channel"); }
- get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); }
get traceExtension() { return this.get<boolean>("trace.extension"); }
- get proxySettings(): ProxySettings {
- const proxy = vscode
- .workspace
- .getConfiguration('http')
- .get<null | string>("proxy")! || process.env["https_proxy"] || process.env["HTTPS_PROXY"];
- const strictSSL = vscode.workspace.getConfiguration("http").get<boolean>("proxyStrictSSL") ?? true;
-
- return {
- proxy: proxy,
- strictSSL: strictSSL,
- };
- }
get inlayHints() {
return {
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index dc0a9a2f87..df7580b9bc 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -5,12 +5,11 @@ import * as commands from './commands';
import { activateInlayHints } from './inlay_hints';
import { Ctx } from './ctx';
import { Config } from './config';
-import { log, assert, isValidExecutable, isRustDocument } from './util';
+import { log, isValidExecutable, isRustDocument } from './util';
import { PersistentState } from './persistent_state';
-import { fetchRelease, download } from './net';
import { activateTaskProvider } from './tasks';
import { setContextValue } from './util';
-import { exec, spawnSync } from 'child_process';
+import { exec } from 'child_process';
let ctx: Ctx | undefined;
@@ -28,14 +27,9 @@ export async function activate(context: vscode.ExtensionContext) {
async function tryActivate(context: vscode.ExtensionContext) {
const config = new Config(context);
const state = new PersistentState(context.globalState);
- const serverPath = await bootstrap(config, state).catch(err => {
+ const serverPath = await bootstrap(context, config, state).catch(err => {
let message = "bootstrap error. ";
- if (err.code === "EBUSY" || err.code === "ETXTBSY" || err.code === "EPERM") {
- message += "Other vscode windows might be using rust-analyzer, ";
- message += "you should close them and reload this window to retry. ";
- }
-
message += 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). ';
message += 'To enable verbose logs use { "rust-analyzer.trace.extension": true }';
@@ -111,10 +105,6 @@ async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
await activate(context).catch(log.error);
});
- ctx.registerCommand('updateGithubToken', ctx => async () => {
- await queryForGithubToken(new PersistentState(ctx.globalState));
- });
-
ctx.registerCommand('analyzerStatus', commands.analyzerStatus);
ctx.registerCommand('memoryUsage', commands.memoryUsage);
ctx.registerCommand('shuffleCrateGraph', commands.shuffleCrateGraph);
@@ -161,99 +151,8 @@ export async function deactivate() {
ctx = undefined;
}
-async function bootstrap(config: Config, state: PersistentState): Promise<string> {
- await vscode.workspace.fs.createDirectory(config.globalStorageUri).then();
-
- if (!config.currentExtensionIsNightly) {
- await state.updateNightlyReleaseId(undefined);
- }
- await bootstrapExtension(config, state);
- const path = await bootstrapServer(config, state);
- return path;
-}
-
-async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> {
- if (config.package.releaseTag === null) return;
- if (config.channel === "stable") {
- if (config.currentExtensionIsNightly) {
- void vscode.window.showWarningMessage(
- `You are running a nightly version of rust-analyzer extension. ` +
- `To switch to stable, uninstall the extension and re-install it from the marketplace`
- );
- }
- return;
- };
- if (serverPath(config)) return;
-
- const now = Date.now();
- const isInitialNightlyDownload = state.nightlyReleaseId === undefined;
- if (config.currentExtensionIsNightly) {
- // Check if we should poll github api for the new nightly version
- // if we haven't done it during the past hour
- const lastCheck = state.lastCheck;
-
- const anHour = 60 * 60 * 1000;
- const shouldCheckForNewNightly = isInitialNightlyDownload || (now - (lastCheck ?? 0)) > anHour;
-
- if (!shouldCheckForNewNightly) return;
- }
-
- const latestNightlyRelease = await downloadWithRetryDialog(state, async () => {
- return await fetchRelease("nightly", state.githubToken, config.proxySettings);
- }).catch(async (e) => {
- log.error(e);
- if (isInitialNightlyDownload) {
- await vscode.window.showErrorMessage(`Failed to download rust-analyzer nightly: ${e}`);
- }
- return;
- });
- if (latestNightlyRelease === undefined) {
- if (isInitialNightlyDownload) {
- await vscode.window.showErrorMessage("Failed to download rust-analyzer nightly: empty release contents returned");
- }
- return;
- }
- if (config.currentExtensionIsNightly && latestNightlyRelease.id === state.nightlyReleaseId) return;
-
- const userResponse = await vscode.window.showInformationMessage(
- "New version of rust-analyzer (nightly) is available (requires reload).",
- "Update"
- );
- if (userResponse !== "Update") return;
-
- let arch = process.arch;
- if (arch === "ia32") {
- arch = "x64";
- }
- let platform = process.platform as string;
- if (platform === "linux" && isMusl()) {
- platform = "alpine";
- }
- const artifactName = `rust-analyzer-${platform}-${arch}.vsix`;
-
- const artifact = latestNightlyRelease.assets.find(artifact => artifact.name === artifactName);
- assert(!!artifact, `Bad release: ${JSON.stringify(latestNightlyRelease)}`);
- const dest = vscode.Uri.joinPath(config.globalStorageUri, "rust-analyzer.vsix");
-
- await downloadWithRetryDialog(state, async () => {
- await download({
- url: artifact.browser_download_url,
- dest,
- progressTitle: "Downloading rust-analyzer extension",
- proxySettings: config.proxySettings,
- });
- });
-
- await vscode.commands.executeCommand("workbench.extensions.installExtension", dest);
- await vscode.workspace.fs.delete(dest);
-
- await state.updateNightlyReleaseId(latestNightlyRelease.id);
- await state.updateLastCheck(now);
- await vscode.commands.executeCommand("workbench.action.reloadWindow");
-}
-
-async function bootstrapServer(config: Config, state: PersistentState): Promise<string> {
- const path = await getServer(config, state);
+async function bootstrap(context: vscode.ExtensionContext, config: Config, state: PersistentState): Promise<string> {
+ const path = await getServer(context, config, state);
if (!path) {
throw new Error(
"Rust Analyzer Language Server is not available. " +
@@ -318,7 +217,7 @@ async function patchelf(dest: vscode.Uri): Promise<void> {
);
}
-async function getServer(config: Config, state: PersistentState): Promise<string | undefined> {
+async function getServer(context: vscode.ExtensionContext, config: Config, state: PersistentState): Promise<string | undefined> {
const explicitPath = serverPath(config);
if (explicitPath) {
if (explicitPath.startsWith("~/")) {
@@ -328,85 +227,39 @@ async function getServer(config: Config, state: PersistentState): Promise<string
};
if (config.package.releaseTag === null) return "rust-analyzer";
- const platforms: { [key: string]: string } = {
- "ia32 win32": "x86_64-pc-windows-msvc",
- "x64 win32": "x86_64-pc-windows-msvc",
- "x64 linux": "x86_64-unknown-linux-gnu",
- "x64 darwin": "x86_64-apple-darwin",
- "arm64 win32": "aarch64-pc-windows-msvc",
- "arm64 linux": "aarch64-unknown-linux-gnu",
- "arm64 darwin": "aarch64-apple-darwin",
- };
- let platform = platforms[`${process.arch} ${process.platform}`];
- if (platform === undefined) {
- await vscode.window.showErrorMessage(
- "Unfortunately we don't ship binaries for your platform yet. " +
- "You need to manually clone rust-analyzer repository and " +
- "run `cargo xtask install --server` to build the language server from sources. " +
- "If you feel that your platform should be supported, please create an issue " +
- "about that [here](https://github.com/rust-analyzer/rust-analyzer/issues) and we " +
- "will consider it."
- );
- return undefined;
- }
- if (platform === "x86_64-unknown-linux-gnu" && isMusl()) {
- platform = "x86_64-unknown-linux-musl";
- }
- const ext = platform.indexOf("-windows-") !== -1 ? ".exe" : "";
- const dest = vscode.Uri.joinPath(config.globalStorageUri, `rust-analyzer-${platform}${ext}`);
- const bundled = vscode.Uri.joinPath(config.installUri, "server", `rust-analyzer${ext}`);
+ const ext = process.platform === "win32" ? ".exe" : "";
+ const bundled = vscode.Uri.joinPath(context.extensionUri, "server", `rust-analyzer${ext}`);
const bundledExists = await vscode.workspace.fs.stat(bundled).then(() => true, () => false);
- let exists = await vscode.workspace.fs.stat(dest).then(() => true, () => false);
if (bundledExists) {
- await state.updateServerVersion(config.package.version);
- if (!await isNixOs()) {
- return bundled.fsPath;
- }
- if (!exists) {
- await vscode.workspace.fs.copy(bundled, dest);
- await patchelf(dest);
- exists = true;
+ let server = bundled;
+ if (await isNixOs()) {
+ await vscode.workspace.fs.createDirectory(config.globalStorageUri).then();
+ const dest = vscode.Uri.joinPath(config.globalStorageUri, `rust-analyzer${ext}`);
+ let exists = await vscode.workspace.fs.stat(dest).then(() => true, () => false);
+ if (exists && config.package.version !== state.serverVersion) {
+ await vscode.workspace.fs.delete(dest);
+ exists = false;
+ }
+ if (!exists) {
+ await vscode.workspace.fs.copy(bundled, dest);
+ await patchelf(dest);
+ server = dest;
+ }
}
- }
- if (!exists) {
- await state.updateServerVersion(undefined);
- }
-
- if (state.serverVersion === config.package.version) return dest.fsPath;
-
- if (config.askBeforeDownload) {
- const userResponse = await vscode.window.showInformationMessage(
- `Language server version ${config.package.version} for rust-analyzer is not installed.`,
- "Download now"
- );
- if (userResponse !== "Download now") return dest.fsPath;
- }
-
- const releaseTag = config.package.releaseTag;
- const release = await downloadWithRetryDialog(state, async () => {
- return await fetchRelease(releaseTag, state.githubToken, config.proxySettings);
- });
- const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`);
- assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
-
- await downloadWithRetryDialog(state, async () => {
- await download({
- url: artifact.browser_download_url,
- dest,
- progressTitle: "Downloading rust-analyzer server",
- gunzip: true,
- mode: 0o755,
- proxySettings: config.proxySettings,
- });
- });
-
- // Patching executable if that's NixOS.
- if (await isNixOs()) {
- await patchelf(dest);
+ await state.updateServerVersion(config.package.version);
+ return server.fsPath;
}
- await state.updateServerVersion(config.package.version);
- return dest.fsPath;
+ await state.updateServerVersion(undefined);
+ await vscode.window.showErrorMessage(
+ "Unfortunately we don't ship binaries for your platform yet. " +
+ "You need to manually clone the rust-analyzer repository and " +
+ "run `cargo xtask install --server` to build the language server from sources. " +
+ "If you feel that your platform should be supported, please create an issue " +
+ "about that [here](https://github.com/rust-analyzer/rust-analyzer/issues) and we " +
+ "will consider it."
+ );
+ return undefined;
}
function serverPath(config: Config): string | null {
@@ -422,85 +275,14 @@ async function isNixOs(): Promise<boolean> {
}
}
-function isMusl(): boolean {
- // We can detect Alpine by checking `/etc/os-release` but not Void Linux musl.
- // Instead, we run `ldd` since it advertises the libc which it belongs to.
- const res = spawnSync("ldd", ["--version"]);
- return res.stderr != null && res.stderr.indexOf("musl libc") >= 0;
-}
-
-async function downloadWithRetryDialog<T>(state: PersistentState, downloadFunc: () => Promise<T>): Promise<T> {
- while (true) {
- try {
- return await downloadFunc();
- } catch (e) {
- const selected = await vscode.window.showErrorMessage("Failed to download: " + e.message, {}, {
- title: "Update Github Auth Token",
- updateToken: true,
- }, {
- title: "Retry download",
- retry: true,
- }, {
- title: "Dismiss",
- });
-
- if (selected?.updateToken) {
- await queryForGithubToken(state);
- continue;
- } else if (selected?.retry) {
- continue;
- }
- throw e;
- };
- }
-}
-
-async function queryForGithubToken(state: PersistentState): Promise<void> {
- const githubTokenOptions: vscode.InputBoxOptions = {
- value: state.githubToken,
- password: true,
- prompt: `
- This dialog allows to store a Github authorization token.
- The usage of an authorization token will increase the rate
- limit on the use of Github APIs and can thereby prevent getting
- throttled.
- Auth tokens can be created at https://github.com/settings/tokens`,
- };
-
- const newToken = await vscode.window.showInputBox(githubTokenOptions);
- if (newToken === undefined) {
- // The user aborted the dialog => Do not update the stored token
- return;
- }
-
- if (newToken === "") {
- log.info("Clearing github token");
- await state.updateGithubToken(undefined);
- } else {
- log.info("Storing new github token");
- await state.updateGithubToken(newToken);
- }
-}
-
function warnAboutExtensionConflicts() {
- const conflicting = [
- ["rust-analyzer", "matklad.rust-analyzer"],
- ["Rust", "rust-lang.rust"],
- ["Rust", "kalitaalexey.vscode-rust"],
- ];
-
- const found = conflicting.filter(
- nameId => vscode.extensions.getExtension(nameId[1]) !== undefined);
-
- if (found.length > 1) {
- const fst = found[0];
- const sec = found[1];
+ if (vscode.extensions.getExtension("rust-lang.rust")) {
vscode.window.showWarningMessage(
- `You have both the ${fst[0]} (${fst[1]}) and ${sec[0]} (${sec[1]}) ` +
+ `You have both the rust-analyzer (matklad.rust-analyzer) and Rust (rust-lang.rust) ` +
"plugins enabled. These are known to conflict and cause various functions of " +
"both plugins to not work correctly. You should disable one of them.", "Got it")
.then(() => { }, console.error);
- };
+ }
}
/**
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts
deleted file mode 100644
index bd1bb5f8a8..0000000000
--- a/editors/code/src/net.ts
+++ /dev/null
@@ -1,228 +0,0 @@
-import fetch from "node-fetch";
-var HttpsProxyAgent = require('https-proxy-agent');
-
-import * as vscode from "vscode";
-import * as stream from "stream";
-import * as crypto from "crypto";
-import * as fs from "fs";
-import * as zlib from "zlib";
-import * as util from "util";
-import * as path from "path";
-import { log, assert } from "./util";
-import * as https from "https";
-import { ProxySettings } from "./config";
-
-const pipeline = util.promisify(stream.pipeline);
-
-const GITHUB_API_ENDPOINT_URL = "https://api.github.com";
-const OWNER = "rust-analyzer";
-const REPO = "rust-analyzer";
-
-function makeHttpAgent(proxy: string | null | undefined, options?: https.AgentOptions) {
- if (proxy) {
- return new HttpsProxyAgent({ ...options, ...new URL(proxy) });
- } else {
- return new https.Agent(options);
- }
-}
-
-export async function fetchRelease(
- releaseTag: string,
- githubToken: string | null | undefined,
- proxySettings: ProxySettings,
-): Promise<GithubRelease> {
-
- const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`;
-
- const requestUrl = GITHUB_API_ENDPOINT_URL + apiEndpointPath;
-
- log.debug("Issuing request for released artifacts metadata to", requestUrl);
-
- const headers: Record<string, string> = { Accept: "application/vnd.github.v3+json" };
- if (githubToken != null) {
- headers.Authorization = "token " + githubToken;
- }
-
- const response = await (() => {
- if (proxySettings.proxy) {
- log.debug(`Fetching release metadata via proxy: ${proxySettings.proxy}`);
- }
- const options: any = {};
- if (proxySettings.strictSSL) {
- options["rejectUnauthorized"] = false;
- }
- const agent = makeHttpAgent(proxySettings.proxy, options);
- return fetch(requestUrl, { headers: headers, agent: agent });
- })();
-
- if (!response.ok) {
- log.error("Error fetching artifact release info", {
- requestUrl,
- releaseTag,
- response: {
- headers: response.headers,
- status: response.status,
- body: await response.text(),
- }
- });
-
- throw new Error(
- `Got response ${response.status} when trying to fetch ` +
- `release info for ${releaseTag} release`
- );
- }
-
- // We skip runtime type checks for simplicity (here we cast from `unknown` to `GithubRelease`)
- const release = await response.json() as GithubRelease;
- return release;
-}
-
-// We omit declaration of tremendous amount of fields that we are not using here
-export interface GithubRelease {
- name: string;
- id: number;
- // eslint-disable-next-line camelcase
- published_at: string;
- assets: Array<{
- name: string;
- // eslint-disable-next-line camelcase
- browser_download_url: vscode.Uri;
- }>;
-}
-
-interface DownloadOpts {
- progressTitle: string;
- url: vscode.Uri;
- dest: vscode.Uri;
- mode?: number;
- gunzip?: boolean;
- proxySettings: ProxySettings;
-}
-
-export async function download(opts: DownloadOpts) {
- // Put artifact into a temporary file (in the same dir for simplicity)
- // to prevent partially downloaded files when user kills vscode
- // This also avoids overwriting running executables
- const randomHex = crypto.randomBytes(5).toString("hex");
- const rawDest = path.parse(opts.dest.fsPath);
- const oldServerPath = vscode.Uri.joinPath(vscode.Uri.file(rawDest.dir), `${rawDest.name}-stale-${randomHex}${rawDest.ext}`);
- const tempFilePath = vscode.Uri.joinPath(vscode.Uri.file(rawDest.dir), `${rawDest.name}${randomHex}`);
-
- await vscode.window.withProgress(
- {
- location: vscode.ProgressLocation.Notification,
- cancellable: false,
- title: opts.progressTitle
- },
- async (progress, _cancellationToken) => {
- let lastPercentage = 0;
- await downloadFile(opts.url, tempFilePath, opts.mode, !!opts.gunzip, opts.proxySettings, (readBytes, totalBytes) => {
- const newPercentage = Math.round((readBytes / totalBytes) * 100);
- if (newPercentage !== lastPercentage) {
- progress.report({
- message: `${newPercentage.toFixed(0)}%`,
- increment: newPercentage - lastPercentage
- });
-
- lastPercentage = newPercentage;
- }
- });
- }
- );
-
- // Try to rename a running server to avoid EPERM on Windows
- // NB: this can lead to issues if a running Code instance tries to restart the server.
- try {
- await vscode.workspace.fs.rename(opts.dest, oldServerPath, { overwrite: true });
- log.info(`Renamed old server binary ${opts.dest.fsPath} to ${oldServerPath.fsPath}`);
- } catch (err) {
- const fsErr = err as vscode.FileSystemError;
-
- // This is supposed to return `FileNotFound` (spelled as `EntryNotFound`)
- // but instead `code` is `Unknown` and `name` is `EntryNotFound (FileSystemError) (FileSystemError)`.
- // https://github.com/rust-analyzer/rust-analyzer/pull/10222
- if (!fsErr.code || fsErr.code !== "EntryNotFound" && fsErr.name.indexOf("EntryNotFound") === -1) {
- log.error(`Cannot rename existing server instance: ${err}"`);
- }
- }
- try {
- await vscode.workspace.fs.rename(tempFilePath, opts.dest, { overwrite: true });
- } catch (err) {
- log.error(`Cannot update server binary: ${err}`);
- }
-
- // Now try to remove any stale server binaries
- const serverDir = vscode.Uri.file(rawDest.dir);
- try {
- const entries = await vscode.workspace.fs.readDirectory(serverDir);
- for (const [entry, _] of entries) {
- try {
- if (entry.includes(`${rawDest.name}-stale-`)) {
- const uri = vscode.Uri.joinPath(serverDir, entry);
- try {
- await vscode.workspace.fs.delete(uri);
- log.info(`Removed old server binary ${uri.fsPath}`);
- } catch (err) {
- log.error(`Unable to remove old server binary ${uri.fsPath}`);
- }
- }
- } catch (err) {
- log.error(`Unable to parse ${entry}`);
- }
- }
- } catch (err) {
- log.error(`Unable to enumerate contents of ${serverDir.fsPath}`);
- }
-}
-
-async function downloadFile(
- url: vscode.Uri,
- destFilePath: vscode.Uri,
- mode: number | undefined,
- gunzip: boolean,
- proxySettings: ProxySettings,
- onProgress: (readBytes: number, totalBytes: number) => void
-): Promise<void> {
- const urlString = url.toString();
-
- const res = await (() => {
- if (proxySettings.proxy) {
- log.debug(`Downloading ${urlString} via proxy: ${proxySettings.proxy}`);
- }
- const options: any = {};
- if (proxySettings.strictSSL) {
- options["rejectUnauthorized"] = false;
- }
- const agent = makeHttpAgent(proxySettings.proxy, options);
- return fetch(urlString, { agent: agent });
- })();
-
- if (!res.ok) {
- log.error("Error", res.status, "while downloading file from", urlString);
- log.error({ body: await res.text(), headers: res.headers });
-
- throw new Error(`Got response ${res.status} when trying to download a file.`);
- }
-
- if (!res.body) {
- log.error("Empty body while downloading file from", urlString);
- log.error({ headers: res.headers });
- throw new Error(`Got empty body when trying to download a file.`);
- }
-
- const totalBytes = Number(res.headers.get('content-length'));
- assert(!Number.isNaN(totalBytes), "Sanity check of content-length protocol");
-
- log.debug("Downloading file of", totalBytes, "bytes size from", urlString, "to", destFilePath.fsPath);
-
- let readBytes = 0;
- res.body.on("data", (chunk: Buffer) => {
- readBytes += chunk.length;
- onProgress(readBytes, totalBytes);
- });
-
- const destFileStream = fs.createWriteStream(destFilePath.fsPath, { mode });
- const srcStream = gunzip ? res.body.pipe(zlib.createGunzip()) : res.body;
-
- await pipeline(srcStream, destFileStream);
-}
diff --git a/editors/code/src/persistent_state.ts b/editors/code/src/persistent_state.ts
index dd2aeecca3..3e86ed1e32 100644
--- a/editors/code/src/persistent_state.ts
+++ b/editors/code/src/persistent_state.ts
@@ -3,34 +3,13 @@ import { log } from './util';
export class PersistentState {
constructor(private readonly globalState: vscode.Memento) {
- const { lastCheck, nightlyReleaseId, serverVersion } = this;
- log.info("PersistentState:", { lastCheck, nightlyReleaseId, serverVersion });
- }
-
- /**
- * Used to check for *nightly* updates once an hour.
- */
- get lastCheck(): number | undefined {
- return this.globalState.get("lastCheck");
- }
- async updateLastCheck(value: number) {
- await this.globalState.update("lastCheck", value);
- }
-
- /**
- * Release id of the *nightly* extension.
- * Used to check if we should update.
- */
- get nightlyReleaseId(): number | undefined {
- return this.globalState.get("releaseId");
- }
- async updateNightlyReleaseId(value: number | undefined) {
- await this.globalState.update("releaseId", value);
+ const { serverVersion } = this;
+ log.info("PersistentState:", { serverVersion });
}
/**
* Version of the extension that installed the server.
- * Used to check if we need to update the server.
+ * Used to check if we need to run patchelf again on NixOS.
*/
get serverVersion(): string | undefined {
return this.globalState.get("serverVersion");
@@ -38,15 +17,4 @@ export class PersistentState {
async updateServerVersion(value: string | undefined) {
await this.globalState.update("serverVersion", value);
}
-
- /**
- * Github authorization token.
- * This is used for API requests against the Github API.
- */
- get githubToken(): string | undefined {
- return this.globalState.get("githubToken");
- }
- async updateGithubToken(value: string | undefined) {
- await this.globalState.update("githubToken", value);
- }
}