Skip to content

Commit 5bba438

Browse files
committed
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 #13226 #9661
2 parents f735105 + 3622fb6 commit 5bba438

File tree

6 files changed

+74
-15
lines changed

6 files changed

+74
-15
lines changed

crates/ide-diagnostics/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ use ide_db::{
7474
};
7575
use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange};
7676

77+
// FIXME: Make this an enum
7778
#[derive(Copy, Clone, Debug, PartialEq)]
7879
pub struct DiagnosticCode(pub &'static str);
7980

crates/rust-analyzer/src/global_state.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ impl GlobalState {
337337
}
338338

339339
pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
340-
&mut self,
340+
&self,
341341
params: N::Params,
342342
) {
343343
let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
@@ -378,7 +378,7 @@ impl GlobalState {
378378
self.req_queue.incoming.is_completed(&request.id)
379379
}
380380

381-
fn send(&mut self, message: lsp_server::Message) {
381+
fn send(&self, message: lsp_server::Message) {
382382
self.sender.send(message).unwrap()
383383
}
384384
}

crates/rust-analyzer/src/reload.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ impl GlobalState {
120120
&& self.config.notifications().cargo_toml_not_found
121121
{
122122
status.health = lsp_ext::Health::Warning;
123-
message.push_str("Failed to discover workspace.\n\n");
123+
message.push_str("Failed to discover workspace.\n");
124+
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");
124125
}
125126

126127
for ws in self.workspaces.iter() {

editors/code/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,11 @@
449449
"type": "string"
450450
}
451451
},
452+
"rust-analyzer.showUnlinkedFileNotification": {
453+
"markdownDescription": "Whether to show a notification for unlinked files asking the user to add the corresponding Cargo.toml to the linked projects setting.",
454+
"default": true,
455+
"type": "boolean"
456+
},
452457
"$generated-start": {},
453458
"rust-analyzer.assist.emitMustUse": {
454459
"markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.",

editors/code/src/client.ts

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import * as diagnostics from "./diagnostics";
88
import { WorkspaceEdit } from "vscode";
99
import { Config, prepareVSCodeConfig } from "./config";
1010
import { randomUUID } from "crypto";
11+
import { sep as pathSeparator } from "path";
1112

1213
export interface Env {
1314
[name: string]: string;
@@ -69,7 +70,8 @@ export async function createClient(
6970
outputChannel: vscode.OutputChannel,
7071
initializationOptions: vscode.WorkspaceConfiguration,
7172
serverOptions: lc.ServerOptions,
72-
config: Config
73+
config: Config,
74+
unlinkedFiles: vscode.Uri[]
7375
): Promise<lc.LanguageClient> {
7476
const clientOptions: lc.LanguageClientOptions = {
7577
documentSelector: [{ scheme: "file", language: "rust" }],
@@ -119,6 +121,60 @@ export async function createClient(
119121
const preview = config.previewRustcOutput;
120122
const errorCode = config.useRustcErrorCode;
121123
diagnosticList.forEach((diag, idx) => {
124+
const value =
125+
typeof diag.code === "string" || typeof diag.code === "number"
126+
? diag.code
127+
: diag.code?.value;
128+
if (value === "unlinked-file" && !unlinkedFiles.includes(uri)) {
129+
const config = vscode.workspace.getConfiguration("rust-analyzer");
130+
if (config.get("showUnlinkedFileNotification")) {
131+
unlinkedFiles.push(uri);
132+
const folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath;
133+
if (folder) {
134+
const parentBackslash = uri.fsPath.lastIndexOf(
135+
pathSeparator + "src"
136+
);
137+
const parent = uri.fsPath.substring(0, parentBackslash);
138+
139+
if (parent.startsWith(folder)) {
140+
const path = vscode.Uri.file(
141+
parent + pathSeparator + "Cargo.toml"
142+
);
143+
void vscode.workspace.fs.stat(path).then(async () => {
144+
const choice = await vscode.window.showInformationMessage(
145+
`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?`,
146+
"Yes",
147+
"No",
148+
"Don't show this again"
149+
);
150+
switch (choice) {
151+
case "Yes":
152+
break;
153+
case "No":
154+
await config.update(
155+
"linkedProjects",
156+
config
157+
.get<any[]>("linkedProjects")
158+
?.concat(
159+
path.fsPath.substring(folder.length)
160+
),
161+
false
162+
);
163+
break;
164+
case "Don't show this again":
165+
await config.update(
166+
"showUnlinkedFileNotification",
167+
false,
168+
false
169+
);
170+
break;
171+
}
172+
});
173+
}
174+
}
175+
}
176+
}
177+
122178
// Abuse the fact that VSCode leaks the LSP diagnostics data field through the
123179
// Diagnostic class, if they ever break this we are out of luck and have to go
124180
// back to the worst diagnostics experience ever:)
@@ -138,22 +194,15 @@ export async function createClient(
138194
.substring(0, index)
139195
.replace(/^ -->[^\n]+\n/m, "");
140196
}
141-
let value;
142-
if (errorCode) {
143-
if (typeof diag.code === "string" || typeof diag.code === "number") {
144-
value = diag.code;
145-
} else {
146-
value = diag.code?.value;
147-
}
148-
}
149197
diag.code = {
150198
target: vscode.Uri.from({
151199
scheme: diagnostics.URI_SCHEME,
152200
path: `/diagnostic message [${idx.toString()}]`,
153201
fragment: uri.toString(),
154202
query: idx.toString(),
155203
}),
156-
value: value ?? "Click for full compiler diagnostic",
204+
value:
205+
errorCode && value ? value : "Click for full compiler diagnostic",
157206
};
158207
}
159208
});

editors/code/src/ctx.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export class Ctx {
8282
private state: PersistentState;
8383
private commandFactories: Record<string, CommandFactory>;
8484
private commandDisposables: Disposable[];
85+
private unlinkedFiles: vscode.Uri[];
8586

8687
get client() {
8788
return this._client;
@@ -94,11 +95,11 @@ export class Ctx {
9495
) {
9596
extCtx.subscriptions.push(this);
9697
this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
97-
this.statusBar.show();
9898
this.workspace = workspace;
9999
this.clientSubscriptions = [];
100100
this.commandDisposables = [];
101101
this.commandFactories = commandFactories;
102+
this.unlinkedFiles = [];
102103

103104
this.state = new PersistentState(extCtx.globalState);
104105
this.config = new Config(extCtx);
@@ -218,7 +219,8 @@ export class Ctx {
218219
this.outputChannel,
219220
initializationOptions,
220221
serverOptions,
221-
this.config
222+
this.config,
223+
this.unlinkedFiles
222224
);
223225
this.pushClientCleanup(
224226
this._client.onNotification(ra.serverStatus, (params) =>
@@ -335,6 +337,7 @@ export class Ctx {
335337
setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
336338
let icon = "";
337339
const statusBar = this.statusBar;
340+
statusBar.show();
338341
statusBar.tooltip = new vscode.MarkdownString("", true);
339342
statusBar.tooltip.isTrusted = true;
340343
switch (status.health) {

0 commit comments

Comments
 (0)