about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-28 07:05:43 +0000
committerbors <bors@rust-lang.org>2023-03-28 07:05:43 +0000
commit5bba438c9cf684812e72e9d90c8fea55bd29909c (patch)
tree4b7988975a168b0cac6cc18fe3e31daa4ed938e2
parentf73510560f93262ad2e375d63b526af37aa3ae48 (diff)
parent3622fb645632fcf81af82919259be4b0012a3939 (diff)
downloadrust-5bba438c9cf684812e72e9d90c8fea55bd29909c.tar.gz
rust-5bba438c9cf684812e72e9d90c8fea55bd29909c.zip
Auto merge of #14366 - Veykril:linked-proj, r=Veykril
feat: Pop a notification prompting the user to add a Cargo.toml of unlinked file to the linkedProjects

cc https://github.com/rust-lang/rust-analyzer/issues/13226 https://github.com/rust-lang/rust-analyzer/issues/9661
-rw-r--r--crates/ide-diagnostics/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/global_state.rs4
-rw-r--r--crates/rust-analyzer/src/reload.rs3
-rw-r--r--editors/code/package.json5
-rw-r--r--editors/code/src/client.ts69
-rw-r--r--editors/code/src/ctx.ts7
6 files changed, 74 insertions, 15 deletions
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 71f136b8c90..0dc5343f942 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -74,6 +74,7 @@ use ide_db::{
 };
 use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange};
 
