Skip to content

Commit a1c0e1c

Browse files
mmahroussfda-odoo
authored andcommitted
[IMP] server: diagnostic filters configuration
1 parent 909524b commit a1c0e1c

File tree

2 files changed

+139
-27
lines changed

2 files changed

+139
-27
lines changed

server/src/core/config.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::path::PathBuf;
55
use std::str::FromStr;
66
use std::{fs, path::Path};
77
use itertools::Itertools;
8+
use glob::Pattern;
89
use regex::Regex;
910
use ruff_python_ast::{Expr, Mod};
1011
use ruff_python_parser::{Mode, ParseOptions};
@@ -89,7 +90,72 @@ impl Default for MergeMethod {
8990
}
9091
}
9192

93+
#[derive(Debug, Clone)]
94+
pub struct DiagnosticFilter {
95+
pub paths: Pattern,
96+
pub codes: Vec<Regex>,
97+
pub types: Vec<DiagnosticSetting>,
98+
pub negation: bool,
99+
}
92100

101+
impl<'de> serde::Deserialize<'de> for DiagnosticFilter {
102+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
103+
where
104+
D: serde::Deserializer<'de>,
105+
{
106+
#[derive(serde::Deserialize)]
107+
struct Helper {
108+
path: String,
109+
#[serde(default)]
110+
codes: Vec<String>,
111+
#[serde(default)]
112+
types: Vec<DiagnosticSetting>,
113+
}
114+
let helper = Helper::deserialize(deserializer)?;
115+
let (path_str, negation) = if let Some(stripped) = helper.path.strip_prefix('!') {
116+
(stripped, true)
117+
} else {
118+
(helper.path.as_str(), false)
119+
};
120+
let sanitized_path = PathBuf::from(path_str).sanitize();
121+
let path_pattern = Pattern::new(&sanitized_path).map_err(serde::de::Error::custom)?;
122+
let mut code_regexes = Vec::with_capacity(helper.codes.len());
123+
for code in &helper.codes {
124+
let regex = Regex::new(code).map_err(serde::de::Error::custom)?;
125+
code_regexes.push(regex);
126+
}
127+
for t in &helper.types {
128+
if let DiagnosticSetting::Disabled = t {
129+
return Err(serde::de::Error::custom("DiagnosticFilter.types cannot contain 'Disabled'"));
130+
}
131+
}
132+
Ok(DiagnosticFilter {
133+
paths: path_pattern,
134+
codes: code_regexes,
135+
types: helper.types,
136+
negation,
137+
})
138+
}
139+
}
140+
141+
impl Serialize for DiagnosticFilter {
142+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
143+
where
144+
S: serde::Serializer,
145+
{
146+
use serde::ser::SerializeStruct;
147+
let mut s = serializer.serialize_struct("DiagnosticFilter", 3)?;
148+
let mut path_str = self.paths.as_str().to_string();
149+
if self.negation {
150+
path_str = format!("!{}", path_str);
151+
}
152+
s.serialize_field("path", &path_str)?;
153+
let codes: Vec<String> = self.codes.iter().map(|r| r.as_str().to_string()).collect();
154+
s.serialize_field("codes", &codes)?;
155+
s.serialize_field("types", &self.types)?;
156+
s.end()
157+
}
158+
}
93159
#[derive(Debug, Deserialize, Clone, Serialize)]
94160
pub struct ConfigFile {
95161
#[serde(default)]
@@ -239,7 +305,7 @@ impl ConfigFile {
239305
"python_path", "additional_stubs", "additional_stubs_merge",
240306
"refresh_mode", "file_cache", "diag_missing_imports",
241307
"ac_filter_model_names", "auto_refresh_delay", "add_workspace_addon_path",
242-
"diagnostic_settings"
308+
"diagnostic_settings", "diagnostic_filters"
243309
];
244310
for key in order {
245311
if let Some(val) = map.get(key) {
@@ -525,6 +591,9 @@ pub struct ConfigEntryRaw {
525591
#[serde(default)]
526592
diagnostic_settings: HashMap<DiagnosticCode, Sourced<DiagnosticSetting>>,
527593

594+
#[serde(default)]
595+
pub diagnostic_filters: Vec<Sourced<DiagnosticFilter>>,
596+
528597
#[serde(skip_deserializing, rename(serialize = "abstract"))]
529598
abstract_: bool
530599
}
@@ -550,6 +619,7 @@ impl Default for ConfigEntryRaw {
550619
base: None,
551620
diagnostic_settings: Default::default(),
552621
abstract_: false,
622+
diagnostic_filters: vec![],
553623
}
554624
}
555625
}
@@ -593,6 +663,7 @@ pub struct ConfigEntry {
593663
pub no_typeshed: bool,
594664
pub abstract_: bool,
595665
pub diagnostic_settings: HashMap<DiagnosticCode, DiagnosticSetting>,
666+
pub diagnostic_filters: Vec<DiagnosticFilter>,
596667
}
597668

598669
impl Default for ConfigEntry {
@@ -612,6 +683,7 @@ impl Default for ConfigEntry {
612683
no_typeshed: false,
613684
abstract_: false,
614685
diagnostic_settings: Default::default(),
686+
diagnostic_filters: vec![],
615687
}
616688
}
617689
}
@@ -724,6 +796,9 @@ fn read_config_from_file<P: AsRef<Path>>(path: P) -> Result<HashMap<String, Conf
724796
entry.diagnostic_settings.values_mut().for_each(|sourced| {
725797
sourced.sources.insert(path.sanitize());
726798
});
799+
entry.diagnostic_filters.iter_mut().for_each(|filter| {
800+
filter.sources.insert(path.sanitize());
801+
});
727802

