ricecoder_lsp/
server.rs

1//! LSP Server implementation
2//!
3//! This module implements the core LSP server that handles initialization,
4//! shutdown, and request routing.
5
6use crate::code_actions::{CodeActionsEngine, DefaultCodeActionsEngine};
7use crate::completion::CompletionHandler;
8use crate::config::CompletionConfig;
9use crate::diagnostics::{DefaultDiagnosticsEngine, DiagnosticsEngine};
10use crate::hover::HoverProvider;
11use crate::refactoring::RefactoringHandler;
12use crate::transport::{AsyncStdioTransport, JsonRpcError, JsonRpcResponse, LspMessage};
13use crate::types::{Language, LspError, LspResult, Position, ServerState};
14use ricecoder_completion::CompletionEngine;
15use serde_json::{json, Value};
16use std::collections::HashMap;
17use std::sync::Arc;
18use tracing::{debug, error, info, warn};
19
20/// Server capabilities
21#[derive(Debug, Clone)]
22pub struct ServerCapabilities {
23    /// Text document sync capability
24    pub text_document_sync: u32,
25    /// Hover capability
26    pub hover_provider: bool,
27    /// Code action capability
28    pub code_action_provider: bool,
29    /// Diagnostics capability
30    pub diagnostic_provider: bool,
31    /// Completion capability
32    pub completion_provider: bool,
33}
34
35impl Default for ServerCapabilities {
36    fn default() -> Self {
37        Self {
38            text_document_sync: 1, // Full sync
39            hover_provider: true,
40            code_action_provider: true,
41            diagnostic_provider: true,
42            completion_provider: true,
43        }
44    }
45}
46
47impl ServerCapabilities {
48    /// Convert to JSON
49    pub fn to_json(&self) -> Value {
50        json!({
51            "textDocumentSync": self.text_document_sync,
52            "hoverProvider": self.hover_provider,
53            "codeActionProvider": self.code_action_provider,
54            "diagnosticProvider": self.diagnostic_provider,
55            "completionProvider": {
56                "resolveProvider": true,
57                "triggerCharacters": [".", ":", "::", "(", "[", "{", " "]
58            },
59        })
60    }
61}
62
63/// Client capabilities
64#[derive(Debug, Clone)]
65pub struct ClientCapabilities {
66    /// Raw capabilities from client
67    pub raw: Value,
68}
69
70impl ClientCapabilities {
71    /// Create from JSON value
72    pub fn from_json(value: Value) -> Self {
73        Self { raw: value }
74    }
75}
76
77/// LSP Server
78pub struct LspServer {
79    /// Server state
80    state: ServerState,
81    /// Server capabilities
82    capabilities: ServerCapabilities,
83    /// Client capabilities
84    client_capabilities: Option<ClientCapabilities>,
85    /// Open documents (URI -> content)
86    documents: HashMap<String, String>,
87    /// Transport
88    transport: AsyncStdioTransport,
89    /// Hover provider
90    hover_provider: HoverProvider,
91    /// Diagnostics engine
92    diagnostics_engine: Box<dyn DiagnosticsEngine>,
93    /// Code actions engine
94    code_actions_engine: Box<dyn CodeActionsEngine>,
95    /// Completion handler
96    completion_handler: Option<CompletionHandler>,
97    /// Completion configuration
98    completion_config: CompletionConfig,
99    /// Refactoring handler
100    refactoring_handler: RefactoringHandler,
101}
102
103impl LspServer {
104    /// Create a new LSP server
105    pub fn new() -> Self {
106        Self {
107            state: ServerState::Initializing,
108            capabilities: ServerCapabilities::default(),
109            client_capabilities: None,
110            documents: HashMap::new(),
111            transport: AsyncStdioTransport::new(),
112            hover_provider: HoverProvider::new(),
113            diagnostics_engine: Box::new(DefaultDiagnosticsEngine::new()),
114            code_actions_engine: Box::new(DefaultCodeActionsEngine::new()),
115            completion_handler: None,
116            completion_config: CompletionConfig::default(),
117            refactoring_handler: RefactoringHandler::new(),
118        }
119    }
120
121    /// Create a new LSP server with custom completion config
122    pub fn with_completion_config(completion_config: CompletionConfig) -> Self {
123        Self {
124            state: ServerState::Initializing,
125            capabilities: ServerCapabilities::default(),
126            client_capabilities: None,
127            documents: HashMap::new(),
128            transport: AsyncStdioTransport::new(),
129            hover_provider: HoverProvider::new(),
130            diagnostics_engine: Box::new(DefaultDiagnosticsEngine::new()),
131            code_actions_engine: Box::new(DefaultCodeActionsEngine::new()),
132            completion_handler: None,
133            completion_config,
134            refactoring_handler: RefactoringHandler::new(),
135        }
136    }
137
138    /// Register a completion engine
139    pub fn register_completion_engine(&mut self, engine: Arc<dyn CompletionEngine>) {
140        self.completion_handler = Some(CompletionHandler::new(engine));
141        info!("Completion engine registered");
142    }
143
144    /// Get completion configuration
145    pub fn completion_config(&self) -> &CompletionConfig {
146        &self.completion_config
147    }
148
149    /// Set completion configuration
150    pub fn set_completion_config(&mut self, config: CompletionConfig) {
151        self.completion_config = config;
152        debug!("Completion configuration updated");
153    }
154
155    /// Check if completion is enabled
156    pub fn is_completion_enabled(&self) -> bool {
157        self.completion_config.enabled && self.completion_handler.is_some()
158    }
159
160    /// Get the refactoring handler
161    pub fn refactoring_handler(&self) -> &RefactoringHandler {
162        &self.refactoring_handler
163    }
164
165    /// Get mutable refactoring handler
166    pub fn refactoring_handler_mut(&mut self) -> &mut RefactoringHandler {
167        &mut self.refactoring_handler
168    }
169
170    /// Enable or disable refactoring
171    pub fn set_refactoring_enabled(&mut self, enabled: bool) {
172        self.refactoring_handler.set_enabled(enabled);
173    }
174
175    /// Check if refactoring is enabled
176    pub fn is_refactoring_enabled(&self) -> bool {
177        self.refactoring_handler.is_enabled()
178    }
179
180    /// Get the current server state
181    pub fn state(&self) -> ServerState {
182        self.state
183    }
184
185    /// Set the server state (for testing)
186    pub fn set_state(&mut self, state: ServerState) {
187        self.state = state;
188    }
189
190    /// Get server capabilities
191    pub fn capabilities(&self) -> &ServerCapabilities {
192        &self.capabilities
193    }
194
195    /// Get client capabilities
196    pub fn client_capabilities(&self) -> Option<&ClientCapabilities> {
197        self.client_capabilities.as_ref()
198    }
199
200    /// Get a document by URI
201    pub fn get_document(&self, uri: &str) -> Option<&str> {
202        self.documents.get(uri).map(|s| s.as_str())
203    }
204
205    /// Set a document
206    pub fn set_document(&mut self, uri: String, content: String) {
207        self.documents.insert(uri, content);
208    }
209
210    /// Remove a document
211    pub fn remove_document(&mut self, uri: &str) {
212        self.documents.remove(uri);
213    }
214
215    /// Handle initialize request
216    pub async fn handle_initialize(&mut self, params: Value) -> LspResult<Value> {
217        if self.state != ServerState::Initializing {
218            return Err(LspError::InvalidRequest(
219                "Server is not in initializing state".to_string(),
220            ));
221        }
222
223        info!("Initializing LSP server");
224
225        // Extract client capabilities
226        if let Some(capabilities) = params.get("capabilities") {
227            self.client_capabilities = Some(ClientCapabilities::from_json(capabilities.clone()));
228            debug!("Client capabilities received");
229        }
230
231        // Return server capabilities
232        info!("LSP server initialization complete");
233        Ok(json!({
234            "capabilities": self.capabilities.to_json(),
235            "serverInfo": {
236                "name": "ricecoder-lsp",
237                "version": "0.1.0"
238            }
239        }))
240    }
241
242    /// Handle initialized notification
243    pub async fn handle_initialized(&mut self) -> LspResult<()> {
244        if self.state != ServerState::Initializing {
245            return Err(LspError::InvalidRequest(
246                "Server is not in initializing state".to_string(),
247            ));
248        }
249
250        self.state = ServerState::Initialized;
251        Ok(())
252    }
253
254    /// Handle shutdown request
255    pub async fn handle_shutdown(&mut self) -> LspResult<Value> {
256        if self.state != ServerState::Initialized {
257            return Err(LspError::InvalidRequest(
258                "Server is not initialized".to_string(),
259            ));
260        }
261
262        info!("Shutdown request received");
263        self.state = ServerState::ShuttingDown;
264        info!("Server state changed to ShuttingDown");
265        Ok(json!(null))
266    }
267
268    /// Handle exit notification
269    pub async fn handle_exit(&mut self) -> LspResult<()> {
270        if self.state != ServerState::ShuttingDown {
271            return Err(LspError::InvalidRequest(
272                "Server is not shutting down".to_string(),
273            ));
274        }
275
276        info!("Exit notification received");
277        self.state = ServerState::ShutDown;
278        info!("Server state changed to ShutDown");
279        Ok(())
280    }
281
282    /// Handle did_open notification
283    pub async fn handle_did_open(&mut self, params: Value) -> LspResult<()> {
284        if self.state != ServerState::Initialized {
285            return Err(LspError::InvalidRequest(
286                "Server is not initialized".to_string(),
287            ));
288        }
289
290        let text_document = params
291            .get("textDocument")
292            .ok_or_else(|| LspError::InvalidParams("Missing textDocument".to_string()))?;
293
294        let uri = text_document
295            .get("uri")
296            .and_then(|v| v.as_str())
297            .ok_or_else(|| LspError::InvalidParams("Missing uri".to_string()))?;
298
299        let text = text_document
300            .get("text")
301            .and_then(|v| v.as_str())
302            .ok_or_else(|| LspError::InvalidParams("Missing text".to_string()))?;
303
304        debug!("Document opened: uri={}, size={} bytes", uri, text.len());
305        self.set_document(uri.to_string(), text.to_string());
306        Ok(())
307    }
308
309    /// Handle did_change notification
310    pub async fn handle_did_change(&mut self, params: Value) -> LspResult<()> {
311        if self.state != ServerState::Initialized {
312            return Err(LspError::InvalidRequest(
313                "Server is not initialized".to_string(),
314            ));
315        }
316
317        let text_document = params
318            .get("textDocument")
319            .ok_or_else(|| LspError::InvalidParams("Missing textDocument".to_string()))?;
320
321        let uri = text_document
322            .get("uri")
323            .and_then(|v| v.as_str())
324            .ok_or_else(|| LspError::InvalidParams("Missing uri".to_string()))?;
325
326        let content_changes = params
327            .get("contentChanges")
328            .and_then(|v| v.as_array())
329            .ok_or_else(|| LspError::InvalidParams("Missing contentChanges".to_string()))?;
330
331        // For full document sync, take the last change
332        if let Some(last_change) = content_changes.last() {
333            if let Some(text) = last_change.get("text").and_then(|v| v.as_str()) {
334                self.set_document(uri.to_string(), text.to_string());
335            }
336        }
337
338        Ok(())
339    }
340
341    /// Handle did_close notification
342    pub async fn handle_did_close(&mut self, params: Value) -> LspResult<()> {
343        if self.state != ServerState::Initialized {
344            return Err(LspError::InvalidRequest(
345                "Server is not initialized".to_string(),
346            ));
347        }
348
349        let text_document = params
350            .get("textDocument")
351            .ok_or_else(|| LspError::InvalidParams("Missing textDocument".to_string()))?;
352
353        let uri = text_document
354            .get("uri")
355            .and_then(|v| v.as_str())
356            .ok_or_else(|| LspError::InvalidParams("Missing uri".to_string()))?;
357
358        debug!("Document closed: uri={}", uri);
359        self.remove_document(uri);
360        Ok(())
361    }
362
363    /// Handle hover request
364    pub async fn handle_hover(&self, params: Value) -> LspResult<Value> {
365        if self.state != ServerState::Initialized {
366            return Err(LspError::InvalidRequest(
367                "Server is not initialized".to_string(),
368            ));
369        }
370
371        let text_document = params
372            .get("textDocument")
373            .ok_or_else(|| LspError::InvalidParams("Missing textDocument".to_string()))?;
374
375        let uri = text_document
376            .get("uri")
377            .and_then(|v| v.as_str())
378            .ok_or_else(|| LspError::InvalidParams("Missing uri".to_string()))?;
379
380        let position = params
381            .get("position")
382            .ok_or_else(|| LspError::InvalidParams("Missing position".to_string()))?;
383
384        let line = position
385            .get("line")
386            .and_then(|v| v.as_u64())
387            .ok_or_else(|| LspError::InvalidParams("Missing line".to_string()))?
388            as u32;
389
390        let character = position
391            .get("character")
392            .and_then(|v| v.as_u64())
393            .ok_or_else(|| LspError::InvalidParams("Missing character".to_string()))?
394            as u32;
395
396        // Get document content
397        let code = self
398            .get_document(uri)
399            .ok_or_else(|| LspError::InvalidParams(format!("Document not found: {}", uri)))?;
400
401        // Get hover information
402        let hover_info = self
403            .hover_provider
404            .get_hover_info(code, Position::new(line, character));
405
406        // Convert to JSON response
407        match hover_info {
408            Some(info) => Ok(json!({
409                "contents": {
410                    "kind": match info.contents.kind {
411                        crate::types::MarkupKind::PlainText => "plaintext",
412                        crate::types::MarkupKind::Markdown => "markdown",
413                    },
414                    "value": info.contents.value,
415                },
416                "range": info.range.map(|r| {
417                    json!({
418                        "start": {
419                            "line": r.start.line,
420                            "character": r.start.character,
421                        },
422                        "end": {
423                            "line": r.end.line,
424                            "character": r.end.character,
425                        },
426                    })
427                }),
428            })),
429            None => Ok(json!(null)),
430        }
431    }
432
433    /// Handle diagnostics request
434    pub async fn handle_diagnostics(&self, params: Value) -> LspResult<Value> {
435        if self.state != ServerState::Initialized {
436            return Err(LspError::InvalidRequest(
437                "Server is not initialized".to_string(),
438            ));
439        }
440
441        let text_document = params
442            .get("textDocument")
443            .ok_or_else(|| LspError::InvalidParams("Missing textDocument".to_string()))?;
444
445        let uri = text_document
446            .get("uri")
447            .and_then(|v| v.as_str())
448            .ok_or_else(|| LspError::InvalidParams("Missing uri".to_string()))?;
449
450        // Get document content
451        let code = self
452            .get_document(uri)
453            .ok_or_else(|| LspError::InvalidParams(format!("Document not found: {}", uri)))?;
454
455        // Detect language from URI
456        let language = self.detect_language(uri);
457
458        // Generate diagnostics
459        let diagnostics = self
460            .diagnostics_engine
461            .generate_diagnostics(code, language)
462            .map_err(|e| LspError::InternalError(e.to_string()))?;
463
464        // Convert to JSON response
465        let diagnostics_json: Vec<Value> = diagnostics
466            .iter()
467            .map(|diag| {
468                json!({
469                    "range": {
470                        "start": {
471                            "line": diag.range.start.line,
472                            "character": diag.range.start.character,
473                        },
474                        "end": {
475                            "line": diag.range.end.line,
476                            "character": diag.range.end.character,
477                        },
478                    },
479                    "severity": match diag.severity {
480                        crate::types::DiagnosticSeverity::Error => 1,
481                        crate::types::DiagnosticSeverity::Warning => 2,
482                        crate::types::DiagnosticSeverity::Information => 3,
483                        crate::types::DiagnosticSeverity::Hint => 4,
484                    },
485                    "message": diag.message,
486                    "code": diag.code,
487                    "source": diag.source,
488                })
489            })
490            .collect();
491
492        Ok(json!(diagnostics_json))
493    }
494
495    /// Handle completion request
496    pub async fn handle_completion(&self, params: Value) -> LspResult<Value> {
497        if self.state != ServerState::Initialized {
498            return Err(LspError::InvalidRequest(
499                "Server is not initialized".to_string(),
500            ));
501        }
502
503        if !self.is_completion_enabled() {
504            return Ok(json!([]));
505        }
506
507        let text_document = params
508            .get("textDocument")
509            .ok_or_else(|| LspError::InvalidParams("Missing textDocument".to_string()))?;
510
511        let uri = text_document
512            .get("uri")
513            .and_then(|v| v.as_str())
514            .ok_or_else(|| LspError::InvalidParams("Missing uri".to_string()))?;
515
516        let position = params
517            .get("position")
518            .ok_or_else(|| LspError::InvalidParams("Missing position".to_string()))?;
519
520        let line = position
521            .get("line")
522            .and_then(|v| v.as_u64())
523            .ok_or_else(|| LspError::InvalidParams("Missing line".to_string()))?
524            as u32;
525
526        let character = position
527            .get("character")
528            .and_then(|v| v.as_u64())
529            .ok_or_else(|| LspError::InvalidParams("Missing character".to_string()))?
530            as u32;
531
532        // Get document content
533        let code = self
534            .get_document(uri)
535            .ok_or_else(|| LspError::InvalidParams(format!("Document not found: {}", uri)))?;
536
537        // Detect language from URI
538        let language = self.detect_language(uri);
539
540        // Get completion handler
541        let handler = self.completion_handler.as_ref().ok_or_else(|| {
542            LspError::InternalError("Completion engine not initialized".to_string())
543        })?;
544
545        // Generate completions
546        let completions = handler
547            .handle_completion(code, Position::new(line, character), language.as_str())
548            .await?;
549
550        Ok(json!(completions))
551    }
552
553    /// Handle completionItem/resolve request
554    pub async fn handle_completion_resolve(&self, params: Value) -> LspResult<Value> {
555        if self.state != ServerState::Initialized {
556            return Err(LspError::InvalidRequest(
557                "Server is not initialized".to_string(),
558            ));
559        }
560
561        if !self.is_completion_enabled() {
562            return Ok(params);
563        }
564
565        let handler = self.completion_handler.as_ref().ok_or_else(|| {
566            LspError::InternalError("Completion engine not initialized".to_string())
567        })?;
568
569        handler.handle_completion_resolve(&params).await
570    }
571
572    /// Handle code action request
573    pub async fn handle_code_action(&self, params: Value) -> LspResult<Value> {
574        if self.state != ServerState::Initialized {
575            return Err(LspError::InvalidRequest(
576                "Server is not initialized".to_string(),
577            ));
578        }
579
580        let text_document = params
581            .get("textDocument")
582            .ok_or_else(|| LspError::InvalidParams("Missing textDocument".to_string()))?;
583
584        let uri = text_document
585            .get("uri")
586            .and_then(|v| v.as_str())
587            .ok_or_else(|| LspError::InvalidParams("Missing uri".to_string()))?;
588
589        let context = params
590            .get("context")
591            .ok_or_else(|| LspError::InvalidParams("Missing context".to_string()))?;
592
593        let diagnostics_array = context
594            .get("diagnostics")
595            .and_then(|v| v.as_array())
596            .ok_or_else(|| LspError::InvalidParams("Missing diagnostics in context".to_string()))?;
597
598        // Get document content
599        let code = self
600            .get_document(uri)
601            .ok_or_else(|| LspError::InvalidParams(format!("Document not found: {}", uri)))?;
602
603        let mut actions = Vec::new();
604
605        // Generate code actions for each diagnostic
606        for diag_json in diagnostics_array {
607            // Parse diagnostic from JSON
608            if let Ok(diagnostic) =
609                serde_json::from_value::<crate::types::Diagnostic>(diag_json.clone())
610            {
611                match self
612                    .code_actions_engine
613                    .suggest_code_actions(&diagnostic, code)
614                {
615                    Ok(suggested_actions) => {
616                        for action in suggested_actions {
617                            actions.push(json!({
618                                "title": action.title,
619                                "kind": match action.kind {
620                                    crate::types::CodeActionKind::QuickFix => "quickfix",
621                                    crate::types::CodeActionKind::Refactor => "refactor",
622                                    crate::types::CodeActionKind::RefactorExtract => "refactor.extract",
623                                    crate::types::CodeActionKind::RefactorInline => "refactor.inline",
624                                    crate::types::CodeActionKind::RefactorRewrite => "refactor.rewrite",
625                                    crate::types::CodeActionKind::Source => "source",
626                                    crate::types::CodeActionKind::SourceOrganizeImports => "source.organizeImports",
627                                    crate::types::CodeActionKind::SourceFixAll => "source.fixAll",
628                                },
629                                "edit": {
630                                    "changes": action.edit.changes.iter().map(|(uri, edits)| {
631                                        (uri.clone(), edits.iter().map(|edit| {
632                                            json!({
633                                                "range": {
634                                                    "start": {
635                                                        "line": edit.range.start.line,
636                                                        "character": edit.range.start.character,
637                                                    },
638                                                    "end": {
639                                                        "line": edit.range.end.line,
640                                                        "character": edit.range.end.character,
641                                                    },
642                                                },
643                                                "newText": edit.new_text,
644                                            })
645                                        }).collect::<Vec<_>>())
646                                    }).collect::<std::collections::HashMap<_, _>>(),
647                                },
648                            }));
649                        }
650                    }
651                    Err(e) => {
652                        // Log error but continue processing other diagnostics
653                        eprintln!("Error generating code actions: {}", e);
654                    }
655                }
656            }
657        }
658
659        Ok(json!(actions))
660    }
661
662    /// Detect language from file URI
663    fn detect_language(&self, uri: &str) -> Language {
664        // Extract file extension from URI
665        if let Some(last_dot) = uri.rfind('.') {
666            let ext = &uri[last_dot + 1..];
667            Language::from_extension(ext)
668        } else {
669            Language::Unknown
670        }
671    }
672
673    /// Process a message
674    async fn process_message(&mut self, message: LspMessage) -> LspResult<Option<Value>> {
675        match message {
676            LspMessage::Request(req) => match req.method.as_str() {
677                "initialize" => {
678                    let params = req.params.unwrap_or(json!({}));
679                    self.handle_initialize(params).await
680                }
681                "shutdown" => self.handle_shutdown().await,
682                "textDocument/hover" => {
683                    let params = req.params.unwrap_or(json!({}));
684                    self.handle_hover(params).await
685                }
686                "textDocument/diagnostics" => {
687                    let params = req.params.unwrap_or(json!({}));
688                    self.handle_diagnostics(params).await
689                }
690                "textDocument/codeAction" => {
691                    let params = req.params.unwrap_or(json!({}));
692                    self.handle_code_action(params).await
693                }
694                "textDocument/completion" => {
695                    let params = req.params.unwrap_or(json!({}));
696                    self.handle_completion(params).await
697                }
698                "completionItem/resolve" => {
699                    let params = req.params.unwrap_or(json!({}));
700                    self.handle_completion_resolve(params).await
701                }
702                _ => Err(LspError::MethodNotFound(req.method)),
703            }
704            .map(Some),
705            LspMessage::Notification(notif) => {
706                match notif.method.as_str() {
707                    "initialized" => self.handle_initialized().await,
708                    "textDocument/didOpen" => {
709                        let params = notif.params.unwrap_or(json!({}));
710                        self.handle_did_open(params).await
711                    }
712                    "textDocument/didChange" => {
713                        let params = notif.params.unwrap_or(json!({}));
714                        self.handle_did_change(params).await
715                    }
716                    "textDocument/didClose" => {
717                        let params = notif.params.unwrap_or(json!({}));
718                        self.handle_did_close(params).await
719                    }
720                    "exit" => self.handle_exit().await,
721                    _ => {
722                        // Unknown notification, just ignore it
723                        Ok(())
724                    }
725                }
726                .map(|_| None)
727            }
728            LspMessage::Response(_) => {
729                // Ignore responses from client
730                Ok(None)
731            }
732        }
733    }
734
735    /// Run the server
736    pub async fn run(&mut self) -> LspResult<()> {
737        info!("LSP server started");
738
739        loop {
740            // Read message
741            match self.transport.read_message().await {
742                Ok(message) => {
743                    // Log incoming message
744                    match &message {
745                        LspMessage::Request(req) => {
746                            debug!("Received request: method={}, id={:?}", req.method, req.id);
747                        }
748                        LspMessage::Notification(notif) => {
749                            debug!("Received notification: method={}", notif.method);
750                        }
751                        LspMessage::Response(_) => {
752                            debug!("Received response");
753                        }
754                    }
755
756                    // Process message
757                    match self.process_message(message.clone()).await {
758                        Ok(Some(result)) => {
759                            // Send response
760                            if let LspMessage::Request(req) = message {
761                                debug!("Sending response for request: id={:?}", req.id);
762                                let response = JsonRpcResponse::success(req.id, result);
763                                let response_msg = LspMessage::Response(response);
764                                if let Err(e) = self.transport.write_message(&response_msg).await {
765                                    error!("Failed to send response: {}", e);
766                                }
767                            }
768                        }
769                        Ok(None) => {
770                            // Notification processed, no response needed
771                            if let LspMessage::Notification(notif) = message {
772                                debug!("Notification processed: method={}", notif.method);
773                            }
774                        }
775                        Err(err) => {
776                            // Send error response
777                            if let LspMessage::Request(req) = message {
778                                warn!("Error processing request: {}", err);
779                                let error = match err {
780                                    LspError::MethodNotFound(method) => {
781                                        error!("Method not found: {}", method);
782                                        JsonRpcError::method_not_found(method)
783                                    }
784                                    LspError::InvalidParams(msg) => {
785                                        warn!("Invalid parameters: {}", msg);
786                                        JsonRpcError::invalid_params(msg)
787                                    }
788                                    LspError::ParseError(msg) => {
789                                        error!("Parse error: {}", msg);
790                                        JsonRpcError::parse_error(msg)
791                                    }
792                                    LspError::TimeoutError(msg) => {
793                                        warn!("Timeout error: {}", msg);
794                                        JsonRpcError::internal_error(msg)
795                                    }
796                                    _ => {
797                                        error!("Internal error: {}", err);
798                                        JsonRpcError::internal_error(err.to_string())
799                                    }
800                                };
801                                let response = JsonRpcResponse::error(req.id, error);
802                                let response_msg = LspMessage::Response(response);
803                                if let Err(e) = self.transport.write_message(&response_msg).await {
804                                    error!("Failed to send error response: {}", e);
805                                }
806                            }
807                        }
808                    }
809                }
810                Err(e) => {
811                    error!("Failed to read message: {}", e);
812                    // Continue processing, don't break on read errors
813                }
814            }
815
816            // Check if server should exit
817            if self.state == ServerState::ShutDown {
818                info!("LSP server shutting down");
819                break;
820            }
821        }
822
823        info!("LSP server stopped");
824        Ok(())
825    }
826}
827
828impl Default for LspServer {
829    fn default() -> Self {
830        Self::new()
831    }
832}
833
834#[cfg(test)]
835mod tests {
836    use super::*;
837
838    #[test]
839    fn test_server_creation() {
840        let server = LspServer::new();
841        assert_eq!(server.state(), ServerState::Initializing);
842    }
843
844    #[test]
845    fn test_server_capabilities() {
846        let server = LspServer::new();
847        let caps = server.capabilities();
848        assert!(caps.hover_provider);
849        assert!(caps.code_action_provider);
850    }
851
852    #[test]
853    fn test_document_management() {
854        let mut server = LspServer::new();
855        server.set_document("file://test.rs".to_string(), "fn main() {}".to_string());
856        assert_eq!(server.get_document("file://test.rs"), Some("fn main() {}"));
857
858        server.remove_document("file://test.rs");
859        assert_eq!(server.get_document("file://test.rs"), None);
860    }
861
862    #[test]
863    fn test_server_capabilities_json() {
864        let caps = ServerCapabilities::default();
865        let json = caps.to_json();
866        assert!(json.get("textDocumentSync").is_some());
867        assert!(json.get("hoverProvider").is_some());
868    }
869
870    #[test]
871    fn test_error_handling_invalid_request() {
872        let server = LspServer::new();
873        // Server should be in Initializing state, so requests should fail
874        let result = tokio::runtime::Runtime::new()
875            .unwrap()
876            .block_on(server.handle_hover(json!({})));
877        assert!(result.is_err());
878    }
879
880    #[test]
881    fn test_error_handling_missing_params() {
882        let mut server = LspServer::new();
883        server.state = ServerState::Initialized;
884
885        // Missing textDocument parameter
886        let result = tokio::runtime::Runtime::new()
887            .unwrap()
888            .block_on(server.handle_hover(json!({})));
889        assert!(result.is_err());
890    }
891
892    #[test]
893    fn test_language_detection_rust() {
894        let server = LspServer::new();
895        let lang = server.detect_language("file://test.rs");
896        assert_eq!(lang, Language::Rust);
897    }
898
899    #[test]
900    fn test_language_detection_typescript() {
901        let server = LspServer::new();
902        let lang = server.detect_language("file://test.ts");
903        assert_eq!(lang, Language::TypeScript);
904    }
905
906    #[test]
907    fn test_language_detection_python() {
908        let server = LspServer::new();
909        let lang = server.detect_language("file://test.py");
910        assert_eq!(lang, Language::Python);
911    }
912
913    #[test]
914    fn test_language_detection_unknown() {
915        let server = LspServer::new();
916        let lang = server.detect_language("file://test.unknown");
917        assert_eq!(lang, Language::Unknown);
918    }
919
920    #[test]
921    fn test_error_handling_document_not_found() {
922        let mut server = LspServer::new();
923        server.state = ServerState::Initialized;
924
925        // Try to get hover for non-existent document
926        let result = tokio::runtime::Runtime::new()
927            .unwrap()
928            .block_on(server.handle_hover(json!({
929                "textDocument": { "uri": "file://nonexistent.rs" },
930                "position": { "line": 0, "character": 0 }
931            })));
932        assert!(result.is_err());
933    }
934
935    #[test]
936    fn test_diagnostics_handler_integration() {
937        let mut server = LspServer::new();
938        server.state = ServerState::Initialized;
939
940        // Add a document
941        server.set_document("file://test.rs".to_string(), "fn main() {}".to_string());
942
943        // Request diagnostics
944        let result = tokio::runtime::Runtime::new()
945            .unwrap()
946            .block_on(server.handle_diagnostics(json!({
947                "textDocument": { "uri": "file://test.rs" }
948            })));
949
950        // Should succeed and return a JSON array
951        assert!(result.is_ok());
952        let response = result.unwrap();
953        assert!(response.is_array());
954    }
955
956    #[test]
957    fn test_code_action_handler_integration() {
958        let mut server = LspServer::new();
959        server.state = ServerState::Initialized;
960
961        // Add a document
962        server.set_document("file://test.rs".to_string(), "fn main() {}".to_string());
963
964        // Request code actions
965        let result = tokio::runtime::Runtime::new()
966            .unwrap()
967            .block_on(server.handle_code_action(json!({
968                "textDocument": { "uri": "file://test.rs" },
969                "context": { "diagnostics": [] }
970            })));
971
972        // Should succeed and return a JSON array
973        assert!(result.is_ok());
974        let response = result.unwrap();
975        assert!(response.is_array());
976    }
977
978    #[test]
979    fn test_hover_handler_integration() {
980        let mut server = LspServer::new();
981        server.state = ServerState::Initialized;
982
983        // Add a document
984        server.set_document("file://test.rs".to_string(), "fn main() {}".to_string());
985
986        // Request hover information
987        let result = tokio::runtime::Runtime::new()
988            .unwrap()
989            .block_on(server.handle_hover(json!({
990                "textDocument": { "uri": "file://test.rs" },
991                "position": { "line": 0, "character": 0 }
992            })));
993
994        // Should succeed (may return null if no symbol at position)
995        assert!(result.is_ok());
996    }
997
998    #[test]
999    fn test_diagnostics_with_language_detection() {
1000        let mut server = LspServer::new();
1001        server.state = ServerState::Initialized;
1002
1003        // Add Rust document
1004        server.set_document("file://test.rs".to_string(), "fn main() {}".to_string());
1005
1006        // Request diagnostics for Rust file
1007        let result = tokio::runtime::Runtime::new()
1008            .unwrap()
1009            .block_on(server.handle_diagnostics(json!({
1010                "textDocument": { "uri": "file://test.rs" }
1011            })));
1012
1013        assert!(result.is_ok());
1014
1015        // Add Python document
1016        server.set_document("file://test.py".to_string(), "def main(): pass".to_string());
1017
1018        // Request diagnostics for Python file
1019        let result = tokio::runtime::Runtime::new()
1020            .unwrap()
1021            .block_on(server.handle_diagnostics(json!({
1022                "textDocument": { "uri": "file://test.py" }
1023            })));
1024
1025        assert!(result.is_ok());
1026    }
1027}