]> git.lizzy.rs Git - rust.git/blob - crates/rust-analyzer/src/diagnostics.rs
Merge #4927
[rust.git] / crates / rust-analyzer / src / diagnostics.rs
1 //! Book keeping for keeping diagnostics easily in sync with the client.
2 pub(crate) mod to_proto;
3
4 use std::{collections::HashMap, sync::Arc};
5
6 use lsp_types::{Diagnostic, Range};
7 use ra_ide::FileId;
8
9 use crate::lsp_ext;
10
11 pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
12
13 #[derive(Debug, Default, Clone)]
14 pub struct DiagnosticsConfig {
15     pub warnings_as_info: Vec<String>,
16     pub warnings_as_hint: Vec<String>,
17 }
18
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,
24 }
25
26 #[derive(Debug, Clone)]
27 pub struct Fix {
28     pub range: Range,
29     pub action: lsp_ext::CodeAction,
30 }
31
32 #[derive(Debug)]
33 pub enum DiagnosticTask {
34     ClearCheck,
35     AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>),
36     SetNative(FileId, Vec<Diagnostic>),
37 }
38
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()
43     }
44
45     pub fn add_check_diagnostic(
46         &mut self,
47         file_id: FileId,
48         diagnostic: Diagnostic,
49         fixes: Vec<lsp_ext::CodeAction>,
50     ) {
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) {
54                 return;
55             }
56         }
57
58         let check_fixes = Arc::make_mut(&mut self.check_fixes);
59         check_fixes
60             .entry(file_id)
61             .or_default()
62             .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action }));
63         diagnostics.push(diagnostic);
64     }
65
66     pub fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec<Diagnostic>) {
67         self.native.insert(file_id, diagnostics);
68     }
69
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();
73         native.chain(check)
74     }
75
76     pub fn handle_task(&mut self, task: DiagnosticTask) -> Vec<FileId> {
77         match task {
78             DiagnosticTask::ClearCheck => self.clear_check(),
79             DiagnosticTask::AddCheck(file_id, diagnostic, fixes) => {
80                 self.add_check_diagnostic(file_id, diagnostic, fixes);
81                 vec![file_id]
82             }
83             DiagnosticTask::SetNative(file_id, diagnostics) => {
84                 self.set_native_diagnostics(file_id, diagnostics);
85                 vec![file_id]
86             }
87         }
88     }
89 }
90
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
96 }