Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #18404 from Veykril/veykril/push-swpmkoqqxrvu
feat: Implement diagnostics pull model
Lukas Wirth 2024-10-24
parent 6005446 · parent 8b59541 · commit 58e9871
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs34
-rw-r--r--crates/rust-analyzer/src/handlers/dispatch.rs24
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs66
-rw-r--r--crates/rust-analyzer/src/lsp/capabilities.rs19
-rw-r--r--crates/rust-analyzer/src/main_loop.rs25
5 files changed, 150 insertions, 18 deletions
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index bcdd045d70..22910ee4c6 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -173,21 +173,6 @@ pub(crate) fn fetch_native_diagnostics(
let _p = tracing::info_span!("fetch_native_diagnostics").entered();
let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned());
- let convert_diagnostic =
- |line_index: &crate::line_index::LineIndex, d: ide::Diagnostic| lsp_types::Diagnostic {
- range: lsp::to_proto::range(line_index, d.range.range),
- severity: Some(lsp::to_proto::diagnostic_severity(d.severity)),
- code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_owned())),
- code_description: Some(lsp_types::CodeDescription {
- href: lsp_types::Url::parse(&d.code.url()).unwrap(),
- }),
- source: Some("rust-analyzer".to_owned()),
- message: d.message,
- related_information: None,
- tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]),
- data: None,
- };
-
// the diagnostics produced may point to different files not requested by the concrete request,
// put those into here and filter later
let mut odd_ones = Vec::new();
@@ -248,3 +233,22 @@ pub(crate) fn fetch_native_diagnostics(
}
diagnostics
}
+
+pub(crate) fn convert_diagnostic(
+ line_index: &crate::line_index::LineIndex,
+ d: ide::Diagnostic,
+) -> lsp_types::Diagnostic {
+ lsp_types::Diagnostic {
+ range: lsp::to_proto::range(line_index, d.range.range),
+ severity: Some(lsp::to_proto::diagnostic_severity(d.severity)),
+ code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_owned())),
+ code_description: Some(lsp_types::CodeDescription {
+ href: lsp_types::Url::parse(&d.code.url()).unwrap(),
+ }),
+ source: Some("rust-analyzer".to_owned()),
+ message: d.message,
+ related_information: None,
+ tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]),
+ data: None,
+ }
+}
diff --git a/crates/rust-analyzer/src/handlers/dispatch.rs b/crates/rust-analyzer/src/handlers/dispatch.rs
index ed7bf27843..21f95a945d 100644
--- a/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -121,6 +121,30 @@ impl RequestDispatcher<'_> {
}
/// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
+ /// ready this will return a default constructed [`R::Result`].
+ pub(crate) fn on_or<const ALLOW_RETRYING: bool, R>(
+ &mut self,
+ f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>,
+ default: impl FnOnce() -> R::Result,
+ ) -> &mut Self
+ where
+ R: lsp_types::request::Request<
+ Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+ Result: Serialize,
+ > + 'static,
+ {
+ if !self.global_state.vfs_done {
+ if let Some(lsp_server::Request { id, .. }) =
+ self.req.take_if(|it| it.method == R::METHOD)
+ {
+ self.global_state.respond(lsp_server::Response::new_ok(id, default()));
+ }
+ return self;
+ }
+ self.on_with_thread_intent::<true, ALLOW_RETRYING, R>(ThreadIntent::Worker, f)
+ }
+
+ /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not
/// ready this will return the parameter as is.
pub(crate) fn on_identity<const ALLOW_RETRYING: bool, R, Params>(
&mut self,
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 5b4b678c0c..35c61a336e 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -4,6 +4,7 @@
use std::{
fs,
io::Write as _,
+ ops::Not,
process::{self, Stdio},
};
@@ -14,7 +15,7 @@ use ide::{
FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query,
RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
};
-use ide_db::SymbolKind;
+use ide_db::{FxHashMap, SymbolKind};
use itertools::Itertools;
use lsp_server::ErrorCode;
use lsp_types::{
@@ -36,6 +37,7 @@ use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
use crate::{
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
+ diagnostics::convert_diagnostic,
global_state::{FetchWorkspaceRequest, GlobalState, GlobalStateSnapshot},
hack_recover_crate_name,
line_index::LineEndings,
@@ -473,6 +475,68 @@ pub(crate) fn handle_on_type_formatting(
Ok(Some(change))
}
+pub(crate) fn handle_document_diagnostics(
+ snap: GlobalStateSnapshot,
+ params: lsp_types::DocumentDiagnosticParams,
+) -> anyhow::Result<lsp_types::DocumentDiagnosticReportResult> {
+ let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
+ let source_root = snap.analysis.source_root_id(file_id)?;
+ let line_index = snap.file_line_index(file_id)?;
+ let config = snap.config.diagnostics(Some(source_root));
+ if !config.enabled {
+ return Ok(lsp_types::DocumentDiagnosticReportResult::Report(
+ lsp_types::DocumentDiagnosticReport::Full(
+ lsp_types::RelatedFullDocumentDiagnosticReport {
+ related_documents: None,
+ full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
+ result_id: None,
+ items: vec![],
+ },
+ },
+ ),
+ ));
+ }
+ let supports_related = snap.config.text_document_diagnostic_related_document_support();
+
+ let mut related_documents = FxHashMap::default();
+ let diagnostics = snap
+ .analysis
+ .full_diagnostics(&config, AssistResolveStrategy::None, file_id)?
+ .into_iter()
+ .filter_map(|d| {
+ let file = d.range.file_id;
+ let diagnostic = convert_diagnostic(&line_index, d);
+ if file == file_id {
+ return Some(diagnostic);
+ }
+ if supports_related {
+ related_documents.entry(file).or_insert_with(Vec::new).push(diagnostic);
+ }
+ None
+ });
+ Ok(lsp_types::DocumentDiagnosticReportResult::Report(
+ lsp_types::DocumentDiagnosticReport::Full(lsp_types::RelatedFullDocumentDiagnosticReport {
+ full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
+ result_id: None,
+ items: diagnostics.collect(),
+ },
+ related_documents: related_documents.is_empty().not().then(|| {
+ related_documents
+ .into_iter()
+ .map(|(id, items)| {
+ (
+ to_proto::url(&snap, id),
+ lsp_types::DocumentDiagnosticReportKind::Full(
+ lsp_types::FullDocumentDiagnosticReport { result_id: None, items },
+ ),
+ )
+ })
+ .collect()
+ }),
+ }),
+ ))
+}
+
pub(crate) fn handle_document_symbol(
snap: GlobalStateSnapshot,
params: lsp_types::DocumentSymbolParams,
diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs
index 939593dd45..271a9c0f3d 100644
--- a/crates/rust-analyzer/src/lsp/capabilities.rs
+++ b/crates/rust-analyzer/src/lsp/capabilities.rs
@@ -155,7 +155,15 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
"ssr": true,
"workspaceSymbolScopeKindFiltering": true,
})),
- diagnostic_provider: None,
+ diagnostic_provider: Some(lsp_types::DiagnosticServerCapabilities::Options(
+ lsp_types::DiagnosticOptions {
+ identifier: None,
+ inter_file_dependencies: true,
+ // FIXME
+ workspace_diagnostics: false,
+ work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
+ },
+ )),
inline_completion_provider: None,
}
}
@@ -380,6 +388,15 @@ impl ClientCapabilities {
.unwrap_or_default()
}
+ pub fn text_document_diagnostic(&self) -> bool {
+ (|| -> _ { self.0.text_document.as_ref()?.diagnostic.as_ref() })().is_some()
+ }
+
+ pub fn text_document_diagnostic_related_document_support(&self) -> bool {
+ (|| -> _ { self.0.text_document.as_ref()?.diagnostic.as_ref()?.related_document_support })()
+ == Some(true)
+ }
+
pub fn code_action_group(&self) -> bool {
self.experimental_bool("codeActionGroup")
}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 20be38a9e4..c531cd8d11 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -417,6 +417,8 @@ impl GlobalState {
}
}
+ let supports_diagnostic_pull_model = self.config.text_document_diagnostic();
+
let client_refresh = became_quiescent || state_changed;
if client_refresh {
// Refresh semantic tokens if the client supports it.
@@ -434,11 +436,21 @@ impl GlobalState {
if self.config.inlay_hints_refresh() {
self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ());
}
+
+ if supports_diagnostic_pull_model {
+ self.send_request::<lsp_types::request::WorkspaceDiagnosticRefresh>(
+ (),
+ |_, _| (),
+ );
+ }
}
let project_or_mem_docs_changed =
became_quiescent || state_changed || memdocs_added_or_removed;
- if project_or_mem_docs_changed && self.config.publish_diagnostics(None) {
+ if project_or_mem_docs_changed
+ && !supports_diagnostic_pull_model
+ && self.config.publish_diagnostics(None)
+ {
self.update_diagnostics();
}
if project_or_mem_docs_changed && self.config.test_explorer() {
@@ -1080,6 +1092,17 @@ impl GlobalState {
.on_latency_sensitive::<NO_RETRY, lsp_request::SemanticTokensRangeRequest>(handlers::handle_semantic_tokens_range)
// FIXME: Some of these NO_RETRY could be retries if the file they are interested didn't change.
// All other request handlers
+ .on_or::<NO_RETRY, lsp_request::DocumentDiagnosticRequest>(handlers::handle_document_diagnostics, || lsp_types::DocumentDiagnosticReportResult::Report(
+ lsp_types::DocumentDiagnosticReport::Full(
+ lsp_types::RelatedFullDocumentDiagnosticReport {
+ related_documents: None,
+ full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport {
+ result_id: None,
+ items: vec![],
+ },
+ },
+ ),
+ ))
.on::<RETRY, lsp_request::DocumentSymbolRequest>(handlers::handle_document_symbol)
.on::<RETRY, lsp_request::FoldingRangeRequest>(handlers::handle_folding_range)
.on::<NO_RETRY, lsp_request::SignatureHelpRequest>(handlers::handle_signature_help)