+// FIXME: Make this an enum
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub struct DiagnosticCode(pub &'static str);
 
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index e0c143310d8..f0ca8ff9dbd 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -337,7 +337,7 @@ impl GlobalState {
     }
 
     pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
-        &mut self,
+        &self,
         params: N::Params,
     ) {
         let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
@@ -378,7 +378,7 @@ impl GlobalState {
         self.req_queue.incoming.is_completed(&request.id)
     }
 
-    fn send(&mut self, message: lsp_server::Message) {
+    fn send(&self, message: lsp_server::Message) {
         self.sender.send(message).unwrap()
     }
 }
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 987eb8aad6d..7b27a067062 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -120,7 +120,8 @@ impl GlobalState {
             && self.config.notifications().cargo_toml_not_found
         {
             status.health = lsp_ext::Health::Warning;
-            message.push_str("Failed to discover workspace.\n\n");
+            message.push_str("Failed to discover workspace.\n");
+            message.push_str("Consider adding the `Cargo.toml` of the workspace to the [`linkedProjects`](https://rust-analyzer.github.io/manual.html#rust-analyzer.linkedProjects) setting.\n\n");
         }
 
         for ws in self.workspaces.iter() {
diff --git a/editors/code/package.json b/editors/code/package.json
index 4e57bf0e29b..0332fe30251 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -449,6 +449,11 @@
                         "type": "string"
                     }
                 },
+                "rust-analyzer.showUnlinkedFileNotification": {
+                    "markdownDescription": "Whether to show a notification for unlinked files asking the user to add the corresponding Cargo.toml to the linked projects setting.",
+                    "default": true,
+                    "type": "boolean"
+                },
                 "$generated-start": {},
                 "rust-analyzer.assist.emitMustUse": {
                     "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 565cb9c6432..4ca6601a6aa 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -8,6 +8,7 @@ import * as diagnostics from "./diagnostics";
 import { WorkspaceEdit } from "vscode";
 import { Config, prepareVSCodeConfig } from "./config";
 import { randomUUID } from "crypto";
+import { sep as pathSeparator } from "path";
 
 export interface Env {
     [name: string]: string;
@@ -69,7 +70,8 @@ export async function createClient(
     outputChannel: vscode.OutputChannel,
     initializationOptions: vscode.WorkspaceConfiguration,
     serverOptions: lc.ServerOptions,
-    config: Config
+    config: Config,
+    unlinkedFiles: vscode.Uri[]
 ): Promise<lc.LanguageClient> {
     const clientOptions: lc.LanguageClientOptions = {
         documentSelector: [{ scheme: "file", language: "rust" }],
@@ -119,6 +121,60 @@ export async function createClient(
                 const preview = config.previewRustcOutput;
                 const errorCode = config.useRustcErrorCode;
                 diagnosticList.forEach((diag, idx) => {
+                    const value =
+                        typeof diag.code === "string" || typeof diag.code === "number"
+                            ? diag.code
+                            : diag.code?.value;
+                    if (value === "unlinked-file" && !unlinkedFiles.includes(uri)) {
+                        const config = vscode.workspace.getConfiguration("rust-analyzer");
+                        if (config.get("showUnlinkedFileNotification")) {
+                            unlinkedFiles.push(uri);
+                            const folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath;
+                            if (folder) {
+                                const parentBackslash = uri.fsPath.lastIndexOf(
+                                    pathSeparator + "src"
+                                );
+                                const parent = uri.fsPath.substring(0, parentBackslash);
+
+                                if (parent.startsWith(folder)) {
+                                    const path = vscode.Uri.file(
+                                        parent + pathSeparator + "Cargo.toml"
+                                    );
+                                    void vscode.workspace.fs.stat(path).then(async () => {
+                                        const choice = await vscode.window.showInformationMessage(
+                                            `This rust file does not belong to a loaded cargo project. It looks like it might belong to the workspace at ${path}, do you want to add it to the linked Projects?`,
+                                            "Yes",
+                                            "No",
+                                            "Don't show this again"
+                                        );
+                                        switch (choice) {
+                                            case "Yes":
+                                                break;
+                                            case "No":
+                                                await config.update(
+                                                    "linkedProjects",
+                                                    config
+                                                        .get<any[]>("linkedProjects")
+                                                        ?.concat(
+                                                            path.fsPath.substring(folder.length)
+                                                        ),
+                                                    false
+                                                );
+                                                break;
+                                            case "Don't show this again":
+                                                await config.update(
+                                                    "showUnlinkedFileNotification",
+                                                    false,
+                                                    false
+                                                );
+                                                break;
+                                        }
+                                    });
+                                }
+                            }
+                        }
+                    }
+
                     // Abuse the fact that VSCode leaks the LSP diagnostics data field through the
                     // Diagnostic class, if they ever break this we are out of luck and have to go
                     // back to the worst diagnostics experience ever:)
@@ -138,14 +194,6 @@ export async function createClient(
                                 .substring(0, index)
                                 .replace(/^ -->[^\n]+\n/m, "");
                         }
-                        let value;
-                        if (errorCode) {
-                            if (typeof diag.code === "string" || typeof diag.code === "number") {
-                                value = diag.code;
-                            } else {
-                                value = diag.code?.value;
-                            }
-                        }
                         diag.code = {
                             target: vscode.Uri.from({
                                 scheme: diagnostics.URI_SCHEME,
@@ -153,7 +201,8 @@ export async function createClient(
                                 fragment: uri.toString(),
                                 query: idx.toString(),
                             }),
-                            value: value ?? "Click for full compiler diagnostic",
+                            value:
+                                errorCode && value ? value : "Click for full compiler diagnostic",
                         };
                     }
                 });
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 89264ebe469..dd74b31cc71 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -82,6 +82,7 @@ export class Ctx {
     private state: PersistentState;
     private commandFactories: Record<string, CommandFactory>;
     private commandDisposables: Disposable[];
+    private unlinkedFiles: vscode.Uri[];
 
     get client() {
         return this._client;
@@ -94,11 +95,11 @@ export class Ctx {
     ) {
         extCtx.subscriptions.push(this);
         this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
-        this.statusBar.show();
         this.workspace = workspace;
         this.clientSubscriptions = [];
         this.commandDisposables = [];
         this.commandFactories = commandFactories;
+        this.unlinkedFiles = [];
 
         this.state = new PersistentState(extCtx.globalState);
         this.config = new Config(extCtx);
@@ -218,7 +219,8 @@ export class Ctx {
                 this.outputChannel,
                 initializationOptions,
                 serverOptions,
-                this.config
+                this.config,
+                this.unlinkedFiles
             );
             this.pushClientCleanup(
                 this._client.onNotification(ra.serverStatus, (params) =>
@@ -335,6 +337,7 @@ export class Ctx {
     setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
         let icon = "";
         const statusBar = this.statusBar;
+        statusBar.show();
         statusBar.tooltip = new vscode.MarkdownString("", true);
         statusBar.tooltip.isTrusted = true;
         switch (status.health) {