1use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::collections::HashMap;
8use std::io::Write;
9use std::process::{Child, Command, Stdio};
10use std::sync::atomic::{AtomicU64, Ordering};
11use std::sync::Arc;
12use tokio::sync::{broadcast, Mutex, RwLock};
13
14use super::types::*;
15
16pub(crate) type LspResponseSender = tokio::sync::oneshot::Sender<Result<Value, LspError>>;
18
19pub(crate) type PendingRequestsMap = Arc<Mutex<HashMap<u64, LspResponseSender>>>;
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct LspMessage {
25 pub jsonrpc: String,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub id: Option<u64>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub method: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub params: Option<Value>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub result: Option<Value>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub error: Option<LspError>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct LspError {
41 pub code: i32,
42 pub message: String,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub data: Option<Value>,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum LspServerState {
50 Stopped,
51 Starting,
52 Running,
53 Error,
54}
55
56#[derive(Debug, Clone)]
58pub struct LspClientConfig {
59 pub command: String,
61 pub args: Vec<String>,
63 pub root_uri: Option<String>,
65 pub initialization_options: Option<Value>,
67}
68
69#[derive(Debug, Clone)]
71pub enum LspClientEvent {
72 StateChange(LspServerState),
73 Notification { method: String, params: Value },
74 Error(String),
75}
76
77pub struct LspClient {
79 language: String,
80 config: LspClientConfig,
81 state: Arc<RwLock<LspServerState>>,
82 process: Arc<Mutex<Option<Child>>>,
83 message_id: AtomicU64,
84 pending_requests: PendingRequestsMap,
85 capabilities: Arc<RwLock<Option<Value>>>,
86 event_sender: broadcast::Sender<LspClientEvent>,
87}
88
89impl LspClient {
90 pub fn new(language: impl Into<String>, config: LspClientConfig) -> Self {
92 let (event_sender, _) = broadcast::channel(64);
93 Self {
94 language: language.into(),
95 config,
96 state: Arc::new(RwLock::new(LspServerState::Stopped)),
97 process: Arc::new(Mutex::new(None)),
98 message_id: AtomicU64::new(0),
99 pending_requests: Arc::new(Mutex::new(HashMap::new())),
100 capabilities: Arc::new(RwLock::new(None)),
101 event_sender,
102 }
103 }
104
105 pub fn subscribe(&self) -> broadcast::Receiver<LspClientEvent> {
107 self.event_sender.subscribe()
108 }
109
110 pub async fn get_state(&self) -> LspServerState {
112 *self.state.read().await
113 }
114
115 pub async fn get_capabilities(&self) -> Option<Value> {
117 self.capabilities.read().await.clone()
118 }
119
120 pub async fn start(&self) -> Result<bool, String> {
122 let current_state = *self.state.read().await;
123 if current_state == LspServerState::Running {
124 return Ok(true);
125 }
126
127 *self.state.write().await = LspServerState::Starting;
128 let _ = self
129 .event_sender
130 .send(LspClientEvent::StateChange(LspServerState::Starting));
131
132 let child = Command::new(&self.config.command)
134 .args(&self.config.args)
135 .stdin(Stdio::piped())
136 .stdout(Stdio::piped())
137 .stderr(Stdio::piped())
138 .spawn()
139 .map_err(|e| format!("Failed to spawn LSP server: {}", e))?;
140
141 *self.process.lock().await = Some(child);
142
143 let init_params = serde_json::json!({
145 "processId": std::process::id(),
146 "capabilities": {
147 "textDocument": {
148 "documentSymbol": {
149 "hierarchicalDocumentSymbolSupport": true
150 },
151 "references": {
152 "dynamicRegistration": false
153 },
154 "definition": {
155 "dynamicRegistration": false
156 }
157 }
158 },
159 "rootUri": self.config.root_uri,
160 "initializationOptions": self.config.initialization_options
161 });
162
163 match self.send_request("initialize", init_params).await {
164 Ok(result) => {
165 if let Some(caps) = result.get("capabilities") {
166 *self.capabilities.write().await = Some(caps.clone());
167 }
168
169 self.send_notification("initialized", serde_json::json!({}))
171 .await;
172
173 *self.state.write().await = LspServerState::Running;
174 let _ = self
175 .event_sender
176 .send(LspClientEvent::StateChange(LspServerState::Running));
177 Ok(true)
178 }
179 Err(e) => {
180 *self.state.write().await = LspServerState::Error;
181 let _ = self
182 .event_sender
183 .send(LspClientEvent::StateChange(LspServerState::Error));
184 Err(format!("Initialize failed: {}", e))
185 }
186 }
187 }
188
189 pub async fn stop(&self) {
191 if *self.state.read().await == LspServerState::Stopped {
192 return;
193 }
194
195 let _ = self.send_request("shutdown", Value::Null).await;
197 self.send_notification("exit", Value::Null).await;
198
199 if let Some(mut child) = self.process.lock().await.take() {
201 let _ = child.kill();
202 }
203
204 *self.state.write().await = LspServerState::Stopped;
205 let _ = self
206 .event_sender
207 .send(LspClientEvent::StateChange(LspServerState::Stopped));
208 }
209
210 async fn send_request(&self, method: &str, params: Value) -> Result<Value, String> {
212 let id = self.message_id.fetch_add(1, Ordering::SeqCst);
213
214 let message = LspMessage {
215 jsonrpc: "2.0".to_string(),
216 id: Some(id),
217 method: Some(method.to_string()),
218 params: Some(params),
219 result: None,
220 error: None,
221 };
222
223 self.send_message(&message).await?;
224
225 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
228
229 Ok(Value::Null)
230 }
231
232 async fn send_notification(&self, method: &str, params: Value) {
234 let message = LspMessage {
235 jsonrpc: "2.0".to_string(),
236 id: None,
237 method: Some(method.to_string()),
238 params: Some(params),
239 result: None,
240 error: None,
241 };
242
243 let _ = self.send_message(&message).await;
244 }
245
246 async fn send_message(&self, message: &LspMessage) -> Result<(), String> {
248 let content = serde_json::to_string(message)
249 .map_err(|e| format!("Failed to serialize message: {}", e))?;
250
251 let header = format!("Content-Length: {}\r\n\r\n", content.len());
252
253 let mut process = self.process.lock().await;
254 if let Some(ref mut child) = *process {
255 if let Some(ref mut stdin) = child.stdin {
256 stdin
257 .write_all(header.as_bytes())
258 .map_err(|e| format!("Failed to write header: {}", e))?;
259 stdin
260 .write_all(content.as_bytes())
261 .map_err(|e| format!("Failed to write content: {}", e))?;
262 stdin
263 .flush()
264 .map_err(|e| format!("Failed to flush: {}", e))?;
265 }
266 }
267
268 Ok(())
269 }
270
271 pub async fn get_document_symbols(&self, uri: &str) -> Result<Vec<Value>, String> {
273 if *self.state.read().await != LspServerState::Running {
274 return Err("LSP server is not running".to_string());
275 }
276
277 let params = serde_json::json!({
278 "textDocument": { "uri": uri }
279 });
280
281 let result = self
282 .send_request("textDocument/documentSymbol", params)
283 .await?;
284
285 match result {
286 Value::Array(symbols) => Ok(symbols),
287 Value::Null => Ok(Vec::new()),
288 _ => Ok(Vec::new()),
289 }
290 }
291
292 pub async fn open_document(&self, uri: &str, language_id: &str, version: i32, text: &str) {
294 let params = serde_json::json!({
295 "textDocument": {
296 "uri": uri,
297 "languageId": language_id,
298 "version": version,
299 "text": text
300 }
301 });
302
303 self.send_notification("textDocument/didOpen", params).await;
304 }
305
306 pub async fn close_document(&self, uri: &str) {
308 let params = serde_json::json!({
309 "textDocument": { "uri": uri }
310 });
311
312 self.send_notification("textDocument/didClose", params)
313 .await;
314 }
315
316 pub async fn find_references(
318 &self,
319 uri: &str,
320 position: LspPosition,
321 ) -> Result<Vec<LspLocation>, String> {
322 if *self.state.read().await != LspServerState::Running {
323 return Err("LSP server is not running".to_string());
324 }
325
326 let params = serde_json::json!({
327 "textDocument": { "uri": uri },
328 "position": { "line": position.line, "character": position.character },
329 "context": { "includeDeclaration": true }
330 });
331
332 let result = self.send_request("textDocument/references", params).await?;
333
334 match result {
335 Value::Array(locations) => {
336 let parsed: Vec<LspLocation> = locations
337 .iter()
338 .filter_map(|v| serde_json::from_value(v.clone()).ok())
339 .collect();
340 Ok(parsed)
341 }
342 _ => Ok(Vec::new()),
343 }
344 }
345
346 pub async fn get_definition(
348 &self,
349 uri: &str,
350 position: LspPosition,
351 ) -> Result<Option<LspLocation>, String> {
352 if *self.state.read().await != LspServerState::Running {
353 return Err("LSP server is not running".to_string());
354 }
355
356 let params = serde_json::json!({
357 "textDocument": { "uri": uri },
358 "position": { "line": position.line, "character": position.character }
359 });
360
361 let result = self.send_request("textDocument/definition", params).await?;
362
363 match result {
364 Value::Array(locations) if !locations.is_empty() => {
365 serde_json::from_value(locations[0].clone())
366 .map(Some)
367 .map_err(|e| format!("Failed to parse location: {}", e))
368 }
369 Value::Object(_) => serde_json::from_value(result)
370 .map(Some)
371 .map_err(|e| format!("Failed to parse location: {}", e)),
372 _ => Ok(None),
373 }
374 }
375}
376
377#[cfg(test)]
378mod tests {
379 use super::*;
380
381 #[test]
382 fn test_lsp_server_state() {
383 assert_eq!(LspServerState::Stopped, LspServerState::Stopped);
384 assert_ne!(LspServerState::Running, LspServerState::Stopped);
385 }
386
387 #[test]
388 fn test_lsp_client_config() {
389 let config = LspClientConfig {
390 command: "typescript-language-server".to_string(),
391 args: vec!["--stdio".to_string()],
392 root_uri: Some("file:///tmp".to_string()),
393 initialization_options: None,
394 };
395 assert_eq!(config.command, "typescript-language-server");
396 }
397
398 #[test]
399 fn test_lsp_message_serialize() {
400 let msg = LspMessage {
401 jsonrpc: "2.0".to_string(),
402 id: Some(1),
403 method: Some("initialize".to_string()),
404 params: Some(serde_json::json!({})),
405 result: None,
406 error: None,
407 };
408 let json = serde_json::to_string(&msg).unwrap();
409 assert!(json.contains("jsonrpc"));
410 assert!(json.contains("initialize"));
411 }
412}