ricecoder_external_lsp/merger/
completion.rs1use crate::types::MergeConfig;
4use ricecoder_completion::types::CompletionItem;
5use std::collections::HashSet;
6
7pub struct CompletionMerger;
9
10impl CompletionMerger {
11 pub fn new() -> Self {
13 Self
14 }
15
16 pub fn merge(
28 external: Option<Vec<CompletionItem>>,
29 internal: Vec<CompletionItem>,
30 config: &MergeConfig,
31 ) -> Vec<CompletionItem> {
32 let mut result = Vec::new();
33 let mut seen_labels = HashSet::new();
34
35 if let Some(ext) = external {
37 for item in ext {
38 if config.deduplicate {
39 if !seen_labels.contains(&item.label) {
40 seen_labels.insert(item.label.clone());
41 result.push(item);
42 }
43 } else {
44 result.push(item);
45 }
46 }
47 }
48
49 if config.include_internal {
51 for item in internal {
52 if config.deduplicate {
53 if !seen_labels.contains(&item.label) {
54 seen_labels.insert(item.label.clone());
55 result.push(item);
56 }
57 } else {
58 result.push(item);
59 }
60 }
61 }
62
63 result.sort_by(|a, b| {
65 b.score
66 .partial_cmp(&a.score)
67 .unwrap_or(std::cmp::Ordering::Equal)
68 });
69
70 result
71 }
72}
73
74impl Default for CompletionMerger {
75 fn default() -> Self {
76 Self::new()
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use ricecoder_completion::types::CompletionItemKind;
84
85 fn create_completion(label: &str, score: f32) -> CompletionItem {
86 CompletionItem::new(
87 label.to_string(),
88 CompletionItemKind::Variable,
89 label.to_string(),
90 )
91 .with_score(score)
92 }
93
94 #[test]
95 fn test_merge_external_only() {
96 let external = vec![
97 create_completion("foo", 0.9),
98 create_completion("bar", 0.8),
99 ];
100 let internal = vec![];
101 let config = MergeConfig::default();
102
103 let result = CompletionMerger::merge(Some(external), internal, &config);
104
105 assert_eq!(result.len(), 2);
106 assert_eq!(result[0].label, "foo");
107 assert_eq!(result[1].label, "bar");
108 }
109
110 #[test]
111 fn test_merge_internal_only() {
112 let external = None;
113 let internal = vec![
114 create_completion("baz", 0.7),
115 create_completion("qux", 0.6),
116 ];
117 let config = MergeConfig::default();
118
119 let result = CompletionMerger::merge(external, internal, &config);
120
121 assert_eq!(result.len(), 2);
122 assert_eq!(result[0].label, "baz");
123 assert_eq!(result[1].label, "qux");
124 }
125
126 #[test]
127 fn test_merge_both_with_deduplication() {
128 let external = vec![
129 create_completion("foo", 0.9),
130 create_completion("bar", 0.8),
131 ];
132 let internal = vec![
133 create_completion("foo", 0.5), create_completion("baz", 0.7),
135 ];
136 let config = MergeConfig {
137 include_internal: true,
138 deduplicate: true,
139 };
140
141 let result = CompletionMerger::merge(Some(external), internal, &config);
142
143 assert_eq!(result.len(), 3);
144 assert_eq!(result[0].label, "foo"); assert_eq!(result[1].label, "bar"); assert_eq!(result[2].label, "baz"); }
149
150 #[test]
151 fn test_merge_both_without_deduplication() {
152 let external = vec![create_completion("foo", 0.9)];
153 let internal = vec![create_completion("foo", 0.5)];
154 let config = MergeConfig {
155 include_internal: true,
156 deduplicate: false,
157 };
158
159 let result = CompletionMerger::merge(Some(external), internal, &config);
160
161 assert_eq!(result.len(), 2);
162 assert_eq!(result[0].label, "foo");
163 assert_eq!(result[1].label, "foo");
164 }
165
166 #[test]
167 fn test_merge_without_internal() {
168 let external = vec![create_completion("foo", 0.9)];
169 let internal = vec![create_completion("bar", 0.8)];
170 let config = MergeConfig {
171 include_internal: false,
172 deduplicate: true,
173 };
174
175 let result = CompletionMerger::merge(Some(external), internal, &config);
176
177 assert_eq!(result.len(), 1);
178 assert_eq!(result[0].label, "foo");
179 }
180
181 #[test]
182 fn test_merge_sorting_by_score() {
183 let external = vec![
184 create_completion("low", 0.3),
185 create_completion("high", 0.9),
186 create_completion("mid", 0.6),
187 ];
188 let internal = vec![];
189 let config = MergeConfig::default();
190
191 let result = CompletionMerger::merge(Some(external), internal, &config);
192
193 assert_eq!(result.len(), 3);
194 assert_eq!(result[0].label, "high");
195 assert_eq!(result[1].label, "mid");
196 assert_eq!(result[2].label, "low");
197 }
198
199 #[test]
200 fn test_merge_empty_external() {
201 let external = Some(vec![]);
202 let internal = vec![create_completion("foo", 0.8)];
203 let config = MergeConfig::default();
204
205 let result = CompletionMerger::merge(external, internal, &config);
206
207 assert_eq!(result.len(), 1);
208 assert_eq!(result[0].label, "foo");
209 }
210}