diff options
Diffstat (limited to 'editors/code/src/main.ts')
-rw-r--r-- | editors/code/src/main.ts | 88 |
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 | ||
6 | import * as commands from './commands'; | 6 | import * as commands from './commands'; |
7 | import { activateInlayHints } from './inlay_hints'; | 7 | import { activateInlayHints } from './inlay_hints'; |
8 | import { activateStatusDisplay } from './status_display'; | ||
9 | import { Ctx } from './ctx'; | 8 | import { Ctx } from './ctx'; |
10 | import { Config, NIGHTLY_TAG } from './config'; | 9 | import { Config, NIGHTLY_TAG } from './config'; |
11 | import { log, assert, isValidExecutable } from './util'; | 10 | import { log, assert, isValidExecutable } from './util'; |
@@ -20,6 +19,16 @@ let ctx: Ctx | undefined; | |||
20 | const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; | 19 | const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; |
21 | 20 | ||
22 | export async function activate(context: vscode.ExtensionContext) { | 21 | export 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 | |||
31 | async 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)) { |