nvrim/diagnostics/filters/
related_info.rs1use std::collections::HashSet;
7
8use nvim_oxi::Array;
9use nvim_oxi::Dictionary;
10use nvim_oxi::ObjectKind;
11use nvim_oxi::conversion::FromObject;
12use rootcause::prelude::ResultExt;
13use ytil_noxi::dict::DictionaryExt;
14
15use crate::diagnostics::filters::BufferWithPath;
16use crate::diagnostics::filters::DiagnosticsFilter;
17
18pub struct RelatedInfoFilter {
21 rel_infos: HashSet<RelatedInfo>,
25}
26
27impl RelatedInfoFilter {
28 pub fn new(lsp_diags: &[Dictionary]) -> rootcause::Result<Self> {
33 Ok(Self {
34 rel_infos: Self::get_related_infos(lsp_diags)?,
35 })
36 }
37
38 fn get_related_infos(lsp_diags: &[Dictionary]) -> rootcause::Result<HashSet<RelatedInfo>> {
43 let mut out = HashSet::with_capacity(lsp_diags.len().saturating_mul(2));
45 for lsp_diag in lsp_diags {
46 let Some(lsp) = lsp_diag.get_dict(&["user_data", "lsp"])? else {
48 continue;
49 };
50 let rel_infos_key = "relatedInformation";
51 let Some(rel_infos) = lsp.get(rel_infos_key) else {
52 continue;
53 };
54
55 let rel_infos = Array::from_object(rel_infos.clone())
56 .context("unexpected object kind")
57 .attach_with(|| {
58 ytil_noxi::extract::unexpected_kind_error_msg(rel_infos, rel_infos_key, &lsp, ObjectKind::Array)
59 })?;
60 for rel_info in rel_infos {
61 let rel_info = Dictionary::try_from(rel_info)?;
62 out.insert(RelatedInfo::from_related_info(&rel_info)?);
63 }
64 }
65 Ok(out)
66 }
67}
68
69impl DiagnosticsFilter for RelatedInfoFilter {
70 fn skip_diagnostic(&self, _buf: &BufferWithPath, lsp_diag: &Dictionary) -> rootcause::Result<bool> {
75 if self.rel_infos.is_empty() {
76 return Ok(false);
77 }
78 let rel_info = RelatedInfo::from_lsp_diagnostic(lsp_diag)?;
80 Ok(self.rel_infos.contains(&rel_info))
81 }
82}
83
84#[derive(Eq, Hash, PartialEq)]
86struct RelatedInfo {
87 col: i64,
89 end_col: i64,
91 end_lnum: i64,
93 lnum: i64,
95 message: String,
97}
98
99impl RelatedInfo {
100 fn from_lsp_diagnostic(lsp_diagnostic: &Dictionary) -> rootcause::Result<Self> {
105 Ok(Self {
106 message: lsp_diagnostic.get_t::<nvim_oxi::String>("message")?,
107 lnum: lsp_diagnostic.get_t::<nvim_oxi::Integer>("lnum")?,
108 col: lsp_diagnostic.get_t::<nvim_oxi::Integer>("col")?,
109 end_lnum: lsp_diagnostic.get_t::<nvim_oxi::Integer>("end_lnum")?,
110 end_col: lsp_diagnostic.get_t::<nvim_oxi::Integer>("end_col")?,
111 })
112 }
113
114 fn from_related_info(rel_info: &Dictionary) -> rootcause::Result<Self> {
119 let (start, end) = {
120 let range_query = ["location", "range"];
121 let range = rel_info.get_required_dict(&range_query)?;
122
123 let start_query = ["start"];
124 let end_query = ["end"];
125 (
126 range.get_required_dict(&start_query)?,
127 range.get_required_dict(&end_query)?,
128 )
129 };
130
131 Ok(Self {
132 message: rel_info.get_t::<nvim_oxi::String>("message")?,
133 lnum: start.get_t::<nvim_oxi::Integer>("line")?,
134 col: start.get_t::<nvim_oxi::Integer>("character")?,
135 end_lnum: end.get_t::<nvim_oxi::Integer>("line")?,
136 end_col: end.get_t::<nvim_oxi::Integer>("character")?,
137 })
138 }
139}