Be more lenient when dealing with rust-analyzer's flycheck commands (#36782)
Flycheck commands are global and makes sense to fall back to looking up project's rust-analyzer even if the commands are run on a non-rust buffer. If multiple rust-analyzers are found in the project, avoid ambiguous commands and bail (as before). Closes #ISSUE Release Notes: - Made it possible to run rust-analyzer's flycheck actions from anywhere in the project
This commit is contained in:
parent
153724aad3
commit
d24cad30f3
5 changed files with 114 additions and 64 deletions
|
@ -438,7 +438,7 @@ impl ProjectDiagnosticsEditor {
|
||||||
for buffer_path in diagnostics_sources.iter().cloned() {
|
for buffer_path in diagnostics_sources.iter().cloned() {
|
||||||
if cx
|
if cx
|
||||||
.update(|cx| {
|
.update(|cx| {
|
||||||
fetch_tasks.push(run_flycheck(project.clone(), buffer_path, cx));
|
fetch_tasks.push(run_flycheck(project.clone(), Some(buffer_path), cx));
|
||||||
})
|
})
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
|
@ -462,7 +462,7 @@ impl ProjectDiagnosticsEditor {
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
cancel_gasks.push(cancel_flycheck(self.project.clone(), buffer_path, cx));
|
cancel_gasks.push(cancel_flycheck(self.project.clone(), Some(buffer_path), cx));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cargo_diagnostics_fetch.cancel_task = Some(cx.background_spawn(async move {
|
self.cargo_diagnostics_fetch.cancel_task = Some(cx.background_spawn(async move {
|
||||||
|
|
|
@ -26,6 +26,17 @@ fn is_rust_language(language: &Language) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_related_actions(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) {
|
pub fn apply_related_actions(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) {
|
||||||
|
if editor.read(cx).project().is_some_and(|project| {
|
||||||
|
project
|
||||||
|
.read(cx)
|
||||||
|
.language_server_statuses(cx)
|
||||||
|
.any(|(_, status)| status.name == RUST_ANALYZER_NAME)
|
||||||
|
}) {
|
||||||
|
register_action(editor, window, cancel_flycheck_action);
|
||||||
|
register_action(editor, window, run_flycheck_action);
|
||||||
|
register_action(editor, window, clear_flycheck_action);
|
||||||
|
}
|
||||||
|
|
||||||
if editor
|
if editor
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.buffer()
|
.buffer()
|
||||||
|
@ -38,9 +49,6 @@ pub fn apply_related_actions(editor: &Entity<Editor>, window: &mut Window, cx: &
|
||||||
register_action(editor, window, go_to_parent_module);
|
register_action(editor, window, go_to_parent_module);
|
||||||
register_action(editor, window, expand_macro_recursively);
|
register_action(editor, window, expand_macro_recursively);
|
||||||
register_action(editor, window, open_docs);
|
register_action(editor, window, open_docs);
|
||||||
register_action(editor, window, cancel_flycheck_action);
|
|
||||||
register_action(editor, window, run_flycheck_action);
|
|
||||||
register_action(editor, window, clear_flycheck_action);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,7 +317,7 @@ fn cancel_flycheck_action(
|
||||||
let Some(project) = &editor.project else {
|
let Some(project) = &editor.project else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(buffer_id) = editor
|
let buffer_id = editor
|
||||||
.selections
|
.selections
|
||||||
.disjoint_anchors()
|
.disjoint_anchors()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -321,10 +329,7 @@ fn cancel_flycheck_action(
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.entry_id(cx)?;
|
.entry_id(cx)?;
|
||||||
project.path_for_entry(entry_id, cx)
|
project.path_for_entry(entry_id, cx)
|
||||||
})
|
});
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
cancel_flycheck(project.clone(), buffer_id, cx).detach_and_log_err(cx);
|
cancel_flycheck(project.clone(), buffer_id, cx).detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +342,7 @@ fn run_flycheck_action(
|
||||||
let Some(project) = &editor.project else {
|
let Some(project) = &editor.project else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(buffer_id) = editor
|
let buffer_id = editor
|
||||||
.selections
|
.selections
|
||||||
.disjoint_anchors()
|
.disjoint_anchors()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -349,10 +354,7 @@ fn run_flycheck_action(
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.entry_id(cx)?;
|
.entry_id(cx)?;
|
||||||
project.path_for_entry(entry_id, cx)
|
project.path_for_entry(entry_id, cx)
|
||||||
})
|
});
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
run_flycheck(project.clone(), buffer_id, cx).detach_and_log_err(cx);
|
run_flycheck(project.clone(), buffer_id, cx).detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +367,7 @@ fn clear_flycheck_action(
|
||||||
let Some(project) = &editor.project else {
|
let Some(project) = &editor.project else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(buffer_id) = editor
|
let buffer_id = editor
|
||||||
.selections
|
.selections
|
||||||
.disjoint_anchors()
|
.disjoint_anchors()
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -377,9 +379,6 @@ fn clear_flycheck_action(
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.entry_id(cx)?;
|
.entry_id(cx)?;
|
||||||
project.path_for_entry(entry_id, cx)
|
project.path_for_entry(entry_id, cx)
|
||||||
})
|
});
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
clear_flycheck(project.clone(), buffer_id, cx).detach_and_log_err(cx);
|
clear_flycheck(project.clone(), buffer_id, cx).detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9029,13 +9029,22 @@ impl LspStore {
|
||||||
lsp_store.update(&mut cx, |lsp_store, cx| {
|
lsp_store.update(&mut cx, |lsp_store, cx| {
|
||||||
if let Some(server) = lsp_store.language_server_for_id(server_id) {
|
if let Some(server) = lsp_store.language_server_for_id(server_id) {
|
||||||
let text_document = if envelope.payload.current_file_only {
|
let text_document = if envelope.payload.current_file_only {
|
||||||
let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
|
let buffer_id = envelope
|
||||||
lsp_store
|
.payload
|
||||||
.buffer_store()
|
.buffer_id
|
||||||
.read(cx)
|
.map(|id| BufferId::new(id))
|
||||||
.get(buffer_id)
|
.transpose()?;
|
||||||
.and_then(|buffer| Some(buffer.read(cx).file()?.as_local()?.abs_path(cx)))
|
buffer_id
|
||||||
.map(|path| make_text_document_identifier(&path))
|
.and_then(|buffer_id| {
|
||||||
|
lsp_store
|
||||||
|
.buffer_store()
|
||||||
|
.read(cx)
|
||||||
|
.get(buffer_id)
|
||||||
|
.and_then(|buffer| {
|
||||||
|
Some(buffer.read(cx).file()?.as_local()?.abs_path(cx))
|
||||||
|
})
|
||||||
|
.map(|path| make_text_document_identifier(&path))
|
||||||
|
})
|
||||||
.transpose()?
|
.transpose()?
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use ::serde::{Deserialize, Serialize};
|
use ::serde::{Deserialize, Serialize};
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use gpui::{App, Entity, Task, WeakEntity};
|
use gpui::{App, AsyncApp, Entity, Task, WeakEntity};
|
||||||
use language::ServerHealth;
|
use language::{Buffer, ServerHealth};
|
||||||
use lsp::{LanguageServer, LanguageServerName};
|
use lsp::{LanguageServer, LanguageServerId, LanguageServerName};
|
||||||
use rpc::proto;
|
use rpc::proto;
|
||||||
|
|
||||||
use crate::{LspStore, LspStoreEvent, Project, ProjectPath, lsp_store};
|
use crate::{LspStore, LspStoreEvent, Project, ProjectPath, lsp_store};
|
||||||
|
@ -83,31 +83,32 @@ pub fn register_notifications(lsp_store: WeakEntity<LspStore>, language_server:
|
||||||
|
|
||||||
pub fn cancel_flycheck(
|
pub fn cancel_flycheck(
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
buffer_path: ProjectPath,
|
buffer_path: Option<ProjectPath>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<anyhow::Result<()>> {
|
) -> Task<anyhow::Result<()>> {
|
||||||
let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
|
let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
|
||||||
let lsp_store = project.read(cx).lsp_store();
|
let lsp_store = project.read(cx).lsp_store();
|
||||||
let buffer = project.update(cx, |project, cx| {
|
let buffer = buffer_path.map(|buffer_path| {
|
||||||
project.buffer_store().update(cx, |buffer_store, cx| {
|
project.update(cx, |project, cx| {
|
||||||
buffer_store.open_buffer(buffer_path, cx)
|
project.buffer_store().update(cx, |buffer_store, cx| {
|
||||||
|
buffer_store.open_buffer(buffer_path, cx)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.spawn(async move |cx| {
|
cx.spawn(async move |cx| {
|
||||||
let buffer = buffer.await?;
|
let buffer = match buffer {
|
||||||
let Some(rust_analyzer_server) = project.read_with(cx, |project, cx| {
|
Some(buffer) => Some(buffer.await?),
|
||||||
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
|
None => None,
|
||||||
})?
|
};
|
||||||
|
let Some(rust_analyzer_server) = find_rust_analyzer_server(&project, buffer.as_ref(), cx)
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let buffer_id = buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto())?;
|
|
||||||
|
|
||||||
if let Some((client, project_id)) = upstream_client {
|
if let Some((client, project_id)) = upstream_client {
|
||||||
let request = proto::LspExtCancelFlycheck {
|
let request = proto::LspExtCancelFlycheck {
|
||||||
project_id,
|
project_id,
|
||||||
buffer_id,
|
|
||||||
language_server_id: rust_analyzer_server.to_proto(),
|
language_server_id: rust_analyzer_server.to_proto(),
|
||||||
};
|
};
|
||||||
client
|
client
|
||||||
|
@ -130,28 +131,33 @@ pub fn cancel_flycheck(
|
||||||
|
|
||||||
pub fn run_flycheck(
|
pub fn run_flycheck(
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
buffer_path: ProjectPath,
|
buffer_path: Option<ProjectPath>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<anyhow::Result<()>> {
|
) -> Task<anyhow::Result<()>> {
|
||||||
let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
|
let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
|
||||||
let lsp_store = project.read(cx).lsp_store();
|
let lsp_store = project.read(cx).lsp_store();
|
||||||
let buffer = project.update(cx, |project, cx| {
|
let buffer = buffer_path.map(|buffer_path| {
|
||||||
project.buffer_store().update(cx, |buffer_store, cx| {
|
project.update(cx, |project, cx| {
|
||||||
buffer_store.open_buffer(buffer_path, cx)
|
project.buffer_store().update(cx, |buffer_store, cx| {
|
||||||
|
buffer_store.open_buffer(buffer_path, cx)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.spawn(async move |cx| {
|
cx.spawn(async move |cx| {
|
||||||
let buffer = buffer.await?;
|
let buffer = match buffer {
|
||||||
let Some(rust_analyzer_server) = project.read_with(cx, |project, cx| {
|
Some(buffer) => Some(buffer.await?),
|
||||||
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
|
None => None,
|
||||||
})?
|
};
|
||||||
|
let Some(rust_analyzer_server) = find_rust_analyzer_server(&project, buffer.as_ref(), cx)
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let buffer_id = buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto())?;
|
|
||||||
|
|
||||||
if let Some((client, project_id)) = upstream_client {
|
if let Some((client, project_id)) = upstream_client {
|
||||||
|
let buffer_id = buffer
|
||||||
|
.map(|buffer| buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto()))
|
||||||
|
.transpose()?;
|
||||||
let request = proto::LspExtRunFlycheck {
|
let request = proto::LspExtRunFlycheck {
|
||||||
project_id,
|
project_id,
|
||||||
buffer_id,
|
buffer_id,
|
||||||
|
@ -182,31 +188,32 @@ pub fn run_flycheck(
|
||||||
|
|
||||||
pub fn clear_flycheck(
|
pub fn clear_flycheck(
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
buffer_path: ProjectPath,
|
buffer_path: Option<ProjectPath>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<anyhow::Result<()>> {
|
) -> Task<anyhow::Result<()>> {
|
||||||
let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
|
let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
|
||||||
let lsp_store = project.read(cx).lsp_store();
|
let lsp_store = project.read(cx).lsp_store();
|
||||||
let buffer = project.update(cx, |project, cx| {
|
let buffer = buffer_path.map(|buffer_path| {
|
||||||
project.buffer_store().update(cx, |buffer_store, cx| {
|
project.update(cx, |project, cx| {
|
||||||
buffer_store.open_buffer(buffer_path, cx)
|
project.buffer_store().update(cx, |buffer_store, cx| {
|
||||||
|
buffer_store.open_buffer(buffer_path, cx)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.spawn(async move |cx| {
|
cx.spawn(async move |cx| {
|
||||||
let buffer = buffer.await?;
|
let buffer = match buffer {
|
||||||
let Some(rust_analyzer_server) = project.read_with(cx, |project, cx| {
|
Some(buffer) => Some(buffer.await?),
|
||||||
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
|
None => None,
|
||||||
})?
|
};
|
||||||
|
let Some(rust_analyzer_server) = find_rust_analyzer_server(&project, buffer.as_ref(), cx)
|
||||||
else {
|
else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let buffer_id = buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto())?;
|
|
||||||
|
|
||||||
if let Some((client, project_id)) = upstream_client {
|
if let Some((client, project_id)) = upstream_client {
|
||||||
let request = proto::LspExtClearFlycheck {
|
let request = proto::LspExtClearFlycheck {
|
||||||
project_id,
|
project_id,
|
||||||
buffer_id,
|
|
||||||
language_server_id: rust_analyzer_server.to_proto(),
|
language_server_id: rust_analyzer_server.to_proto(),
|
||||||
};
|
};
|
||||||
client
|
client
|
||||||
|
@ -226,3 +233,40 @@ pub fn clear_flycheck(
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_rust_analyzer_server(
|
||||||
|
project: &Entity<Project>,
|
||||||
|
buffer: Option<&Entity<Buffer>>,
|
||||||
|
cx: &mut AsyncApp,
|
||||||
|
) -> Option<LanguageServerId> {
|
||||||
|
project
|
||||||
|
.read_with(cx, |project, cx| {
|
||||||
|
buffer
|
||||||
|
.and_then(|buffer| {
|
||||||
|
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
|
||||||
|
})
|
||||||
|
// If no rust-analyzer found for the current buffer (e.g. `settings.json`), fall back to the project lookup
|
||||||
|
// and use project's rust-analyzer if it's the only one.
|
||||||
|
.or_else(|| {
|
||||||
|
let rust_analyzer_servers = project
|
||||||
|
.lsp_store()
|
||||||
|
.read(cx)
|
||||||
|
.language_server_statuses
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(server_id, server_status)| {
|
||||||
|
if server_status.name == RUST_ANALYZER_NAME {
|
||||||
|
Some(*server_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if rust_analyzer_servers.len() == 1 {
|
||||||
|
rust_analyzer_servers.first().copied()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.ok()?
|
||||||
|
}
|
||||||
|
|
|
@ -834,21 +834,19 @@ message LspRunnable {
|
||||||
|
|
||||||
message LspExtCancelFlycheck {
|
message LspExtCancelFlycheck {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
uint64 buffer_id = 2;
|
uint64 language_server_id = 2;
|
||||||
uint64 language_server_id = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message LspExtRunFlycheck {
|
message LspExtRunFlycheck {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
uint64 buffer_id = 2;
|
optional uint64 buffer_id = 2;
|
||||||
uint64 language_server_id = 3;
|
uint64 language_server_id = 3;
|
||||||
bool current_file_only = 4;
|
bool current_file_only = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LspExtClearFlycheck {
|
message LspExtClearFlycheck {
|
||||||
uint64 project_id = 1;
|
uint64 project_id = 1;
|
||||||
uint64 buffer_id = 2;
|
uint64 language_server_id = 2;
|
||||||
uint64 language_server_id = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message LspDiagnosticRelatedInformation {
|
message LspDiagnosticRelatedInformation {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue