aboutsummaryrefslogtreecommitdiff
path: root/editors/code/src/main.ts
diff options
context:
space:
mode:
Diffstat (limited to 'editors/code/src/main.ts')
-rw-r--r--editors/code/src/main.ts88
1 files changed, 64 insertions, 24 deletions
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index b7337621c..bd99d696a 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -5,7 +5,6 @@ import { promises as fs, PathLike } from "fs";
5 5
6import * as commands from './commands'; 6import * as commands from './commands';
7import { activateInlayHints } from './inlay_hints'; 7import { activateInlayHints } from './inlay_hints';
8import { activateStatusDisplay } from './status_display';
9import { Ctx } from './ctx'; 8import { Ctx } from './ctx';
10import { Config, NIGHTLY_TAG } from './config'; 9import { Config, NIGHTLY_TAG } from './config';
11import { log, assert, isValidExecutable } from './util'; 10import { log, assert, isValidExecutable } from './util';
@@ -20,6 +19,16 @@ let ctx: Ctx | undefined;
20const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; 19const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
21 20
22export async function activate(context: vscode.ExtensionContext) { 21export async function activate(context: vscode.ExtensionContext) {
22 // For some reason vscode not always shows pop-up error notifications
23 // when an extension fails to activate, so we do it explicitly by ourselves.
24 // FIXME: remove this bit of code once vscode fixes this issue: https://github.com/microsoft/vscode/issues/101242
25 await tryActivate(context).catch(err => {
26 void vscode.window.showErrorMessage(`Cannot activate rust-analyzer: ${err.message}`);
27 throw err;
28 });
29}
30
31async function tryActivate(context: vscode.ExtensionContext) {
23 // Register a "dumb" onEnter command for the case where server fails to 32 // Register a "dumb" onEnter command for the case where server fails to
24 // start. 33 // start.
25 // 34 //
@@ -42,13 +51,24 @@ export async function activate(context: vscode.ExtensionContext) {
42 51
43 const config = new Config(context); 52 const config = new Config(context);
44 const state = new PersistentState(context.globalState); 53 const state = new PersistentState(context.globalState);
45 const serverPath = await bootstrap(config, state); 54 const serverPath = await bootstrap(config, state).catch(err => {
55 let message = "bootstrap error. ";
56
57 if (err.code === "EBUSY" || err.code === "ETXTBSY" || err.code === "EPERM") {
58 message += "Other vscode windows might be using rust-analyzer, ";
59 message += "you should close them and reload this window to retry. ";
60 }
61
62 message += 'See the logs in "OUTPUT > Rust Analyzer Client" (should open automatically). ';
63 message += 'To enable verbose logs use { "rust-analyzer.trace.extension": true }';
64
65 log.error("Bootstrap error", err);
66 throw new Error(message);
67 });
46 68
47 const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; 69 const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
48 if (workspaceFolder === undefined) { 70 if (workspaceFolder === undefined) {
49 const err = "Cannot activate rust-analyzer when no folder is opened"; 71 throw new Error("no folder is opened");
50 void vscode.window.showErrorMessage(err);
51 throw new Error(err);
52 } 72 }
53 73
54 // Note: we try to start the server before we activate type hints so that it 74 // Note: we try to start the server before we activate type hints so that it
@@ -76,7 +96,8 @@ export async function activate(context: vscode.ExtensionContext) {
76 }); 96 });
77 97
78 ctx.registerCommand('analyzerStatus', commands.analyzerStatus); 98 ctx.registerCommand('analyzerStatus', commands.analyzerStatus);
79 ctx.registerCommand('collectGarbage', commands.collectGarbage); 99 ctx.registerCommand('memoryUsage', commands.memoryUsage);
100 ctx.registerCommand('reloadWorkspace', commands.reloadWorkspace);
80 ctx.registerCommand('matchingBrace', commands.matchingBrace); 101 ctx.registerCommand('matchingBrace', commands.matchingBrace);
81 ctx.registerCommand('joinLines', commands.joinLines); 102 ctx.registerCommand('joinLines', commands.joinLines);
82 ctx.registerCommand('parentModule', commands.parentModule); 103 ctx.registerCommand('parentModule', commands.parentModule);
@@ -98,11 +119,11 @@ export async function activate(context: vscode.ExtensionContext) {
98 ctx.registerCommand('debugSingle', commands.debugSingle); 119 ctx.registerCommand('debugSingle', commands.debugSingle);
99 ctx.registerCommand('showReferences', commands.showReferences); 120 ctx.registerCommand('showReferences', commands.showReferences);
100 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); 121 ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
122 ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
101 ctx.registerCommand('applyActionGroup', commands.applyActionGroup); 123 ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
124 ctx.registerCommand('gotoLocation', commands.gotoLocation);
102 125
103 ctx.pushCleanup(activateTaskProvider(workspaceFolder)); 126 ctx.pushCleanup(activateTaskProvider(workspaceFolder, ctx.config));
104
105 activateStatusDisplay(ctx);
106 127
107 activateInlayHints(ctx); 128 activateInlayHints(ctx);
108 129
@@ -140,13 +161,17 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
140 return; 161 return;
141 }; 162 };
142 163
143 const lastCheck = state.lastCheck;
144 const now = Date.now(); 164 const now = Date.now();
165 if (config.package.releaseTag === NIGHTLY_TAG) {
166 // Check if we should poll github api for the new nightly version
167 // if we haven't done it during the past hour
168 const lastCheck = state.lastCheck;
145 169
146 const anHour = 60 * 60 * 1000; 170 const anHour = 60 * 60 * 1000;
147 const shouldDownloadNightly = state.releaseId === undefined || (now - (lastCheck ?? 0)) > anHour; 171 const shouldCheckForNewNightly = state.releaseId === undefined || (now - (lastCheck ?? 0)) > anHour;
148 172
149 if (!shouldDownloadNightly) return; 173 if (!shouldCheckForNewNightly) return;
174 }
150 175
151 const release = await fetchRelease("nightly").catch((e) => { 176 const release = await fetchRelease("nightly").catch((e) => {
152 log.error(e); 177 log.error(e);
@@ -167,7 +192,11 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
167 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 192 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
168 193
169 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix"); 194 const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix");
170 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer extension"); 195 await download({
196 url: artifact.browser_download_url,
197 dest,
198 progressTitle: "Downloading rust-analyzer extension",
199 });
171 200
172 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest)); 201 await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest));
173 await fs.unlink(dest); 202 await fs.unlink(dest);
@@ -186,7 +215,7 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise<
186 ); 215 );
187 } 216 }
188 217
189 log.debug("Using server binary at", path); 218 log.info("Using server binary at", path);
190 219
191 if (!isValidExecutable(path)) { 220 if (!isValidExecutable(path)) {
192 throw new Error(`Failed to execute ${path} --version`); 221 throw new Error(`Failed to execute ${path} --version`);
@@ -245,13 +274,13 @@ async function getServer(config: Config, state: PersistentState): Promise<string
245 }; 274 };
246 if (config.package.releaseTag === null) return "rust-analyzer"; 275 if (config.package.releaseTag === null) return "rust-analyzer";
247 276
248 let binaryName: string | undefined = undefined; 277 let platform: string | undefined;
249 if (process.arch === "x64" || process.arch === "ia32") { 278 if (process.arch === "x64" || process.arch === "ia32") {
250 if (process.platform === "linux") binaryName = "rust-analyzer-linux"; 279 if (process.platform === "linux") platform = "linux";
251 if (process.platform === "darwin") binaryName = "rust-analyzer-mac"; 280 if (process.platform === "darwin") platform = "mac";
252 if (process.platform === "win32") binaryName = "rust-analyzer-windows.exe"; 281 if (process.platform === "win32") platform = "windows";
253 } 282 }
254 if (binaryName === undefined) { 283 if (platform === undefined) {
255 vscode.window.showErrorMessage( 284 vscode.window.showErrorMessage(
256 "Unfortunately we don't ship binaries for your platform yet. " + 285 "Unfortunately we don't ship binaries for your platform yet. " +
257 "You need to manually clone rust-analyzer repository and " + 286 "You need to manually clone rust-analyzer repository and " +
@@ -262,8 +291,8 @@ async function getServer(config: Config, state: PersistentState): Promise<string
262 ); 291 );
263 return undefined; 292 return undefined;
264 } 293 }
265 294 const ext = platform === "windows" ? ".exe" : "";
266 const dest = path.join(config.globalStoragePath, binaryName); 295 const dest = path.join(config.globalStoragePath, `rust-analyzer-${platform}${ext}`);
267 const exists = await fs.stat(dest).then(() => true, () => false); 296 const exists = await fs.stat(dest).then(() => true, () => false);
268 if (!exists) { 297 if (!exists) {
269 await state.updateServerVersion(undefined); 298 await state.updateServerVersion(undefined);
@@ -280,10 +309,21 @@ async function getServer(config: Config, state: PersistentState): Promise<string
280 } 309 }
281 310
282 const release = await fetchRelease(config.package.releaseTag); 311 const release = await fetchRelease(config.package.releaseTag);
283 const artifact = release.assets.find(artifact => artifact.name === binaryName); 312 const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`);
284 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); 313 assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
285 314
286 await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 }); 315 // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error.
316 await fs.unlink(dest).catch(err => {
317 if (err.code !== "ENOENT") throw err;
318 });
319
320 await download({
321 url: artifact.browser_download_url,
322 dest,
323 progressTitle: "Downloading rust-analyzer server",
324 gunzip: true,
325 mode: 0o755
326 });
287 327
288 // Patching executable if that's NixOS. 328 // Patching executable if that's NixOS.
289 if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) { 329 if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) {