1use super::protocol::*;
6use super::server::{LanguageServer, LanguageServerManager};
7use super::types::*;
8use super::{LspError, LspResult};
9
10use super::protocol::{create_implementation_request, create_type_definition_request};
12use std::collections::HashMap;
13use std::io::{BufRead, Write};
14use std::path::{Path, PathBuf};
15use std::sync::atomic::{AtomicI64, Ordering};
16use std::sync::Arc;
17use tokio::sync::RwLock;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum ConnectionState {
22 Disconnected,
24 Initializing,
26 Ready,
28 Shutdown,
30 Error,
32}
33
34#[allow(dead_code)]
36struct ServerConnection {
37 language: String,
39 root_path: PathBuf,
41 stdin: std::process::ChildStdin,
43 stdout: std::io::BufReader<std::process::ChildStdout>,
45 capabilities: Option<ServerCapabilities>,
47 doc_versions: HashMap<String, i32>,
49 open_docs: HashMap<String, String>,
51}
52
53pub struct LspClient {
55 manager: Arc<LanguageServerManager>,
57 request_id: AtomicI64,
59 connections: Arc<RwLock<HashMap<String, ServerConnection>>>,
61 states: Arc<RwLock<HashMap<String, ConnectionState>>>,
63 timeout_ms: u64,
65}
66
67impl LspClient {
68 pub fn new() -> Self {
70 Self {
71 manager: Arc::new(LanguageServerManager::new()),
72 request_id: AtomicI64::new(1),
73 connections: Arc::new(RwLock::new(HashMap::new())),
74 states: Arc::new(RwLock::new(HashMap::new())),
75 timeout_ms: 30000,
76 }
77 }
78
79 pub fn with_timeout(mut self, timeout_ms: u64) -> Self {
81 self.timeout_ms = timeout_ms;
82 self
83 }
84
85 fn next_id(&self) -> RequestId {
87 RequestId::Number(self.request_id.fetch_add(1, Ordering::SeqCst))
88 }
89
90 pub async fn get_state(&self, language: &str) -> ConnectionState {
92 self.states
93 .read()
94 .await
95 .get(language)
96 .copied()
97 .unwrap_or(ConnectionState::Disconnected)
98 }
99
100 pub async fn is_connected(&self, language: &str) -> bool {
102 self.get_state(language).await == ConnectionState::Ready
103 }
104
105 pub async fn initialize(
107 &self,
108 language: &str,
109 root_path: &Path,
110 ) -> LspResult<InitializeResult> {
111 let root_path = root_path
112 .canonicalize()
113 .unwrap_or_else(|_| root_path.to_path_buf());
114
115 self.states
117 .write()
118 .await
119 .insert(language.to_string(), ConnectionState::Initializing);
120
121 let config = self
123 .manager
124 .get_config(language)
125 .ok_or_else(|| LspError::ServerNotFound(language.to_string()))?
126 .clone();
127
128 let mut server = LanguageServer::new(config.clone(), root_path.clone())?;
130 server.start()?;
131
132 let stdin = server
134 .process
135 .as_mut()
136 .and_then(|p| p.stdin.take())
137 .ok_or_else(|| LspError::RequestFailed("Failed to get stdin".to_string()))?;
138
139 let stdout = server
140 .process
141 .as_mut()
142 .and_then(|p| p.stdout.take())
143 .ok_or_else(|| LspError::RequestFailed("Failed to get stdout".to_string()))?;
144
145 let root_uri = self.path_to_uri(&root_path);
147 let request = create_initialize_request(
148 self.next_id(),
149 Some(root_uri),
150 "continuum",
151 env!("CARGO_PKG_VERSION"),
152 );
153
154 let mut writer = std::io::BufWriter::new(stdin);
156 let mut reader = std::io::BufReader::new(stdout);
157
158 write_message(&mut writer, &request)?;
159 writer.flush().map_err(LspError::Io)?;
160
161 let response = self.read_response::<InitializeResult>(&mut reader)?;
163
164 let initialized_notification = LspNotification {
166 jsonrpc: "2.0".to_string(),
167 method: "initialized".to_string(),
168 params: Some(serde_json::json!({})),
169 };
170 write_message(&mut writer, &initialized_notification)?;
171 writer.flush().map_err(LspError::Io)?;
172
173 let connection = ServerConnection {
175 language: language.to_string(),
176 root_path: root_path.clone(),
177 stdin: writer
178 .into_inner()
179 .map_err(|e| LspError::RequestFailed(format!("Failed to get stdin: {}", e)))?,
180 stdout: reader,
181 capabilities: Some(
182 response
183 .result
184 .clone()
185 .map(|r| r.capabilities)
186 .unwrap_or_default(),
187 ),
188 doc_versions: HashMap::new(),
189 open_docs: HashMap::new(),
190 };
191
192 self.connections
193 .write()
194 .await
195 .insert(language.to_string(), connection);
196 self.states
197 .write()
198 .await
199 .insert(language.to_string(), ConnectionState::Ready);
200
201 self.manager
203 .servers
204 .lock()
205 .await
206 .insert(language.to_string(), server);
207
208 Ok(response.result.unwrap_or_else(|| InitializeResult {
209 capabilities: ServerCapabilities::default(),
210 server_info: None,
211 }))
212 }
213
214 pub async fn open_document(&self, language: &str, file_path: &Path) -> LspResult<()> {
216 let mut connections = self.connections.write().await;
217 let conn = connections.get_mut(language).ok_or_else(|| {
218 LspError::RequestFailed("Language server not initialized".to_string())
219 })?;
220
221 let uri = self.path_to_uri(file_path);
222
223 if conn.open_docs.contains_key(&uri) {
225 return Ok(());
226 }
227
228 let content = std::fs::read_to_string(file_path)
230 .map_err(|e| LspError::RequestFailed(format!("Failed to read file: {}", e)))?;
231
232 let version = conn.doc_versions.entry(uri.clone()).or_insert(0);
233 *version += 1;
234
235 let notification = create_did_open_notification(uri.clone(), language, *version, &content);
237
238 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
239 write_message(&mut writer, ¬ification)?;
240 writer.flush().map_err(LspError::Io)?;
241
242 conn.open_docs.insert(uri, content);
243
244 Ok(())
245 }
246
247 pub async fn close_document(&self, language: &str, file_path: &Path) -> LspResult<()> {
249 let mut connections = self.connections.write().await;
250 let conn = connections.get_mut(language).ok_or_else(|| {
251 LspError::RequestFailed("Language server not initialized".to_string())
252 })?;
253
254 let uri = self.path_to_uri(file_path);
255
256 let notification = create_did_close_notification(uri.clone());
258
259 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
260 write_message(&mut writer, ¬ification)?;
261 writer.flush().map_err(LspError::Io)?;
262
263 conn.open_docs.remove(&uri);
264 conn.doc_versions.remove(&uri);
265
266 Ok(())
267 }
268
269 pub async fn go_to_definition(
271 &self,
272 language: &str,
273 file_path: &Path,
274 position: Position,
275 ) -> LspResult<Vec<Location>> {
276 self.open_document(language, file_path).await?;
278
279 let mut connections = self.connections.write().await;
280 let conn = connections.get_mut(language).ok_or_else(|| {
281 LspError::RequestFailed("Language server not initialized".to_string())
282 })?;
283
284 if !conn
286 .capabilities
287 .as_ref()
288 .map(|c| c.definition_provider.unwrap_or(false))
289 .unwrap_or(false)
290 {
291 return Err(LspError::RequestFailed(
292 "Server does not support go to definition".to_string(),
293 ));
294 }
295
296 let uri = self.path_to_uri(file_path);
297 let request = create_definition_request(self.next_id(), uri, position);
298
299 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
300 write_message(&mut writer, &request)?;
301 writer.flush().map_err(LspError::Io)?;
302
303 let response = self.read_response::<Option<DefinitionResult>>(&mut conn.stdout)?;
305
306 match response.result {
307 Some(Some(result)) => Ok(result.to_locations()),
308 Some(None) => Ok(Vec::new()),
309 None => Ok(Vec::new()),
310 }
311 }
312
313 pub async fn find_references(
315 &self,
316 language: &str,
317 file_path: &Path,
318 position: Position,
319 include_declaration: bool,
320 ) -> LspResult<Vec<Location>> {
321 self.open_document(language, file_path).await?;
323
324 let mut connections = self.connections.write().await;
325 let conn = connections.get_mut(language).ok_or_else(|| {
326 LspError::RequestFailed("Language server not initialized".to_string())
327 })?;
328
329 if !conn
331 .capabilities
332 .as_ref()
333 .map(|c| c.references_provider.unwrap_or(false))
334 .unwrap_or(false)
335 {
336 return Err(LspError::RequestFailed(
337 "Server does not support find references".to_string(),
338 ));
339 }
340
341 let uri = self.path_to_uri(file_path);
342 let request = create_references_request(self.next_id(), uri, position, include_declaration);
343
344 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
345 write_message(&mut writer, &request)?;
346 writer.flush().map_err(LspError::Io)?;
347
348 let response = self.read_response::<Option<ReferenceResult>>(&mut conn.stdout)?;
350
351 Ok(response.result.flatten().unwrap_or_default())
352 }
353
354 pub async fn get_hover(
356 &self,
357 language: &str,
358 file_path: &Path,
359 position: Position,
360 ) -> LspResult<Option<Hover>> {
361 self.open_document(language, file_path).await?;
363
364 let mut connections = self.connections.write().await;
365 let conn = connections.get_mut(language).ok_or_else(|| {
366 LspError::RequestFailed("Language server not initialized".to_string())
367 })?;
368
369 if !conn
371 .capabilities
372 .as_ref()
373 .map(|c| c.hover_provider.unwrap_or(false))
374 .unwrap_or(false)
375 {
376 return Ok(None);
377 }
378
379 let uri = self.path_to_uri(file_path);
380 let request = create_hover_request(self.next_id(), uri, position);
381
382 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
383 write_message(&mut writer, &request)?;
384 writer.flush().map_err(LspError::Io)?;
385
386 let response = self.read_response::<Option<Hover>>(&mut conn.stdout)?;
388
389 Ok(response.result.flatten())
390 }
391
392 pub async fn rename_symbol(
394 &self,
395 language: &str,
396 file_path: &Path,
397 position: Position,
398 new_name: &str,
399 ) -> LspResult<Option<WorkspaceEdit>> {
400 self.open_document(language, file_path).await?;
402
403 let mut connections = self.connections.write().await;
404 let conn = connections.get_mut(language).ok_or_else(|| {
405 LspError::RequestFailed("Language server not initialized".to_string())
406 })?;
407
408 if conn
410 .capabilities
411 .as_ref()
412 .map(|c| c.rename_provider.is_none())
413 .unwrap_or(true)
414 {
415 return Err(LspError::RequestFailed(
416 "Server does not support rename".to_string(),
417 ));
418 }
419
420 let uri = self.path_to_uri(file_path);
421 let request = create_rename_request(self.next_id(), uri, position, new_name);
422
423 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
424 write_message(&mut writer, &request)?;
425 writer.flush().map_err(LspError::Io)?;
426
427 let response = self.read_response::<Option<RenameResult>>(&mut conn.stdout)?;
429
430 Ok(response.result.flatten())
431 }
432
433 pub async fn get_document_symbols(
435 &self,
436 language: &str,
437 file_path: &Path,
438 ) -> LspResult<Vec<DocumentSymbol>> {
439 self.open_document(language, file_path).await?;
441
442 let mut connections = self.connections.write().await;
443 let conn = connections.get_mut(language).ok_or_else(|| {
444 LspError::RequestFailed("Language server not initialized".to_string())
445 })?;
446
447 if !conn
449 .capabilities
450 .as_ref()
451 .map(|c| c.document_symbol_provider.unwrap_or(false))
452 .unwrap_or(false)
453 {
454 return Ok(Vec::new());
455 }
456
457 let uri = self.path_to_uri(file_path);
458 let request = create_document_symbol_request(self.next_id(), uri);
459
460 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
461 write_message(&mut writer, &request)?;
462 writer.flush().map_err(LspError::Io)?;
463
464 let response = self.read_response::<Option<Vec<DocumentSymbol>>>(&mut conn.stdout)?;
466
467 Ok(response.result.flatten().unwrap_or_default())
468 }
469
470 pub async fn get_workspace_symbols(
472 &self,
473 language: &str,
474 query: &str,
475 ) -> LspResult<Vec<SymbolInformation>> {
476 let mut connections = self.connections.write().await;
477 let conn = connections.get_mut(language).ok_or_else(|| {
478 LspError::RequestFailed("Language server not initialized".to_string())
479 })?;
480
481 if !conn
483 .capabilities
484 .as_ref()
485 .map(|c| c.workspace_symbol_provider.unwrap_or(false))
486 .unwrap_or(false)
487 {
488 return Ok(Vec::new());
489 }
490
491 let request = create_workspace_symbol_request(self.next_id(), query);
492
493 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
494 write_message(&mut writer, &request)?;
495 writer.flush().map_err(LspError::Io)?;
496
497 let response = self.read_response::<Option<Vec<SymbolInformation>>>(&mut conn.stdout)?;
499
500 Ok(response.result.flatten().unwrap_or_default())
501 }
502
503 pub async fn get_code_actions(
505 &self,
506 language: &str,
507 file_path: &Path,
508 range: Range,
509 diagnostics: Vec<Diagnostic>,
510 only: Option<Vec<CodeActionKind>>,
511 ) -> LspResult<Vec<CodeAction>> {
512 self.open_document(language, file_path).await?;
514
515 let mut connections = self.connections.write().await;
516 let conn = connections.get_mut(language).ok_or_else(|| {
517 LspError::RequestFailed("Language server not initialized".to_string())
518 })?;
519
520 if conn
522 .capabilities
523 .as_ref()
524 .map(|c| c.code_action_provider.is_none())
525 .unwrap_or(true)
526 {
527 return Ok(Vec::new());
528 }
529
530 let uri = self.path_to_uri(file_path);
531 let request = create_code_action_request(
532 self.next_id(),
533 uri,
534 range,
535 CodeActionContext { diagnostics, only },
536 );
537
538 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
539 write_message(&mut writer, &request)?;
540 writer.flush().map_err(LspError::Io)?;
541
542 let response = self.read_response::<Option<Vec<CodeAction>>>(&mut conn.stdout)?;
544
545 Ok(response.result.flatten().unwrap_or_default())
546 }
547
548 pub async fn get_signature_help(
550 &self,
551 language: &str,
552 file_path: &Path,
553 position: Position,
554 ) -> LspResult<Option<SignatureHelp>> {
555 self.open_document(language, file_path).await?;
557
558 let mut connections = self.connections.write().await;
559 let conn = connections.get_mut(language).ok_or_else(|| {
560 LspError::RequestFailed("Language server not initialized".to_string())
561 })?;
562
563 if conn
565 .capabilities
566 .as_ref()
567 .map(|c| c.signature_help_provider.is_none())
568 .unwrap_or(true)
569 {
570 return Ok(None);
571 }
572
573 let uri = self.path_to_uri(file_path);
574 let request = create_signature_help_request(self.next_id(), uri, position);
575
576 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
577 write_message(&mut writer, &request)?;
578 writer.flush().map_err(LspError::Io)?;
579
580 let response = self.read_response::<Option<SignatureHelp>>(&mut conn.stdout)?;
582
583 Ok(response.result.flatten())
584 }
585
586 pub async fn get_document_highlights(
588 &self,
589 language: &str,
590 file_path: &Path,
591 position: Position,
592 ) -> LspResult<Vec<DocumentHighlight>> {
593 self.open_document(language, file_path).await?;
595
596 let mut connections = self.connections.write().await;
597 let conn = connections.get_mut(language).ok_or_else(|| {
598 LspError::RequestFailed("Language server not initialized".to_string())
599 })?;
600
601 let uri = self.path_to_uri(file_path);
602 let request = create_document_highlight_request(self.next_id(), uri, position);
603
604 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
605 write_message(&mut writer, &request)?;
606 writer.flush().map_err(LspError::Io)?;
607
608 let response = self.read_response::<Option<Vec<DocumentHighlight>>>(&mut conn.stdout)?;
610
611 Ok(response.result.flatten().unwrap_or_default())
612 }
613
614 pub async fn format_document(
616 &self,
617 language: &str,
618 file_path: &Path,
619 tab_size: u32,
620 insert_spaces: bool,
621 ) -> LspResult<Vec<TextEdit>> {
622 self.open_document(language, file_path).await?;
624
625 let mut connections = self.connections.write().await;
626 let conn = connections.get_mut(language).ok_or_else(|| {
627 LspError::RequestFailed("Language server not initialized".to_string())
628 })?;
629
630 if !conn
632 .capabilities
633 .as_ref()
634 .map(|c| c.document_formatting_provider.unwrap_or(false))
635 .unwrap_or(false)
636 {
637 return Ok(Vec::new());
638 }
639
640 let uri = self.path_to_uri(file_path);
641 let request = create_formatting_request(
642 self.next_id(),
643 uri,
644 FormattingOptions {
645 tab_size,
646 insert_spaces,
647 trim_trailing_whitespace: Some(true),
648 insert_final_newline: Some(true),
649 trim_final_newlines: Some(true),
650 },
651 );
652
653 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
654 write_message(&mut writer, &request)?;
655 writer.flush().map_err(LspError::Io)?;
656
657 let response = self.read_response::<Option<Vec<TextEdit>>>(&mut conn.stdout)?;
659
660 Ok(response.result.flatten().unwrap_or_default())
661 }
662
663 pub async fn get_completions(
665 &self,
666 language: &str,
667 file_path: &Path,
668 position: Position,
669 ) -> LspResult<Vec<CompletionItem>> {
670 self.open_document(language, file_path).await?;
672
673 let mut connections = self.connections.write().await;
674 let conn = connections.get_mut(language).ok_or_else(|| {
675 LspError::RequestFailed("Language server not initialized".to_string())
676 })?;
677
678 if conn
680 .capabilities
681 .as_ref()
682 .map(|c| c.completion_provider.is_none())
683 .unwrap_or(true)
684 {
685 return Ok(Vec::new());
686 }
687
688 let uri = self.path_to_uri(file_path);
689 let request = create_completion_request(self.next_id(), uri, position);
690
691 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
692 write_message(&mut writer, &request)?;
693 writer.flush().map_err(LspError::Io)?;
694
695 let response = self.read_response::<Option<CompletionResult>>(&mut conn.stdout)?;
697
698 match response.result {
699 Some(Some(CompletionResult::List(list))) => Ok(list.items),
700 Some(Some(CompletionResult::Items(items))) => Ok(items),
701 _ => Ok(Vec::new()),
702 }
703 }
704
705 pub async fn go_to_implementation(
707 &self,
708 language: &str,
709 file_path: &Path,
710 position: Position,
711 ) -> LspResult<Vec<Location>> {
712 self.open_document(language, file_path).await?;
714
715 let mut connections = self.connections.write().await;
716 let conn = connections.get_mut(language).ok_or_else(|| {
717 LspError::RequestFailed("Language server not initialized".to_string())
718 })?;
719
720 if !conn
722 .capabilities
723 .as_ref()
724 .map(|c| c.implementation_provider.unwrap_or(false))
725 .unwrap_or(false)
726 {
727 return Ok(Vec::new());
728 }
729
730 let uri = self.path_to_uri(file_path);
731 let request = create_implementation_request(self.next_id(), uri, position);
732
733 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
734 write_message(&mut writer, &request)?;
735 writer.flush().map_err(LspError::Io)?;
736
737 let response = self.read_response::<Option<DefinitionResult>>(&mut conn.stdout)?;
739
740 match response.result {
741 Some(Some(result)) => Ok(result.to_locations()),
742 _ => Ok(Vec::new()),
743 }
744 }
745
746 pub async fn go_to_type_definition(
748 &self,
749 language: &str,
750 file_path: &Path,
751 position: Position,
752 ) -> LspResult<Option<Location>> {
753 self.open_document(language, file_path).await?;
755
756 let mut connections = self.connections.write().await;
757 let conn = connections.get_mut(language).ok_or_else(|| {
758 LspError::RequestFailed("Language server not initialized".to_string())
759 })?;
760
761 if !conn
763 .capabilities
764 .as_ref()
765 .map(|c| c.type_definition_provider.unwrap_or(false))
766 .unwrap_or(false)
767 {
768 return Ok(None);
769 }
770
771 let uri = self.path_to_uri(file_path);
772 let request = create_type_definition_request(self.next_id(), uri, position);
773
774 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
775 write_message(&mut writer, &request)?;
776 writer.flush().map_err(LspError::Io)?;
777
778 let response = self.read_response::<Option<DefinitionResult>>(&mut conn.stdout)?;
780
781 match response.result {
782 Some(Some(DefinitionResult::Single(loc))) => Ok(Some(loc)),
783 Some(Some(DefinitionResult::Multiple(locs))) => Ok(locs.first().cloned()),
784 Some(Some(DefinitionResult::Link(link))) => Ok(Some(Location {
785 uri: link.target_uri,
786 range: link.target_selection_range,
787 })),
788 Some(Some(DefinitionResult::Links(links))) => Ok(links.first().map(|link| Location {
789 uri: link.target_uri.clone(),
790 range: link.target_selection_range.clone(),
791 })),
792 _ => Ok(None),
793 }
794 }
795
796 pub async fn shutdown(&self, language: &str) -> LspResult<()> {
798 let mut states = self.states.write().await;
799 states.insert(language.to_string(), ConnectionState::Shutdown);
800
801 {
803 let mut connections = self.connections.write().await;
804 if let Some(conn) = connections.get_mut(language) {
805 let request = LspRequest {
806 jsonrpc: "2.0".to_string(),
807 id: self.next_id(),
808 method: "shutdown".to_string(),
809 params: None::<()>,
810 };
811
812 let mut writer = std::io::BufWriter::new(&mut conn.stdin);
813 write_message(&mut writer, &request)?;
814 writer.flush().ok();
815
816 let notification = LspNotification {
818 jsonrpc: "2.0".to_string(),
819 method: "exit".to_string(),
820 params: None::<()>,
821 };
822 write_message(&mut writer, ¬ification)?;
823 writer.flush().ok();
824 }
825 connections.remove(language);
826 }
827
828 self.manager.stop_server(language).await?;
829
830 Ok(())
831 }
832
833 pub async fn shutdown_all(&self) -> LspResult<()> {
835 let languages: Vec<String> = self.connections.read().await.keys().cloned().collect();
836 for lang in languages {
837 self.shutdown(&lang).await.ok();
838 }
839 Ok(())
840 }
841
842 fn path_to_uri(&self, path: &Path) -> String {
844 let path = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
845 let path_str = path.display().to_string();
846 if cfg!(windows) {
847 format!("file:///{}", path_str.replace('\\', "/"))
848 } else {
849 format!("file://{}", path_str)
850 }
851 }
852
853 #[allow(dead_code)]
855 fn uri_to_path(&self, uri: &str) -> PathBuf {
856 let path = uri.strip_prefix("file://").unwrap_or(uri);
857 let path = path.strip_prefix('/').unwrap_or(path);
858 if cfg!(windows) {
859 PathBuf::from(path.replace('/', "\\"))
860 } else {
861 PathBuf::from(path)
862 }
863 }
864
865 fn read_response<R: serde::de::DeserializeOwned>(
867 &self,
868 reader: &mut impl BufRead,
869 ) -> LspResult<LspResponse<R>> {
870 let message = read_message(reader)?;
871
872 match message {
873 LspMessage::Response(response) => {
874 let response: LspResponse<R> =
875 serde_json::from_value(serde_json::to_value(response)?)?;
876 if let Some(error) = &response.error {
877 return Err(LspError::RequestFailed(format!(
878 "LSP error {}: {}",
879 error.code, error.message
880 )));
881 }
882 Ok(response)
883 }
884 LspMessage::Request(_) => Err(LspError::InvalidMessage(
885 "Expected response, got request".to_string(),
886 )),
887 LspMessage::Notification(_) => {
888 self.read_response(reader)
890 }
891 }
892 }
893
894 pub fn get_language_from_extension(ext: &str) -> Option<&'static str> {
896 LanguageServerManager::get_language_from_extension(ext)
897 }
898
899 pub fn manager(&self) -> Arc<LanguageServerManager> {
901 self.manager.clone()
902 }
903}
904
905impl Default for LspClient {
906 fn default() -> Self {
907 Self::new()
908 }
909}
910
911pub struct SyncLspClient {
917 inner: LspClient,
918 runtime: tokio::runtime::Runtime,
919}
920
921impl SyncLspClient {
922 pub fn new() -> Self {
923 Self {
924 inner: LspClient::new(),
925 runtime: tokio::runtime::Runtime::new().expect("Failed to create tokio runtime"),
926 }
927 }
928
929 pub fn initialize(&self, language: &str, root_path: &Path) -> LspResult<InitializeResult> {
930 self.runtime
931 .block_on(self.inner.initialize(language, root_path))
932 }
933
934 pub fn go_to_definition(
935 &self,
936 language: &str,
937 file_path: &Path,
938 position: Position,
939 ) -> LspResult<Vec<Location>> {
940 self.runtime
941 .block_on(self.inner.go_to_definition(language, file_path, position))
942 }
943
944 pub fn find_references(
945 &self,
946 language: &str,
947 file_path: &Path,
948 position: Position,
949 include_declaration: bool,
950 ) -> LspResult<Vec<Location>> {
951 self.runtime.block_on(self.inner.find_references(
952 language,
953 file_path,
954 position,
955 include_declaration,
956 ))
957 }
958
959 pub fn get_hover(
960 &self,
961 language: &str,
962 file_path: &Path,
963 position: Position,
964 ) -> LspResult<Option<Hover>> {
965 self.runtime
966 .block_on(self.inner.get_hover(language, file_path, position))
967 }
968
969 pub fn rename_symbol(
970 &self,
971 language: &str,
972 file_path: &Path,
973 position: Position,
974 new_name: &str,
975 ) -> LspResult<Option<WorkspaceEdit>> {
976 self.runtime.block_on(
977 self.inner
978 .rename_symbol(language, file_path, position, new_name),
979 )
980 }
981
982 pub fn get_document_symbols(
983 &self,
984 language: &str,
985 file_path: &Path,
986 ) -> LspResult<Vec<DocumentSymbol>> {
987 self.runtime
988 .block_on(self.inner.get_document_symbols(language, file_path))
989 }
990
991 pub fn open_document(&self, language: &str, file_path: &Path) -> LspResult<()> {
992 self.runtime
993 .block_on(self.inner.open_document(language, file_path))
994 }
995
996 pub fn close_document(&self, language: &str, file_path: &Path) -> LspResult<()> {
997 self.runtime
998 .block_on(self.inner.close_document(language, file_path))
999 }
1000
1001 pub fn shutdown(&self, language: &str) -> LspResult<()> {
1002 self.runtime.block_on(self.inner.shutdown(language))
1003 }
1004
1005 pub fn shutdown_all(&self) -> LspResult<()> {
1006 self.runtime.block_on(self.inner.shutdown_all())
1007 }
1008
1009 pub fn is_connected(&self, language: &str) -> bool {
1010 self.runtime.block_on(self.inner.is_connected(language))
1011 }
1012
1013 pub fn get_workspace_symbols(
1014 &self,
1015 language: &str,
1016 query: &str,
1017 ) -> LspResult<Vec<SymbolInformation>> {
1018 self.runtime
1019 .block_on(self.inner.get_workspace_symbols(language, query))
1020 }
1021
1022 pub fn get_code_actions(
1023 &self,
1024 language: &str,
1025 file_path: &Path,
1026 range: Range,
1027 diagnostics: Vec<Diagnostic>,
1028 only: Option<Vec<CodeActionKind>>,
1029 ) -> LspResult<Vec<CodeAction>> {
1030 self.runtime.block_on(self.inner.get_code_actions(
1031 language,
1032 file_path,
1033 range,
1034 diagnostics,
1035 only,
1036 ))
1037 }
1038
1039 pub fn get_signature_help(
1040 &self,
1041 language: &str,
1042 file_path: &Path,
1043 position: Position,
1044 ) -> LspResult<Option<SignatureHelp>> {
1045 self.runtime
1046 .block_on(self.inner.get_signature_help(language, file_path, position))
1047 }
1048
1049 pub fn get_document_highlights(
1050 &self,
1051 language: &str,
1052 file_path: &Path,
1053 position: Position,
1054 ) -> LspResult<Vec<DocumentHighlight>> {
1055 self.runtime.block_on(
1056 self.inner
1057 .get_document_highlights(language, file_path, position),
1058 )
1059 }
1060
1061 pub fn format_document(
1062 &self,
1063 language: &str,
1064 file_path: &Path,
1065 tab_size: u32,
1066 insert_spaces: bool,
1067 ) -> LspResult<Vec<TextEdit>> {
1068 self.runtime.block_on(self.inner.format_document(
1069 language,
1070 file_path,
1071 tab_size,
1072 insert_spaces,
1073 ))
1074 }
1075
1076 pub fn get_completions(
1077 &self,
1078 language: &str,
1079 file_path: &Path,
1080 position: Position,
1081 ) -> LspResult<Vec<CompletionItem>> {
1082 self.runtime
1083 .block_on(self.inner.get_completions(language, file_path, position))
1084 }
1085
1086 pub fn go_to_implementation(
1087 &self,
1088 language: &str,
1089 file_path: &Path,
1090 position: Position,
1091 ) -> LspResult<Vec<Location>> {
1092 self.runtime.block_on(
1093 self.inner
1094 .go_to_implementation(language, file_path, position),
1095 )
1096 }
1097
1098 pub fn go_to_type_definition(
1099 &self,
1100 language: &str,
1101 file_path: &Path,
1102 position: Position,
1103 ) -> LspResult<Option<Location>> {
1104 self.runtime.block_on(
1105 self.inner
1106 .go_to_type_definition(language, file_path, position),
1107 )
1108 }
1109}
1110
1111impl Default for SyncLspClient {
1112 fn default() -> Self {
1113 Self::new()
1114 }
1115}
1116
1117pub async fn quick_go_to_definition(
1123 file_path: &Path,
1124 position: Position,
1125) -> LspResult<Vec<Location>> {
1126 let ext = file_path
1127 .extension()
1128 .and_then(|e| e.to_str())
1129 .ok_or_else(|| LspError::RequestFailed("Unknown file extension".to_string()))?;
1130
1131 let language = LanguageServerManager::get_language_from_extension(ext)
1132 .ok_or_else(|| LspError::ServerNotFound(format!("No LSP server for extension: {}", ext)))?;
1133
1134 let client = LspClient::new();
1135 let root_path = file_path.parent().unwrap_or(Path::new("."));
1136
1137 client.initialize(language, root_path).await?;
1138 let result = client.go_to_definition(language, file_path, position).await;
1139 client.shutdown(language).await?;
1140
1141 result
1142}
1143
1144pub async fn quick_find_references(
1146 file_path: &Path,
1147 position: Position,
1148 include_declaration: bool,
1149) -> LspResult<Vec<Location>> {
1150 let ext = file_path
1151 .extension()
1152 .and_then(|e| e.to_str())
1153 .ok_or_else(|| LspError::RequestFailed("Unknown file extension".to_string()))?;
1154
1155 let language = LanguageServerManager::get_language_from_extension(ext)
1156 .ok_or_else(|| LspError::ServerNotFound(format!("No LSP server for extension: {}", ext)))?;
1157
1158 let client = LspClient::new();
1159 let root_path = file_path.parent().unwrap_or(Path::new("."));
1160
1161 client.initialize(language, root_path).await?;
1162 let result = client
1163 .find_references(language, file_path, position, include_declaration)
1164 .await;
1165 client.shutdown(language).await?;
1166
1167 result
1168}
1169
1170#[cfg(test)]
1171mod tests {
1172 use super::*;
1173
1174 #[test]
1175 fn test_client_creation() {
1176 let client = LspClient::new();
1177 assert!(client.manager().get_config("rust").is_some());
1178 }
1179
1180 #[test]
1181 fn test_sync_client_creation() {
1182 let client = SyncLspClient::new();
1183 assert!(!client.is_connected("rust"));
1184 }
1185
1186 #[test]
1187 fn test_request_id_increment() {
1188 let client = LspClient::new();
1189 let id1 = client.next_id();
1190 let id2 = client.next_id();
1191 assert_ne!(id1, id2);
1192 }
1193
1194 #[test]
1195 fn test_path_to_uri() {
1196 let client = LspClient::new();
1197 let path = Path::new("/home/user/test.rs");
1198 let uri = client.path_to_uri(path);
1199 assert!(uri.starts_with("file://"));
1200 }
1201
1202 #[test]
1203 fn test_connection_state() {
1204 use tokio::runtime::Runtime;
1205 let rt = Runtime::new().unwrap();
1206 let client = LspClient::new();
1207
1208 rt.block_on(async {
1209 let state = client.get_state("rust").await;
1210 assert_eq!(state, ConnectionState::Disconnected);
1211 });
1212 }
1213}