]> git.lizzy.rs Git - rust.git/blobdiff - editors/code/src/main.ts
Merge branch 'Veetaha-feat/sync-branch'
[rust.git] / editors / code / src / main.ts
index 980ed925b434480bacfd9408ec720110ee0f8139..cdb63b46f92f04b7d493b005783d6d14a14316de 100644 (file)
@@ -1,21 +1,23 @@
 import * as vscode from 'vscode';
 import * as path from "path";
 import * as os from "os";
-import { promises as fs } from "fs";
+import { promises as fs, PathLike } from "fs";
 
 import * as commands from './commands';
 import { activateInlayHints } from './inlay_hints';
-import { activateStatusDisplay } from './status_display';
 import { Ctx } from './ctx';
-import { activateHighlighting } from './highlighting';
 import { Config, NIGHTLY_TAG } from './config';
-import { log, assert } from './util';
+import { log, assert, isValidExecutable } from './util';
 import { PersistentState } from './persistent_state';
 import { fetchRelease, download } from './net';
-import { spawnSync } from 'child_process';
+import { activateTaskProvider } from './tasks';
+import { setContextValue } from './util';
+import { exec } from 'child_process';
 
 let ctx: Ctx | undefined;
 
+const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
+
 export async function activate(context: vscode.ExtensionContext) {
     // Register a "dumb" onEnter command for the case where server fails to
     // start.
@@ -39,13 +41,35 @@ export async function activate(context: vscode.ExtensionContext) {
 
     const config = new Config(context);
     const state = new PersistentState(context.globalState);
-    const serverPath = await bootstrap(config, state);
+    const serverPath = await bootstrap(config, state).catch(err => {
+        let message = "bootstrap error. ";
+
+        if (err.code === "EBUSY" || err.code === "ETXTBSY") {
+            message += "Other vscode windows might be using rust-analyzer, ";
+            message += "you should close them and reload this window to retry. ";
+        }
+
+        message += 'Open "Help > Toggle Developer Tools > Console" to see the logs ';
+        message += '(enable verbose logs with "rust-analyzer.trace.extension")';
+
+        log.error("Bootstrap error", err);
+        throw new Error(message);
+    });
+
+    const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
+    if (workspaceFolder === undefined) {
+        const err = "Cannot activate rust-analyzer when no folder is opened";
+        void vscode.window.showErrorMessage(err);
+        throw new Error(err);
+    }
 
     // Note: we try to start the server before we activate type hints so that it
     // registers its `onDidChangeDocument` handler before us.
     //
     // This a horribly, horribly wrong way to deal with this problem.
-    ctx = await Ctx.create(config, context, serverPath);
+    ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath);
+
+    setContextValue(RUST_PROJECT_CONTEXT_NAME, true);
 
     // Commands which invokes manually via command palette, shortcut, etc.
 
@@ -71,29 +95,38 @@ export async function activate(context: vscode.ExtensionContext) {
     ctx.registerCommand('syntaxTree', commands.syntaxTree);
     ctx.registerCommand('expandMacro', commands.expandMacro);
     ctx.registerCommand('run', commands.run);
+    ctx.registerCommand('debug', commands.debug);
+    ctx.registerCommand('newDebugConfig', commands.newDebugConfig);
 
     defaultOnEnter.dispose();
     ctx.registerCommand('onEnter', commands.onEnter);
 
     ctx.registerCommand('ssr', commands.ssr);
     ctx.registerCommand('serverVersion', commands.serverVersion);
+    ctx.registerCommand('toggleInlayHints', commands.toggleInlayHints);
 
     // Internal commands which are invoked by the server.
     ctx.registerCommand('runSingle', commands.runSingle);
     ctx.registerCommand('debugSingle', commands.debugSingle);
     ctx.registerCommand('showReferences', commands.showReferences);
-    ctx.registerCommand('applySourceChange', commands.applySourceChange);
-    ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange);
+    ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
+    ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
+    ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
+    ctx.registerCommand('gotoLocation', commands.gotoLocation);
 
-    activateStatusDisplay(ctx);
+    ctx.pushCleanup(activateTaskProvider(workspaceFolder));
 
-    if (!ctx.config.highlightingSemanticTokens) {
-        activateHighlighting(ctx);
-    }
     activateInlayHints(ctx);
+
+    vscode.workspace.onDidChangeConfiguration(
+        _ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }),
+        null,
+        ctx.subscriptions,
+    );
 }
 
 export async function deactivate() {
+    setContextValue(RUST_PROJECT_CONTEXT_NAME, undefined);
     await ctx?.client.stop();
     ctx = undefined;
 }
@@ -146,7 +179,11 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi
     assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
 
     const dest = path.join(config.globalStoragePath, "rust-analyzer.vsix");
-    await download(artifact.browser_download_url, dest, "Downloading rust-analyzer extension");
+    await download({
+        url: artifact.browser_download_url,
+        dest,
+        progressTitle: "Downloading rust-analyzer extension",
+    });
 
     await vscode.commands.executeCommand("workbench.extensions.installExtension", vscode.Uri.file(dest));
     await fs.unlink(dest);
@@ -165,16 +202,55 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise<
         );
     }
 
-    const res = spawnSync(path, ["--version"], { encoding: 'utf8' });
-    log.debug("Checked binary availability via --version", res);
-    log.debug(res, "--version output:", res.output);
-    if (res.status !== 0) {
+    log.debug("Using server binary at", path);
+
+    if (!isValidExecutable(path)) {
         throw new Error(`Failed to execute ${path} --version`);
     }
 
     return path;
 }
 
+async function patchelf(dest: PathLike): Promise<void> {
+    await vscode.window.withProgress(
+        {
+            location: vscode.ProgressLocation.Notification,
+            title: "Patching rust-analyzer for NixOS"
+        },
+        async (progress, _) => {
+            const expression = `
+            {src, pkgs ? import <nixpkgs> {}}:
+                pkgs.stdenv.mkDerivation {
+                    name = "rust-analyzer";
+                    inherit src;
+                    phases = [ "installPhase" "fixupPhase" ];
+                    installPhase = "cp $src $out";
+                    fixupPhase = ''
+                    chmod 755 $out
+                    patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out
+                    '';
+                }
+            `;
+            const origFile = dest + "-orig";
+            await fs.rename(dest, origFile);
+            progress.report({ message: "Patching executable", increment: 20 });
+            await new Promise((resolve, reject) => {
+                const handle = exec(`nix-build -E - --arg src '${origFile}' -o ${dest}`,
+                    (err, stdout, stderr) => {
+                        if (err != null) {
+                            reject(Error(stderr));
+                        } else {
+                            resolve(stdout);
+                        }
+                    });
+                handle.stdin?.write(expression);
+                handle.stdin?.end();
+            });
+            await fs.unlink(origFile);
+        }
+    );
+}
+
 async function getServer(config: Config, state: PersistentState): Promise<string | undefined> {
     const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath;
     if (explicitPath) {
@@ -223,7 +299,23 @@ async function getServer(config: Config, state: PersistentState): Promise<string
     const artifact = release.assets.find(artifact => artifact.name === binaryName);
     assert(!!artifact, `Bad release: ${JSON.stringify(release)}`);
 
-    await download(artifact.browser_download_url, dest, "Downloading rust-analyzer server", { mode: 0o755 });
+    // Unlinking the exe file before moving new one on its place should prevent ETXTBSY error.
+    await fs.unlink(dest).catch(err => {
+        if (err.code !== "ENOENT") throw err;
+    });
+
+    await download({
+        url: artifact.browser_download_url,
+        dest,
+        progressTitle: "Downloading rust-analyzer server",
+        mode: 0o755
+    });
+
+    // Patching executable if that's NixOS.
+    if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) {
+        await patchelf(dest);
+    }
+
     await state.updateServerVersion(config.package.version);
     return dest;
 }