728803
(entry.name.clone(), entry)
729804
}).collect();
@@ -799,6 +874,7 @@ fn apply_merge(child: &ConfigEntryRaw, parent: &ConfigEntryRaw) -> ConfigEntryRa
799874
let version = child.version.clone().or(parent.version.clone());
800875
let base = child.base.clone().or(parent.base.clone());
801876
let diagnostic_settings = merge_sourced_diagnostic_setting_map(&child.diagnostic_settings, &parent.diagnostic_settings);
877+
let diagnostic_filters = child.diagnostic_filters.iter().chain(parent.diagnostic_filters.iter()).cloned().collect::<Vec<_>>();
802878

803879
ConfigEntryRaw {
804880
odoo_path,
@@ -818,6 +894,7 @@ fn apply_merge(child: &ConfigEntryRaw, parent: &ConfigEntryRaw) -> ConfigEntryRa
818894
version,
819895
base,
820896
diagnostic_settings: diagnostic_settings,
897+
diagnostic_filters: diagnostic_filters,
821898
..Default::default()
822899
}
823900
}
@@ -1150,6 +1227,7 @@ fn merge_all_workspaces(
11501227
&raw_entry.diagnostic_settings,
11511228
);
11521229
merged_entry.abstract_ = merged_entry.abstract_ || raw_entry.abstract_;
1230+
merged_entry.diagnostic_filters.extend(raw_entry.diagnostic_filters.iter().cloned());
11531231
}
11541232
}
11551233
// Only infer odoo_path from workspace folders at this stage, to give priority to the user-defined one
@@ -1190,6 +1268,7 @@ fn merge_all_workspaces(
11901268
diagnostic_settings: raw_entry.diagnostic_settings.into_iter()
11911269
.map(|(k, v)| (k, v.value))
11921270
.collect(),
1271+
diagnostic_filters: raw_entry.diagnostic_filters.into_iter().map(|f| f.value).collect(),
11931272
..Default::default()
11941273
},
11951274
);

