1 //! Book keeping for keeping diagnostics easily in sync with the client.
2 pub(crate) mod to_proto;
4 use std::{collections::HashMap, sync::Arc};
6 use lsp_types::{Diagnostic, Range};
11 pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
13 #[derive(Debug, Default, Clone)]
14 pub struct DiagnosticsConfig {
15 pub warnings_as_info: Vec<String>,
16 pub warnings_as_hint: Vec<String>,
19 #[derive(Debug, Default, Clone)]
20 pub struct DiagnosticCollection {
21 pub native: HashMap<FileId, Vec<Diagnostic>>,
22 pub check: HashMap<FileId, Vec<Diagnostic>>,
23 pub check_fixes: CheckFixes,
26 #[derive(Debug, Clone)]
29 pub action: lsp_ext::CodeAction,
33 pub enum DiagnosticTask {
35 AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>),
36 SetNative(FileId, Vec<Diagnostic>),
39 impl DiagnosticCollection {
40 pub fn clear_check(&mut self) -> Vec<FileId> {
41 Arc::make_mut(&mut self.check_fixes).clear();
42 self.check.drain().map(|(key, _value)| key).collect()
45 pub fn add_check_diagnostic(
48 diagnostic: Diagnostic,
49 fixes: Vec<lsp_ext::CodeAction>,
51 let diagnostics = self.check.entry(file_id).or_default();
52 for existing_diagnostic in diagnostics.iter() {
53 if are_diagnostics_equal(&existing_diagnostic, &diagnostic) {
58 let check_fixes = Arc::make_mut(&mut self.check_fixes);
62 .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action }));
63 diagnostics.push(diagnostic);
66 pub fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec<Diagnostic>) {
67 self.native.insert(file_id, diagnostics);
70 pub fn diagnostics_for(&self, file_id: FileId) -> impl Iterator<Item = &Diagnostic> {
71 let native = self.native.get(&file_id).into_iter().flatten();
72 let check = self.check.get(&file_id).into_iter().flatten();
76 pub fn handle_task(&mut self, task: DiagnosticTask) -> Vec<FileId> {
78 DiagnosticTask::ClearCheck => self.clear_check(),
79 DiagnosticTask::AddCheck(file_id, diagnostic, fixes) => {
80 self.add_check_diagnostic(file_id, diagnostic, fixes);
83 DiagnosticTask::SetNative(file_id, diagnostics) => {
84 self.set_native_diagnostics(file_id, diagnostics);
91 fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool {
92 left.source == right.source
93 && left.severity == right.severity
94 && left.range == right.range
95 && left.message == right.message