diff options
Diffstat (limited to 'editors/code/src/main.ts')
-rw-r--r-- | editors/code/src/main.ts | 70 |
1 files changed, 61 insertions, 9 deletions
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index efd56a84b..b7337621c 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -1,21 +1,24 @@ | |||
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 * as os from "os"; | 3 | import * as os from "os"; |
4 | import { promises as fs } from "fs"; | 4 | 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'; | 8 | import { activateStatusDisplay } from './status_display'; |
9 | import { Ctx } from './ctx'; | 9 | import { Ctx } from './ctx'; |
10 | import { Config, NIGHTLY_TAG } from './config'; | 10 | import { Config, NIGHTLY_TAG } from './config'; |
11 | import { log, assert } from './util'; | 11 | import { log, assert, isValidExecutable } from './util'; |
12 | import { PersistentState } from './persistent_state'; | 12 | import { PersistentState } from './persistent_state'; |
13 | import { fetchRelease, download } from './net'; | 13 | import { fetchRelease, download } from './net'; |
14 | import { spawnSync } from 'child_process'; | ||
15 | import { activateTaskProvider } from './tasks'; | 14 | import { activateTaskProvider } from './tasks'; |
15 | import { setContextValue } from './util'; | ||
16 | import { exec } from 'child_process'; | ||
16 | 17 | ||
17 | let ctx: Ctx | undefined; | 18 | let ctx: Ctx | undefined; |
18 | 19 | ||
20 | const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; | ||
21 | |||
19 | export async function activate(context: vscode.ExtensionContext) { | 22 | export async function activate(context: vscode.ExtensionContext) { |
20 | // Register a "dumb" onEnter command for the case where server fails to | 23 | // Register a "dumb" onEnter command for the case where server fails to |
21 | // start. | 24 | // start. |
@@ -54,6 +57,8 @@ export async function activate(context: vscode.ExtensionContext) { | |||
54 | // This a horribly, horribly wrong way to deal with this problem. | 57 | // This a horribly, horribly wrong way to deal with this problem. |
55 | ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath); | 58 | ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath); |
56 | 59 | ||
60 | setContextValue(RUST_PROJECT_CONTEXT_NAME, true); | ||
61 | |||
57 | // Commands which invokes manually via command palette, shortcut, etc. | 62 | // Commands which invokes manually via command palette, shortcut, etc. |
58 | 63 | ||
59 | // Reloading is inspired by @DanTup maneuver: https://github.com/microsoft/vscode/issues/45774#issuecomment-373423895 | 64 | // Reloading is inspired by @DanTup maneuver: https://github.com/microsoft/vscode/issues/45774#issuecomment-373423895 |
@@ -78,19 +83,22 @@ export async function activate(context: vscode.ExtensionContext) { | |||
78 | ctx.registerCommand('syntaxTree', commands.syntaxTree); | 83 | ctx.registerCommand('syntaxTree', commands.syntaxTree); |
79 | ctx.registerCommand('expandMacro', commands.expandMacro); | 84 | ctx.registerCommand('expandMacro', commands.expandMacro); |
80 | ctx.registerCommand('run', commands.run); | 85 | ctx.registerCommand('run', commands.run); |
86 | ctx.registerCommand('debug', commands.debug); | ||
87 | ctx.registerCommand('newDebugConfig', commands.newDebugConfig); | ||
81 | 88 | ||
82 | defaultOnEnter.dispose(); | 89 | defaultOnEnter.dispose(); |
83 | ctx.registerCommand('onEnter', commands.onEnter); | 90 | ctx.registerCommand('onEnter', commands.onEnter); |
84 | 91 | ||
85 | ctx.registerCommand('ssr', commands.ssr); | 92 | ctx.registerCommand('ssr', commands.ssr); |
86 | ctx.registerCommand('serverVersion', commands.serverVersion); | 93 | ctx.registerCommand('serverVersion', commands.serverVersion); |
94 | ctx.registerCommand('toggleInlayHints', commands.toggleInlayHints); | ||
87 | 95 | ||
88 | // Internal commands which are invoked by the server. | 96 | // Internal commands which are invoked by the server. |
89 | ctx.registerCommand('runSingle', commands.runSingle); | 97 | ctx.registerCommand('runSingle', commands.runSingle); |
90 | ctx.registerCommand('debugSingle', commands.debugSingle); | 98 | ctx.registerCommand('debugSingle', commands.debugSingle); |
91 | ctx.registerCommand('showReferences', commands.showReferences); | 99 | ctx.registerCommand('showReferences', commands.showReferences); |
92 | ctx.registerCommand('applySourceChange', commands.applySourceChange); | 100 | ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand); |
93 | ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); | 101 | ctx.registerCommand('applyActionGroup', commands.applyActionGroup); |
94 | 102 | ||
95 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); | 103 | ctx.pushCleanup(activateTaskProvider(workspaceFolder)); |
96 | 104 | ||
@@ -106,6 +114,7 @@ export async function activate(context: vscode.ExtensionContext) { | |||
106 | } | 114 | } |
107 | 115 | ||
108 | export async function deactivate() { | 116 | export async function deactivate() { |
117 | setContextValue(RUST_PROJECT_CONTEXT_NAME, undefined); | ||
109 | await ctx?.client.stop(); | 118 | await ctx?.client.stop(); |
110 | ctx = undefined; | 119 | ctx = undefined; |
111 | } | 120 | } |
@@ -179,16 +188,53 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise< | |||
179 | 188 | ||
180 | log.debug("Using server binary at", path); | 189 | log.debug("Using server binary at", path); |
181 | 190 | ||
182 | const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); | 191 | if (!isValidExecutable(path)) { |
183 | log.debug("Checked binary availability via --version", res); | ||
184 | log.debug(res, "--version output:", res.output); | ||
185 | if (res.status !== 0) { | ||
186 | throw new Error(`Failed to execute ${path} --version`); | 192 | throw new Error(`Failed to execute ${path} --version`); |
187 | } | 193 | } |
188 | 194 | ||
189 | return path; | 195 | return path; |
190 | } | 196 | } |
191 | 197 | ||
198 | async function patchelf(dest: PathLike): Promise<void> { | ||
199 | await vscode.window.withProgress( | ||
200 | { | ||
201 | location: vscode.ProgressLocation.Notification, | ||
202 | title: "Patching rust-analyzer for NixOS" | ||
203 | }, | ||
204 | async (progress, _) => { | ||
205 | const expression = ` | ||
206 | {src, pkgs ? import <nixpkgs> {}}: | ||
207 | pkgs.stdenv.mkDerivation { | ||
208 | name = "rust-analyzer"; | ||
209 | inherit src; | ||
210 | phases = [ "installPhase" "fixupPhase" ]; | ||
211 | installPhase = "cp $src $out"; | ||
212 | fixupPhase = '' | ||
213 | chmod 755 $out | ||
214 | patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out | ||
215 | ''; | ||
216 | } | ||
217 | `; | ||
218 | const origFile = dest + "-orig"; | ||
219 | await fs.rename(dest, origFile); | ||
220 | progress.report({ message: "Patching executable", increment: 20 }); | ||
221 | await new Promise((resolve, reject) => { | ||
222 | const handle = exec(`nix-build -E - --arg src '${origFile}' -o ${dest}`, | ||
223 | (err, stdout, stderr) => { | ||
224 | if (err != null) { | ||
225 | reject(Error(stderr)); | ||
226 | } else { | ||
227 | resolve(stdout); | ||
228 | } | ||
229 | }); | ||
230 | handle.stdin?.write(expression); | ||
231 | handle.stdin?.end(); | ||
232 | }); | ||
233 | await fs.unlink(origFile); | ||
234 | } | ||
235 | ); | ||
236 | } | ||
237 | |||
192 | async function getServer(config: Config, state: PersistentState): Promise<string | undefined> { | 238 | async function getServer(config: Config, state: PersistentState): Promise<string | undefined> { |
193 | const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath; | 239 | const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath; |
194 | if (explicitPath) { | 240 | if (explicitPath) { |
@@ -238,6 +284,12 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
238 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); | 284 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); |
239 | 285 | ||
240 | await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 }); | 286 | await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 }); |
287 | |||
288 | // Patching executable if that's NixOS. | ||
289 | if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) { | ||
290 | await patchelf(dest); | ||
291 | } | ||
292 | |||
241 | await state.updateServerVersion(config.package.version); | 293 | await state.updateServerVersion(config.package.version); |
242 | return dest; | 294 | return dest; |
243 | } | 295 | } |