1use crate::types::{LspError, LspResult, Position};
33use ricecoder_completion::{
34 CompletionEngine, CompletionItem, CompletionItemKind, Position as CompletionPosition,
35};
36use serde_json::{json, Value};
37use std::sync::Arc;
38use tracing::debug;
39
40pub struct CompletionHandler {
42 engine: Arc<dyn CompletionEngine>,
43}
44
45impl CompletionHandler {
46 pub fn new(engine: Arc<dyn CompletionEngine>) -> Self {
48 Self { engine }
49 }
50
51 pub async fn handle_completion(
53 &self,
54 code: &str,
55 position: Position,
56 language: &str,
57 ) -> LspResult<Vec<Value>> {
58 debug!(
59 "Handling completion request at line={}, character={}",
60 position.line, position.character
61 );
62
63 let completion_position = CompletionPosition::new(position.line, position.character);
65
66 let completions = self
68 .engine
69 .generate_completions(code, completion_position, language)
70 .await
71 .map_err(|e| LspError::InternalError(format!("Completion generation failed: {}", e)))?;
72
73 debug!("Generated {} completions", completions.len());
74
75 let items: Vec<Value> = completions
77 .iter()
78 .enumerate()
79 .map(|(index, item)| self.completion_item_to_json(item, index as u32))
80 .collect();
81
82 Ok(items)
83 }
84
85 pub async fn handle_completion_resolve(&self, item: &Value) -> LspResult<Value> {
87 debug!("Handling completion item resolve");
88
89 let _label = item.get("label").and_then(|v| v.as_str()).ok_or_else(|| {
91 LspError::InvalidParams("Missing label in completion item".to_string())
92 })?;
93
94 let mut resolved = item.clone();
96
97 resolved["resolved"] = json!(true);
99
100 Ok(resolved)
101 }
102
103 pub fn apply_completion(
105 &self,
106 code: &str,
107 position: Position,
108 insert_text: &str,
109 ) -> LspResult<String> {
110 debug!(
111 "Applying completion at line={}, character={}",
112 position.line, position.character
113 );
114
115 let lines: Vec<&str> = code.lines().collect();
117
118 if position.line as usize >= lines.len() {
120 return Err(LspError::InvalidParams(format!(
121 "Line {} is out of bounds",
122 position.line
123 )));
124 }
125
126 let line = lines[position.line as usize];
127 if position.character as usize > line.len() {
128 return Err(LspError::InvalidParams(format!(
129 "Character {} is out of bounds on line {}",
130 position.character, position.line
131 )));
132 }
133
134 let mut result = String::new();
136
137 for (i, l) in lines.iter().enumerate() {
139 if i < position.line as usize {
140 result.push_str(l);
141 result.push('\n');
142 }
143 }
144
145 let line = lines[position.line as usize];
147 let before = &line[..position.character as usize];
148 let after = &line[position.character as usize..];
149
150 result.push_str(before);
151 result.push_str(insert_text);
152 result.push_str(after);
153
154 if (position.line as usize + 1) < lines.len() {
156 result.push('\n');
157 for l in lines.iter().skip(position.line as usize + 1) {
158 result.push_str(l);
159 result.push('\n');
160 }
161 if !code.ends_with('\n') {
163 result.pop();
164 }
165 }
166
167 Ok(result)
168 }
169
170 pub fn validate_code_validity(&self, code: &str) -> LspResult<bool> {
172 debug!("Validating code validity");
173
174 let mut bracket_count = 0;
176 let mut paren_count = 0;
177 let mut brace_count = 0;
178
179 for ch in code.chars() {
180 match ch {
181 '[' => bracket_count += 1,
182 ']' => bracket_count -= 1,
183 '(' => paren_count += 1,
184 ')' => paren_count -= 1,
185 '{' => brace_count += 1,
186 '}' => brace_count -= 1,
187 _ => {}
188 }
189
190 if bracket_count < 0 || paren_count < 0 || brace_count < 0 {
192 return Ok(false);
193 }
194 }
195
196 Ok(bracket_count == 0 && paren_count == 0 && brace_count == 0)
198 }
199
200 fn completion_item_to_json(&self, item: &CompletionItem, index: u32) -> Value {
202 let kind = self.completion_kind_to_lsp(item.kind);
203
204 json!({
205 "label": item.label,
206 "kind": kind,
207 "detail": item.detail,
208 "documentation": item.documentation,
209 "sortText": item.sort_text.as_ref().unwrap_or(&format!("{:04}", index)),
210 "filterText": item.filter_text.as_ref().unwrap_or(&item.label),
211 "insertText": item.insert_text,
212 "score": item.score,
213 })
214 }
215
216 fn completion_kind_to_lsp(&self, kind: CompletionItemKind) -> u32 {
218 match kind {
219 CompletionItemKind::Text => 1,
220 CompletionItemKind::Method => 2,
221 CompletionItemKind::Function => 3,
222 CompletionItemKind::Constructor => 4,
223 CompletionItemKind::Field => 5,
224 CompletionItemKind::Variable => 6,
225 CompletionItemKind::Class => 7,
226 CompletionItemKind::Interface => 8,
227 CompletionItemKind::Module => 9,
228 CompletionItemKind::Property => 10,
229 CompletionItemKind::Unit => 11,
230 CompletionItemKind::Value => 12,
231 CompletionItemKind::Enum => 13,
232 CompletionItemKind::Keyword => 14,
233 CompletionItemKind::Snippet => 15,
234 CompletionItemKind::Color => 16,
235 CompletionItemKind::File => 17,
236 CompletionItemKind::Reference => 18,
237 CompletionItemKind::Folder => 19,
238 CompletionItemKind::EnumMember => 20,
239 CompletionItemKind::Constant => 21,
240 CompletionItemKind::Struct => 22,
241 CompletionItemKind::EventListener => 23,
242 CompletionItemKind::Operator => 24,
243 CompletionItemKind::TypeParameter => 25,
244 CompletionItemKind::Trait => 26,
245 }
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn test_completion_kind_to_lsp() {
255 let handler = CompletionHandler::new(Arc::new(MockCompletionEngine));
256 assert_eq!(
257 handler.completion_kind_to_lsp(CompletionItemKind::Function),
258 3
259 );
260 assert_eq!(
261 handler.completion_kind_to_lsp(CompletionItemKind::Variable),
262 6
263 );
264 assert_eq!(
265 handler.completion_kind_to_lsp(CompletionItemKind::Keyword),
266 14
267 );
268 }
269
270 #[test]
271 fn test_apply_completion_simple() {
272 let handler = CompletionHandler::new(Arc::new(MockCompletionEngine));
273 let code = "fn main() {\n let x = ";
274 let position = Position::new(1, 12);
275 let result = handler.apply_completion(code, position, "42");
276 assert!(result.is_ok());
277 let new_code = result.unwrap();
278 assert!(new_code.contains("let x = 42"));
279 }
280
281 #[test]
282 fn test_apply_completion_invalid_position() {
283 let handler = CompletionHandler::new(Arc::new(MockCompletionEngine));
284 let code = "fn main() {}";
285 let position = Position::new(10, 0); let result = handler.apply_completion(code, position, "test");
287 assert!(result.is_err());
288 }
289
290 #[test]
291 fn test_validate_code_validity_balanced() {
292 let handler = CompletionHandler::new(Arc::new(MockCompletionEngine));
293 let code = "fn main() { let x = [1, 2, 3]; }";
294 let result = handler.validate_code_validity(code);
295 assert!(result.is_ok());
296 assert!(result.unwrap());
297 }
298
299 #[test]
300 fn test_validate_code_validity_unbalanced() {
301 let handler = CompletionHandler::new(Arc::new(MockCompletionEngine));
302 let code = "fn main() { let x = [1, 2, 3; }";
303 let result = handler.validate_code_validity(code);
304 assert!(result.is_ok());
305 assert!(!result.unwrap());
306 }
307
308 struct MockCompletionEngine;
310
311 #[async_trait::async_trait]
312 impl CompletionEngine for MockCompletionEngine {
313 async fn generate_completions(
314 &self,
315 _code: &str,
316 _position: CompletionPosition,
317 _language: &str,
318 ) -> ricecoder_completion::CompletionResult<Vec<CompletionItem>> {
319 Ok(vec![])
320 }
321
322 async fn resolve_completion(
323 &self,
324 item: &CompletionItem,
325 ) -> ricecoder_completion::CompletionResult<CompletionItem> {
326 Ok(item.clone())
327 }
328 }
329}