1use super::{LspState, Position, Range};
4use std::sync::Arc;
5use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
6use tokio::sync::RwLock;
7
8pub struct LspServer {
10 state: Arc<LspState>,
12
13 running: Arc<RwLock<bool>>,
15}
16
17impl LspServer {
18 pub fn new() -> Self {
20 Self {
21 state: Arc::new(LspState::new()),
22 running: Arc::new(RwLock::new(false)),
23 }
24 }
25
26 pub async fn start(&self) -> Result<(), LspError> {
28 *self.running.write().await = true;
29
30 let stdin = tokio::io::stdin();
31 let mut stdout = tokio::io::stdout();
32 let mut reader = BufReader::new(stdin);
33
34 eprintln!("VoiRS LSP server started");
35
36 while *self.running.read().await {
37 let mut headers = String::new();
38
39 loop {
41 let mut line = String::new();
42 if reader.read_line(&mut line).await? == 0 {
43 return Ok(()); }
45
46 if line == "\r\n" || line == "\n" {
47 break; }
49
50 headers.push_str(&line);
51 }
52
53 let content_length = self.parse_content_length(&headers)?;
55
56 let mut content = vec![0u8; content_length];
58 tokio::io::AsyncReadExt::read_exact(&mut reader, &mut content).await?;
59
60 let content_str =
61 String::from_utf8(content).map_err(|e| LspError::InvalidUtf8(e.to_string()))?;
62
63 if let Some(response) = self.handle_request(&content_str).await? {
65 let response_json = serde_json::to_string(&response)?;
67 let response_msg = format!(
68 "Content-Length: {}\r\n\r\n{}",
69 response_json.len(),
70 response_json
71 );
72
73 stdout.write_all(response_msg.as_bytes()).await?;
74 stdout.flush().await?;
75 }
76 }
77
78 Ok(())
79 }
80
81 pub async fn stop(&self) {
83 *self.running.write().await = false;
84 }
85
86 fn parse_content_length(&self, headers: &str) -> Result<usize, LspError> {
88 for line in headers.lines() {
89 if line.starts_with("Content-Length:") {
90 let length_str = line.trim_start_matches("Content-Length:").trim();
91 return length_str
92 .parse()
93 .map_err(|_| LspError::InvalidContentLength(line.to_string()));
94 }
95 }
96
97 Err(LspError::MissingContentLength)
98 }
99
100 async fn handle_request(&self, content: &str) -> Result<Option<serde_json::Value>, LspError> {
102 let request: serde_json::Value = serde_json::from_str(content)?;
103
104 let method = request["method"].as_str().ok_or(LspError::MissingMethod)?;
105
106 match method {
107 "initialize" => Ok(Some(self.handle_initialize(&request).await?)),
108 "initialized" => Ok(None), "shutdown" => Ok(Some(self.handle_shutdown().await?)),
110 "exit" => {
111 self.stop().await;
112 Ok(None)
113 }
114 "textDocument/didOpen" => {
115 self.handle_did_open(&request).await?;
116 Ok(None)
117 }
118 "textDocument/didChange" => {
119 self.handle_did_change(&request).await?;
120 Ok(None)
121 }
122 "textDocument/didClose" => {
123 self.handle_did_close(&request).await?;
124 Ok(None)
125 }
126 "textDocument/completion" => Ok(Some(self.handle_completion(&request).await?)),
127 "textDocument/hover" => Ok(Some(self.handle_hover(&request).await?)),
128 "textDocument/codeAction" => Ok(Some(self.handle_code_action(&request).await?)),
129 "textDocument/formatting" => Ok(Some(self.handle_formatting(&request).await?)),
130 "textDocument/rangeFormatting" => {
131 Ok(Some(self.handle_range_formatting(&request).await?))
132 }
133 "textDocument/onTypeFormatting" => {
134 Ok(Some(self.handle_on_type_formatting(&request).await?))
135 }
136 _ => {
137 eprintln!("Unhandled method: {}", method);
138 Ok(None)
139 }
140 }
141 }
142
143 async fn handle_initialize(
145 &self,
146 request: &serde_json::Value,
147 ) -> Result<serde_json::Value, LspError> {
148 let id = request["id"].clone();
149
150 Ok(serde_json::json!({
151 "jsonrpc": "2.0",
152 "id": id,
153 "result": {
154 "capabilities": {
155 "textDocumentSync": 1, "completionProvider": {
157 "triggerCharacters": ["<", " ", "=", "\""]
158 },
159 "hoverProvider": true,
160 "diagnosticProvider": true,
161 "codeActionProvider": {
162 "codeActionKinds": [
163 "quickfix",
164 "refactor",
165 "refactor.rewrite",
166 "source"
167 ]
168 },
169 "documentFormattingProvider": true,
170 "documentRangeFormattingProvider": true,
171 "documentOnTypeFormattingProvider": {
172 "firstTriggerCharacter": ">",
173 "moreTriggerCharacter": ["}","]"]
174 }
175 },
176 "serverInfo": {
177 "name": "voirs-lsp",
178 "version": env!("CARGO_PKG_VERSION")
179 }
180 }
181 }))
182 }
183
184 async fn handle_shutdown(&self) -> Result<serde_json::Value, LspError> {
186 Ok(serde_json::json!({
187 "jsonrpc": "2.0",
188 "result": null
189 }))
190 }
191
192 async fn handle_did_open(&self, request: &serde_json::Value) -> Result<(), LspError> {
194 let params = &request["params"];
195 let text_document = ¶ms["textDocument"];
196
197 let uri = text_document["uri"]
198 .as_str()
199 .ok_or_else(|| LspError::MissingField("uri".to_string()))?
200 .to_string();
201
202 let text = text_document["text"]
203 .as_str()
204 .ok_or_else(|| LspError::MissingField("text".to_string()))?
205 .to_string();
206
207 let language_id = text_document["languageId"]
208 .as_str()
209 .ok_or_else(|| LspError::MissingField("languageId".to_string()))?
210 .to_string();
211
212 self.state.open_document(uri, text, language_id).await;
213
214 Ok(())
215 }
216
217 async fn handle_did_change(&self, request: &serde_json::Value) -> Result<(), LspError> {
219 let params = &request["params"];
220 let text_document = ¶ms["textDocument"];
221 let content_changes = ¶ms["contentChanges"];
222
223 let uri = text_document["uri"]
224 .as_str()
225 .ok_or_else(|| LspError::MissingField("uri".to_string()))?;
226
227 let version = text_document["version"]
228 .as_i64()
229 .ok_or_else(|| LspError::MissingField("version".to_string()))?
230 as i32;
231
232 if let Some(change) = content_changes.get(0) {
233 if let Some(text) = change["text"].as_str() {
234 self.state
235 .update_document(uri, text.to_string(), version)
236 .await;
237 }
238 }
239
240 Ok(())
241 }
242
243 async fn handle_did_close(&self, request: &serde_json::Value) -> Result<(), LspError> {
245 let params = &request["params"];
246 let text_document = ¶ms["textDocument"];
247
248 let uri = text_document["uri"]
249 .as_str()
250 .ok_or_else(|| LspError::MissingField("uri".to_string()))?;
251
252 self.state.close_document(uri).await;
253
254 Ok(())
255 }
256
257 async fn handle_completion(
259 &self,
260 request: &serde_json::Value,
261 ) -> Result<serde_json::Value, LspError> {
262 let id = request["id"].clone();
263
264 let params = &request["params"];
266 let position = ¶ms["position"];
267
268 let line = position["line"]
269 .as_u64()
270 .ok_or_else(|| LspError::MissingField("line".to_string()))? as u32;
271
272 let character = position["character"]
273 .as_u64()
274 .ok_or_else(|| LspError::MissingField("character".to_string()))?
275 as u32;
276
277 let pos = Position::new(line, character);
278
279 let uri = params["textDocument"]["uri"]
281 .as_str()
282 .ok_or_else(|| LspError::MissingField("uri".to_string()))?;
283
284 let items = self.get_completion_items(uri, pos).await;
286
287 Ok(serde_json::json!({
288 "jsonrpc": "2.0",
289 "id": id,
290 "result": items
291 }))
292 }
293
294 async fn handle_hover(
296 &self,
297 request: &serde_json::Value,
298 ) -> Result<serde_json::Value, LspError> {
299 let id = request["id"].clone();
300
301 Ok(serde_json::json!({
302 "jsonrpc": "2.0",
303 "id": id,
304 "result": {
305 "contents": {
306 "kind": "markdown",
307 "value": "VoiRS SSML Element\n\nHover information will be provided here."
308 }
309 }
310 }))
311 }
312
313 async fn get_completion_items(&self, _uri: &str, _pos: Position) -> Vec<serde_json::Value> {
315 vec![
317 serde_json::json!({
318 "label": "speak",
319 "kind": 14, "detail": "SSML root element",
321 "insertText": "<speak>$1</speak>",
322 "insertTextFormat": 2 }),
324 serde_json::json!({
325 "label": "voice",
326 "kind": 14,
327 "detail": "Voice selection",
328 "insertText": "<voice name=\"$1\">$2</voice>",
329 "insertTextFormat": 2
330 }),
331 serde_json::json!({
332 "label": "prosody",
333 "kind": 14,
334 "detail": "Prosody control",
335 "insertText": "<prosody rate=\"$1\" pitch=\"$2\">$3</prosody>",
336 "insertTextFormat": 2
337 }),
338 serde_json::json!({
339 "label": "break",
340 "kind": 14,
341 "detail": "Insert pause",
342 "insertText": "<break time=\"${1:500ms}\"/>",
343 "insertTextFormat": 2
344 }),
345 ]
346 }
347
348 async fn handle_code_action(
350 &self,
351 request: &serde_json::Value,
352 ) -> Result<serde_json::Value, LspError> {
353 let id = request["id"].clone();
354 let params = &request["params"];
355
356 let uri = params["textDocument"]["uri"]
358 .as_str()
359 .ok_or_else(|| LspError::MissingField("uri".to_string()))?;
360
361 let range_json = ¶ms["range"];
362 let range = Range::new(
363 Position::new(
364 range_json["start"]["line"].as_u64().unwrap_or(0) as u32,
365 range_json["start"]["character"].as_u64().unwrap_or(0) as u32,
366 ),
367 Position::new(
368 range_json["end"]["line"].as_u64().unwrap_or(0) as u32,
369 range_json["end"]["character"].as_u64().unwrap_or(0) as u32,
370 ),
371 );
372
373 let doc = self.state.get_document(uri).await;
375 let actions = if let Some(document) = doc {
376 super::code_actions::get_code_actions(&document.text, range)
377 } else {
378 Vec::new()
379 };
380
381 Ok(serde_json::json!({
382 "jsonrpc": "2.0",
383 "id": id,
384 "result": actions
385 }))
386 }
387
388 async fn handle_formatting(
390 &self,
391 request: &serde_json::Value,
392 ) -> Result<serde_json::Value, LspError> {
393 let id = request["id"].clone();
394 let params = &request["params"];
395
396 let uri = params["textDocument"]["uri"]
397 .as_str()
398 .ok_or_else(|| LspError::MissingField("uri".to_string()))?;
399
400 let doc = self.state.get_document(uri).await;
401 let edits = if let Some(document) = doc {
402 super::formatting::format_document(&document.text, &document.language_id)
403 .map(|edits| edits.iter().map(|e| e.to_json()).collect::<Vec<_>>())
404 .unwrap_or_default()
405 } else {
406 Vec::new()
407 };
408
409 Ok(serde_json::json!({
410 "jsonrpc": "2.0",
411 "id": id,
412 "result": edits
413 }))
414 }
415
416 async fn handle_range_formatting(
418 &self,
419 request: &serde_json::Value,
420 ) -> Result<serde_json::Value, LspError> {
421 let id = request["id"].clone();
422 let params = &request["params"];
423
424 let uri = params["textDocument"]["uri"]
425 .as_str()
426 .ok_or_else(|| LspError::MissingField("uri".to_string()))?;
427
428 let range_json = ¶ms["range"];
429 let range = Range::new(
430 Position::new(
431 range_json["start"]["line"].as_u64().unwrap_or(0) as u32,
432 range_json["start"]["character"].as_u64().unwrap_or(0) as u32,
433 ),
434 Position::new(
435 range_json["end"]["line"].as_u64().unwrap_or(0) as u32,
436 range_json["end"]["character"].as_u64().unwrap_or(0) as u32,
437 ),
438 );
439
440 let doc = self.state.get_document(uri).await;
441 let edits = if let Some(document) = doc {
442 super::formatting::format_range(&document.text, range, &document.language_id)
443 .map(|edits| edits.iter().map(|e| e.to_json()).collect::<Vec<_>>())
444 .unwrap_or_default()
445 } else {
446 Vec::new()
447 };
448
449 Ok(serde_json::json!({
450 "jsonrpc": "2.0",
451 "id": id,
452 "result": edits
453 }))
454 }
455
456 async fn handle_on_type_formatting(
458 &self,
459 request: &serde_json::Value,
460 ) -> Result<serde_json::Value, LspError> {
461 let id = request["id"].clone();
462 let params = &request["params"];
463
464 let uri = params["textDocument"]["uri"]
465 .as_str()
466 .ok_or_else(|| LspError::MissingField("uri".to_string()))?;
467
468 let position_json = ¶ms["position"];
469 let position = Position::new(
470 position_json["line"].as_u64().unwrap_or(0) as u32,
471 position_json["character"].as_u64().unwrap_or(0) as u32,
472 );
473
474 let ch_str = params["ch"]
475 .as_str()
476 .ok_or_else(|| LspError::MissingField("ch".to_string()))?;
477 let ch = ch_str.chars().next().unwrap_or('>');
478
479 let doc = self.state.get_document(uri).await;
480 let edits = if let Some(document) = doc {
481 super::formatting::format_on_type(&document.text, position, ch, &document.language_id)
482 .map(|edits| edits.iter().map(|e| e.to_json()).collect::<Vec<_>>())
483 .unwrap_or_default()
484 } else {
485 Vec::new()
486 };
487
488 Ok(serde_json::json!({
489 "jsonrpc": "2.0",
490 "id": id,
491 "result": edits
492 }))
493 }
494
495 #[cfg(test)]
497 pub fn state(&self) -> Arc<LspState> {
498 Arc::clone(&self.state)
499 }
500}
501
502impl Default for LspServer {
503 fn default() -> Self {
504 Self::new()
505 }
506}
507
508#[derive(Debug, thiserror::Error)]
510pub enum LspError {
511 #[error("I/O error: {0}")]
512 Io(#[from] std::io::Error),
513
514 #[error("JSON error: {0}")]
515 Json(#[from] serde_json::Error),
516
517 #[error("Invalid UTF-8: {0}")]
518 InvalidUtf8(String),
519
520 #[error("Missing Content-Length header")]
521 MissingContentLength,
522
523 #[error("Invalid Content-Length: {0}")]
524 InvalidContentLength(String),
525
526 #[error("Missing method field")]
527 MissingMethod,
528
529 #[error("Missing field: {0}")]
530 MissingField(String),
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536
537 #[test]
538 fn test_lsp_server_creation() {
539 let server = LspServer::new();
540 assert!(server.state.documents.try_read().is_ok());
541 }
542
543 #[tokio::test]
544 async fn test_parse_content_length() {
545 let server = LspServer::new();
546 let headers = "Content-Length: 123\r\nContent-Type: application/json\r\n";
547
548 let length = server.parse_content_length(headers).unwrap();
549 assert_eq!(length, 123);
550 }
551
552 #[tokio::test]
553 async fn test_parse_content_length_error() {
554 let server = LspServer::new();
555 let headers = "Content-Type: application/json\r\n";
556
557 let result = server.parse_content_length(headers);
558 assert!(result.is_err());
559 }
560
561 #[tokio::test]
562 async fn test_get_completion_items() {
563 let server = LspServer::new();
564 let items = server
565 .get_completion_items("file:///test.ssml", Position::new(0, 0))
566 .await;
567
568 assert!(!items.is_empty());
569 assert!(items.iter().any(|item| item["label"] == "speak"));
570 assert!(items.iter().any(|item| item["label"] == "voice"));
571 }
572}