Unnamed repository; edit this file 'description' to name the repository.
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
bors 2023-03-28
parent f735105 · parent 3622fb6 · commit 5bba438
-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 71f136b8c9..0dc5343f94 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 e0c143310d..f0ca8ff9db 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 987eb8aad6..7b27a06706 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 4e57bf0e29..0332fe3025 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 565cb9c643..4ca6601a6a 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 89264ebe46..dd74b31cc7 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) {