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
This commit is contained in:
commit
5bba438c9c
@ -74,6 +74,7 @@ mod handlers {
|
|||||||
};
|
};
|
||||||
use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange};
|
use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange};
|
||||||
|
|
||||||
|
// FIXME: Make this an enum
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct DiagnosticCode(pub &'static str);
|
pub struct DiagnosticCode(pub &'static str);
|
||||||
|
|
||||||
|
@ -337,7 +337,7 @@ pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
|
pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
|
||||||
&mut self,
|
&self,
|
||||||
params: N::Params,
|
params: N::Params,
|
||||||
) {
|
) {
|
||||||
let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
|
let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
|
||||||
@ -378,7 +378,7 @@ pub(crate) fn is_completed(&self, request: &lsp_server::Request) -> bool {
|
|||||||
self.req_queue.incoming.is_completed(&request.id)
|
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()
|
self.sender.send(message).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,8 @@ pub(crate) fn current_status(&self) -> lsp_ext::ServerStatusParams {
|
|||||||
&& self.config.notifications().cargo_toml_not_found
|
&& self.config.notifications().cargo_toml_not_found
|
||||||
{
|
{
|
||||||
status.health = lsp_ext::Health::Warning;
|
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() {
|
for ws in self.workspaces.iter() {
|
||||||
|
@ -449,6 +449,11 @@
|
|||||||
"type": "string"
|
"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": {},
|
"$generated-start": {},
|
||||||
"rust-analyzer.assist.emitMustUse": {
|
"rust-analyzer.assist.emitMustUse": {
|
||||||
"markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.",
|
"markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.",
|
||||||
|
@ -8,6 +8,7 @@ import * as diagnostics from "./diagnostics";
|
|||||||
import { WorkspaceEdit } from "vscode";
|
import { WorkspaceEdit } from "vscode";
|
||||||
import { Config, prepareVSCodeConfig } from "./config";
|
import { Config, prepareVSCodeConfig } from "./config";
|
||||||
import { randomUUID } from "crypto";
|
import { randomUUID } from "crypto";
|
||||||
|
import { sep as pathSeparator } from "path";
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
[name: string]: string;
|
[name: string]: string;
|
||||||
@ -69,7 +70,8 @@ export async function createClient(
|
|||||||
outputChannel: vscode.OutputChannel,
|
outputChannel: vscode.OutputChannel,
|
||||||
initializationOptions: vscode.WorkspaceConfiguration,
|
initializationOptions: vscode.WorkspaceConfiguration,
|
||||||
serverOptions: lc.ServerOptions,
|
serverOptions: lc.ServerOptions,
|
||||||
config: Config
|
config: Config,
|
||||||
|
unlinkedFiles: vscode.Uri[]
|
||||||
): Promise<lc.LanguageClient> {
|
): Promise<lc.LanguageClient> {
|
||||||
const clientOptions: lc.LanguageClientOptions = {
|
const clientOptions: lc.LanguageClientOptions = {
|
||||||
documentSelector: [{ scheme: "file", language: "rust" }],
|
documentSelector: [{ scheme: "file", language: "rust" }],
|
||||||
@ -119,6 +121,60 @@ export async function createClient(
|
|||||||
const preview = config.previewRustcOutput;
|
const preview = config.previewRustcOutput;
|
||||||
const errorCode = config.useRustcErrorCode;
|
const errorCode = config.useRustcErrorCode;
|
||||||
diagnosticList.forEach((diag, idx) => {
|
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
|
// 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
|
// Diagnostic class, if they ever break this we are out of luck and have to go
|
||||||
// back to the worst diagnostics experience ever:)
|
// back to the worst diagnostics experience ever:)
|
||||||
@ -138,14 +194,6 @@ export async function createClient(
|
|||||||
.substring(0, index)
|
.substring(0, index)
|
||||||
.replace(/^ -->[^\n]+\n/m, "");
|
.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 = {
|
diag.code = {
|
||||||
target: vscode.Uri.from({
|
target: vscode.Uri.from({
|
||||||
scheme: diagnostics.URI_SCHEME,
|
scheme: diagnostics.URI_SCHEME,
|
||||||
@ -153,7 +201,8 @@ export async function createClient(
|
|||||||
fragment: uri.toString(),
|
fragment: uri.toString(),
|
||||||
query: idx.toString(),
|
query: idx.toString(),
|
||||||
}),
|
}),
|
||||||
value: value ?? "Click for full compiler diagnostic",
|
value:
|
||||||
|
errorCode && value ? value : "Click for full compiler diagnostic",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -82,6 +82,7 @@ export class Ctx {
|
|||||||
private state: PersistentState;
|
private state: PersistentState;
|
||||||
private commandFactories: Record<string, CommandFactory>;
|
private commandFactories: Record<string, CommandFactory>;
|
||||||
private commandDisposables: Disposable[];
|
private commandDisposables: Disposable[];
|
||||||
|
private unlinkedFiles: vscode.Uri[];
|
||||||
|
|
||||||
get client() {
|
get client() {
|
||||||
return this._client;
|
return this._client;
|
||||||
@ -94,11 +95,11 @@ export class Ctx {
|
|||||||
) {
|
) {
|
||||||
extCtx.subscriptions.push(this);
|
extCtx.subscriptions.push(this);
|
||||||
this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
|
this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
|
||||||
this.statusBar.show();
|
|
||||||
this.workspace = workspace;
|
this.workspace = workspace;
|
||||||
this.clientSubscriptions = [];
|
this.clientSubscriptions = [];
|
||||||
this.commandDisposables = [];
|
this.commandDisposables = [];
|
||||||
this.commandFactories = commandFactories;
|
this.commandFactories = commandFactories;
|
||||||
|
this.unlinkedFiles = [];
|
||||||
|
|
||||||
this.state = new PersistentState(extCtx.globalState);
|
this.state = new PersistentState(extCtx.globalState);
|
||||||
this.config = new Config(extCtx);
|
this.config = new Config(extCtx);
|
||||||
@ -218,7 +219,8 @@ export class Ctx {
|
|||||||
this.outputChannel,
|
this.outputChannel,
|
||||||
initializationOptions,
|
initializationOptions,
|
||||||
serverOptions,
|
serverOptions,
|
||||||
this.config
|
this.config,
|
||||||
|
this.unlinkedFiles
|
||||||
);
|
);
|
||||||
this.pushClientCleanup(
|
this.pushClientCleanup(
|
||||||
this._client.onNotification(ra.serverStatus, (params) =>
|
this._client.onNotification(ra.serverStatus, (params) =>
|
||||||
@ -335,6 +337,7 @@ export class Ctx {
|
|||||||
setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
|
setServerStatus(status: ServerStatusParams | { health: "stopped" }) {
|
||||||
let icon = "";
|
let icon = "";
|
||||||
const statusBar = this.statusBar;
|
const statusBar = this.statusBar;
|
||||||
|
statusBar.show();
|
||||||
statusBar.tooltip = new vscode.MarkdownString("", true);
|
statusBar.tooltip = new vscode.MarkdownString("", true);
|
||||||
statusBar.tooltip.isTrusted = true;
|
statusBar.tooltip.isTrusted = true;
|
||||||
switch (status.health) {
|
switch (status.health) {
|
||||||
|
Loading…
Reference in New Issue
Block a user