ricecoder_external_lsp/merger/
diagnostics.rs1use crate::types::MergeConfig;
4use ricecoder_lsp::types::Diagnostic;
5use std::collections::HashSet;
6
7pub struct DiagnosticsMerger;
9
10impl DiagnosticsMerger {
11 pub fn new() -> Self {
13 Self
14 }
15
16 pub fn merge(
28 external: Option<Vec<Diagnostic>>,
29 internal: Vec<Diagnostic>,
30 config: &MergeConfig,
31 ) -> Vec<Diagnostic> {
32 let mut result = Vec::new();
33
34 if let Some(ext) = external {
36 result.extend(ext);
37 } else if config.include_internal {
38 result.extend(internal);
40 }
41
42 if config.deduplicate {
44 result = Self::deduplicate(result);
45 }
46
47 result
48 }
49
50 fn deduplicate(diagnostics: Vec<Diagnostic>) -> Vec<Diagnostic> {
52 let mut seen = HashSet::new();
53 let mut result = Vec::new();
54
55 for diag in diagnostics {
56 let key = format!(
58 "{}:{}:{}:{}:{}",
59 diag.range.start.line,
60 diag.range.start.character,
61 diag.range.end.line,
62 diag.range.end.character,
63 diag.message
64 );
65
66 if !seen.contains(&key) {
67 seen.insert(key);
68 result.push(diag);
69 }
70 }
71
72 result
73 }
74}
75
76impl Default for DiagnosticsMerger {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use ricecoder_lsp::types::{DiagnosticSeverity, Position, Range};
86
87 fn create_diagnostic(line: u32, message: &str) -> Diagnostic {
88 Diagnostic::new(
89 Range::new(Position::new(line, 0), Position::new(line, 5)),
90 DiagnosticSeverity::Error,
91 message.to_string(),
92 )
93 }
94
95 #[test]
96 fn test_merge_external_only() {
97 let external = vec![
98 create_diagnostic(0, "error 1"),
99 create_diagnostic(1, "error 2"),
100 ];
101 let internal = vec![];
102 let config = MergeConfig::default();
103
104 let result = DiagnosticsMerger::merge(Some(external), internal, &config);
105
106 assert_eq!(result.len(), 2);
107 assert_eq!(result[0].message, "error 1");
108 assert_eq!(result[1].message, "error 2");
109 }
110
111 #[test]
112 fn test_merge_internal_only() {
113 let external = None;
114 let internal = vec![
115 create_diagnostic(0, "warning 1"),
116 create_diagnostic(1, "warning 2"),
117 ];
118 let config = MergeConfig::default();
119
120 let result = DiagnosticsMerger::merge(external, internal, &config);
121
122 assert_eq!(result.len(), 2);
123 assert_eq!(result[0].message, "warning 1");
124 assert_eq!(result[1].message, "warning 2");
125 }
126
127 #[test]
128 fn test_merge_external_ignores_internal() {
129 let external = vec![create_diagnostic(0, "external error")];
130 let internal = vec![create_diagnostic(1, "internal warning")];
131 let config = MergeConfig::default();
132
133 let result = DiagnosticsMerger::merge(Some(external), internal, &config);
134
135 assert_eq!(result.len(), 1);
136 assert_eq!(result[0].message, "external error");
137 }
138
139 #[test]
140 fn test_merge_without_internal() {
141 let external = None;
142 let internal = vec![create_diagnostic(0, "warning")];
143 let config = MergeConfig {
144 include_internal: false,
145 deduplicate: true,
146 };
147
148 let result = DiagnosticsMerger::merge(external, internal, &config);
149
150 assert_eq!(result.len(), 0);
151 }
152
153 #[test]
154 fn test_merge_with_deduplication() {
155 let external = vec![
156 create_diagnostic(0, "error 1"),
157 create_diagnostic(0, "error 1"), ];
159 let internal = vec![];
160 let config = MergeConfig {
161 include_internal: true,
162 deduplicate: true,
163 };
164
165 let result = DiagnosticsMerger::merge(Some(external), internal, &config);
166
167 assert_eq!(result.len(), 1);
168 assert_eq!(result[0].message, "error 1");
169 }
170
171 #[test]
172 fn test_merge_without_deduplication() {
173 let external = vec![
174 create_diagnostic(0, "error 1"),
175 create_diagnostic(0, "error 1"), ];
177 let internal = vec![];
178 let config = MergeConfig {
179 include_internal: true,
180 deduplicate: false,
181 };
182
183 let result = DiagnosticsMerger::merge(Some(external), internal, &config);
184
185 assert_eq!(result.len(), 2);
186 }
187
188 #[test]
189 fn test_merge_empty_external() {
190 let external = Some(vec![]);
191 let internal = vec![create_diagnostic(0, "warning")];
192 let config = MergeConfig::default();
193
194 let result = DiagnosticsMerger::merge(external, internal, &config);
195
196 assert_eq!(result.len(), 0);
197 }
198}