server/src/core/file_mgr.rs

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ use lsp_types::notification::{Notification, PublishDiagnostics};
22
use ropey::Rope;
33
use ruff_python_ast::Mod;
44
use ruff_python_parser::{Mode, ParseOptions, Parsed, Token, TokenKind};
5-
use lsp_types::{Diagnostic, MessageType, NumberOrString, Position, PublishDiagnosticsParams, Range, TextDocumentContentChangeEvent};
5+
use lsp_types::{Diagnostic, DiagnosticSeverity, MessageType, NumberOrString, Position, PublishDiagnosticsParams, Range, TextDocumentContentChangeEvent};
66
use tracing::{error, warn};
77
use std::collections::hash_map::DefaultHasher;
88
use std::collections::HashSet;
99
use std::hash::{Hash, Hasher};
1010
use std::path::PathBuf;
1111
use std::str::FromStr;
1212
use std::{collections::HashMap, fs};
13-
use crate::core::diagnostics::{create_diagnostic, DiagnosticCode};
13+
use crate::core::diagnostics::{create_diagnostic, DiagnosticCode, DiagnosticSetting};
1414
use crate::threads::SessionInfo;
1515
use crate::utils::PathSanitizer;
1616
use std::rc::Rc;
@@ -292,36 +292,69 @@ impl FileInfo {
292292
if self.need_push {
293293
let mut all_diagnostics = Vec::new();
294294

295-
for diagnostics in self.diagnostics.values() {
296-
for d in diagnostics.iter() {
297-
//check noqa lines
298-
let updated = self.update_range(d.clone());
299-
let updated_line = updated.range.start.line;
300-
if let Some(noqa_line) = self.noqas_lines.get(&updated_line) {
301-
match noqa_line {
302-
NoqaInfo::None => {},
303-
NoqaInfo::All => {
304-
continue;
305-
}
306-
NoqaInfo::Codes(codes) => {
307-
match &updated.code {
308-
None => {continue;},
309-
Some(NumberOrString::Number(n)) => {
310-
if codes.contains(&n.to_string()) {
311-
continue;
312-
}
313-
},
314-
Some(NumberOrString::String(s)) => {
315-
if codes.contains(&s) {
316-
continue;
317-
}
295+
'diagnostics: for d in self.diagnostics.values().flatten() {
296+
//check noqa lines
297+
let updated = self.update_range(d.clone());
298+
let updated_line = updated.range.start.line;
299+
if let Some(noqa_line) = self.noqas_lines.get(&updated_line) {
300+
match noqa_line {
301+
NoqaInfo::None => {},
302+
NoqaInfo::All => {
303+
continue;
304+
}
305+
NoqaInfo::Codes(codes) => {
306+
match &updated.code {
307+
None => {continue;},
308+
Some(NumberOrString::Number(n)) => {
309+
if codes.contains(&n.to_string()) {
310+
continue;
311+
}
312+
},
313+
Some(NumberOrString::String(s)) => {
314+
if codes.contains(&s) {
315+
continue;
318316
}
319317
}
320318
}
321319
}
322320
}
323-
all_diagnostics.push(updated);
324321
}
322+
for filter in &session.sync_odoo.config.diagnostic_filters {
323+
// Path match
324+
if (!filter.negation && !filter.paths.matches(&self.uri)) || (filter.negation && filter.paths.matches(&self.uri)) {
325+
continue;
326+
}
327+
if !filter.codes.is_empty(){
328+
// we pass the filter if we do not have code, or does it not match the filter
329+
let Some(updated_code) = &updated.code else {
330+
continue;
331+
};
332+
let updated_code = match updated_code {
333+
NumberOrString::Number(n) => n.to_string(),
334+
NumberOrString::String(s) => s.clone(),
335+
};
336+
if !filter.codes.iter().any(|re| re.is_match(&updated_code)) {
337+
continue;
338+
}
339+
}
340+
if !filter.types.is_empty() {
341+
// we pass the filter if we do not have severity, or does it not match the filter
342+
let Some(severity) = &updated.severity else {
343+
continue;
344+
};
345+
if !filter.types.iter().any(|t| match (t, severity) {
346+
(DiagnosticSetting::Error, &DiagnosticSeverity::ERROR)
347+
| (DiagnosticSetting::Warning, &DiagnosticSeverity::WARNING)
348+
| (DiagnosticSetting::Info, &DiagnosticSeverity::INFORMATION)
349+
| (DiagnosticSetting::Hint, &DiagnosticSeverity::HINT) => true,
350+
_ => false,
351+
}) {
352+
continue;
353+
}
354+
}
355+
continue 'diagnostics;
356+
}
357+
all_diagnostics.push(updated);
325358
}
326359
session.send_notification::<PublishDiagnosticsParams>(PublishDiagnostics::METHOD, PublishDiagnosticsParams{
327360
uri: FileMgr::pathname2uri(&self.uri),

0 commit comments

Comments
 (0)