diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 5f2871ac9922..eab07589f56d 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(); @@ -246,3 +231,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/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 5b4b678c0c6e..5805377474e2 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -2,8 +2,10 @@ //! Protocol. This module specifically handles requests. use std::{ + collections::HashMap, fs, io::Write as _, + ops::Not, process::{self, Stdio}, }; @@ -36,6 +38,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 +476,49 @@ pub(crate) fn handle_on_type_formatting( Ok(Some(change)) } +pub(crate) fn handle_document_diagnostics( + snap: GlobalStateSnapshot, + params: lsp_types::DocumentDiagnosticParams, +) -> anyhow::Result { + let file_id = from_proto::file_id(&snap, ¶ms.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(Default::default()); + } + let supports_related = snap.config.text_document_diagnostic().unwrap_or(false); + + let mut related_documents = HashMap::new(); + 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 + }); + // FIXME: flycheck diagnostics + Ok(lsp_types::DocumentDiagnosticReportResult::Report( + lsp_types::DocumentDiagnosticReport::Full(lsp_types::RelatedFullDocumentDiagnosticReport { + related_documents: related_documents.is_empty().not().then(|| { + related_documents.into_iter().map(|(id, d)| (to_proto::url(&snap, id), d)).collect() + }), + full_document_diagnostic_report: lsp_types::FullDocumentDiagnosticReport { + result_id: None, + items: diagnostics.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 939593dd4533..baabe6343aec 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,10 @@ impl ClientCapabilities { .unwrap_or_default() } + pub fn text_document_diagnostic(&self) -> Option { + (|| -> _ { self.0.text_document.as_ref()?.diagnostic.as_ref()?.related_document_support })() + } + 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 20be38a9e4be..13588af44ea1 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -438,7 +438,10 @@ impl GlobalState { 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 + && self.config.text_document_diagnostic().is_none() + && self.config.publish_diagnostics(None) + { self.update_diagnostics(); } if project_or_mem_docs_changed && self.config.test_explorer() { @@ -1080,6 +1083,7 @@ impl GlobalState { .on_latency_sensitive::(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::(handlers::handle_document_diagnostics) .on::(handlers::handle_document_symbol) .on::(handlers::handle_folding_range) .on::(handlers::handle_signature_help)