feat: Pop a notification prompting the user to add a Cargo.toml of unlinked file to the linkedProjects

This commit is contained in:
Lukas Wirth 2023-03-16 16:26:19 +01:00
parent 8330f8efc6
commit 66636939a6
6 changed files with 78 additions and 14 deletions

View file

@ -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);

View file

@ -331,7 +331,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);
@ -372,7 +372,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()
}
}

View file

@ -112,7 +112,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() {

View file

@ -444,6 +444,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.",

View file

@ -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,65 @@ export async function createClient(
const preview = config.previewRustcOutput;
const errorCode = config.useRustcErrorCode;
diagnosticList.forEach((diag, idx) => {
let value =
typeof diag.code === "string" || typeof diag.code === "number"
? diag.code
: diag.code?.value;
if (value === "unlinked-file" && !unlinkedFiles.includes(uri)) {
let config = vscode.workspace.getConfiguration("rust-analyzer");
if (config.get("showUnlinkedFileNotification")) {
unlinkedFiles.push(uri);
let folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath;
if (folder) {
let parent_backslash = uri.fsPath.lastIndexOf(
pathSeparator + "src"
);
let parent = uri.fsPath.substring(0, parent_backslash);
if (parent.startsWith(folder)) {
let path = vscode.Uri.file(
parent + pathSeparator + "Cargo.toml"
);
void vscode.workspace.fs.stat(path).then(() => {
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"
)
.then((choice) => {
switch (choice) {
case "Yes":
break;
case "No":
config.update(
"linkedProjects",
config
.get<any[]>("linkedProjects")
?.concat(
path.fsPath.substring(
folder!.length
)
),
false
);
break;
case "Don't show this again":
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 +199,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 +206,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",
};
}
});

View file

@ -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;
@ -99,6 +100,7 @@ export class Ctx {
this.clientSubscriptions = [];
this.commandDisposables = [];
this.commandFactories = commandFactories;
this.unlinkedFiles = [];
this.state = new PersistentState(extCtx.globalState);
this.config = new Config(extCtx);
@ -218,7 +220,8 @@ export class Ctx {
this.outputChannel,
initializationOptions,
serverOptions,
this.config
this.config,
this.unlinkedFiles
);
this.pushClientCleanup(
this._client.onNotification(ra.serverStatus, (params) =>