fresh/services/completion/
service.rs1use super::buffer_words::BufferWordProvider;
22use super::dabbrev::DabbrevProvider;
23use super::provider::{
24 CompletionCandidate, CompletionContext, CompletionProvider, CompletionSourceId, ProviderResult,
25};
26
27pub struct CompletionService {
29 providers: Vec<Box<dyn CompletionProvider>>,
30 pending_async: Vec<(u64, CompletionSourceId)>,
32}
33
34impl CompletionService {
35 pub fn new() -> Self {
37 let mut svc = Self {
38 providers: Vec::new(),
39 pending_async: Vec::new(),
40 };
41 svc.register(Box::new(BufferWordProvider::new()));
42 svc.register(Box::new(DabbrevProvider::new()));
43 svc
44 }
45
46 pub fn register(&mut self, provider: Box<dyn CompletionProvider>) {
48 let id = provider.id();
50 self.providers.retain(|p| p.id() != id);
51 self.providers.push(provider);
52 }
53
54 pub fn unregister(&mut self, id: &CompletionSourceId) {
56 self.providers.retain(|p| p.id() != *id);
57 }
58
59 pub fn request(
70 &mut self,
71 ctx: &CompletionContext,
72 buffer_window: &[u8],
73 ) -> Vec<CompletionCandidate> {
74 self.pending_async.clear();
75 let mut all_candidates: Vec<CompletionCandidate> = Vec::new();
76
77 let mut indices: Vec<usize> = (0..self.providers.len()).collect();
79 indices.sort_by_key(|&i| self.providers[i].priority());
80
81 for &i in &indices {
82 let provider = &self.providers[i];
83 if !provider.is_enabled(ctx) {
84 continue;
85 }
86 let source_id = provider.id();
87 match provider.provide(ctx, buffer_window) {
88 ProviderResult::Ready(mut candidates) => {
89 for c in &mut candidates {
90 c.source = Some(source_id.clone());
91 }
92 all_candidates.extend(candidates);
93 }
94 ProviderResult::Pending(request_id) => {
95 self.pending_async.push((request_id, source_id));
96 }
97 }
98 }
99
100 Self::rank(&mut all_candidates);
101 all_candidates
102 }
103
104 pub fn supply_async_results(
109 &mut self,
110 request_id: u64,
111 mut candidates: Vec<CompletionCandidate>,
112 ) -> Option<Vec<CompletionCandidate>> {
113 let pos = self
115 .pending_async
116 .iter()
117 .position(|(id, _)| *id == request_id)?;
118 let (_rid, source_id) = self.pending_async.remove(pos);
119 for c in &mut candidates {
120 c.source = Some(source_id.clone());
121 }
122 Self::rank(&mut candidates);
123 Some(candidates)
124 }
125
126 pub fn has_pending(&self) -> bool {
128 !self.pending_async.is_empty()
129 }
130
131 fn rank(candidates: &mut Vec<CompletionCandidate>) {
136 candidates.sort_by(|a, b| {
138 b.score
139 .cmp(&a.score)
140 .then_with(|| a.label.to_lowercase().cmp(&b.label.to_lowercase()))
141 });
142
143 let mut seen = std::collections::HashSet::new();
146 candidates.retain(|c| {
147 let key = (
148 c.label.to_lowercase(),
149 c.insert_text.clone().unwrap_or_default(),
150 );
151 seen.insert(key)
152 });
153 }
154
155 pub fn providers(&self) -> &[Box<dyn CompletionProvider>] {
157 &self.providers
158 }
159}
160
161impl Default for CompletionService {
162 fn default() -> Self {
163 Self::new()
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 struct StaticProvider {
173 id: &'static str,
174 candidates: Vec<CompletionCandidate>,
175 priority: u32,
176 }
177
178 impl CompletionProvider for StaticProvider {
179 fn id(&self) -> CompletionSourceId {
180 CompletionSourceId(self.id.into())
181 }
182 fn display_name(&self) -> &str {
183 self.id
184 }
185 fn is_enabled(&self, _ctx: &CompletionContext) -> bool {
186 true
187 }
188 fn provide(&self, _ctx: &CompletionContext, _buffer_window: &[u8]) -> ProviderResult {
189 ProviderResult::Ready(self.candidates.clone())
190 }
191 fn priority(&self) -> u32 {
192 self.priority
193 }
194 }
195
196 fn test_ctx() -> CompletionContext {
197 CompletionContext {
198 prefix: "te".into(),
199 cursor_byte: 10,
200 word_start_byte: 8,
201 buffer_len: 100,
202 is_large_file: false,
203 scan_range: 0..100,
204 viewport_top_byte: 0,
205 viewport_bottom_byte: 100,
206 language_id: None,
207 word_chars_extra: String::new(),
208 prefix_has_uppercase: false,
209 other_buffers: Vec::new(),
210 }
211 }
212
213 #[test]
214 fn merges_multiple_providers() {
215 let mut svc = CompletionService {
216 providers: Vec::new(),
217 pending_async: Vec::new(),
218 };
219 svc.register(Box::new(StaticProvider {
220 id: "a",
221 candidates: vec![CompletionCandidate::word("test_alpha".into(), 100)],
222 priority: 10,
223 }));
224 svc.register(Box::new(StaticProvider {
225 id: "b",
226 candidates: vec![CompletionCandidate::word("test_beta".into(), 200)],
227 priority: 20,
228 }));
229
230 let ctx = test_ctx();
231 let results = svc.request(&ctx, b"");
232 assert_eq!(results.len(), 2);
233 assert_eq!(results[0].label, "test_beta");
235 assert_eq!(results[1].label, "test_alpha");
236 }
237
238 #[test]
239 fn deduplicates_by_label() {
240 let mut svc = CompletionService {
241 providers: Vec::new(),
242 pending_async: Vec::new(),
243 };
244 svc.register(Box::new(StaticProvider {
245 id: "a",
246 candidates: vec![CompletionCandidate::word("test".into(), 50)],
247 priority: 10,
248 }));
249 svc.register(Box::new(StaticProvider {
250 id: "b",
251 candidates: vec![CompletionCandidate::word("test".into(), 100)],
252 priority: 20,
253 }));
254
255 let ctx = test_ctx();
256 let results = svc.request(&ctx, b"");
257 assert_eq!(results.len(), 1);
259 assert_eq!(results[0].score, 100);
260 }
261
262 #[test]
263 fn async_supply() {
264 let mut svc = CompletionService {
265 providers: Vec::new(),
266 pending_async: vec![(42, CompletionSourceId("lsp".into()))],
267 };
268 let candidates = vec![CompletionCandidate::word("testing".into(), 300)];
269 let merged = svc.supply_async_results(42, candidates);
270 assert!(merged.is_some());
271 let merged = merged.unwrap();
272 assert_eq!(merged.len(), 1);
273 assert_eq!(merged[0].source.as_ref().unwrap().0, "lsp");
274 }
275
276 #[test]
277 fn async_supply_unknown_id_returns_none() {
278 let mut svc = CompletionService {
279 providers: Vec::new(),
280 pending_async: vec![(42, CompletionSourceId("lsp".into()))],
281 };
282 assert!(svc.supply_async_results(99, vec![]).is_none());
283 }
284}