1use super::types::*;
6use super::LspError;
7use serde::{Deserialize, Serialize};
8use std::io::{BufRead, Write};
9
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
12#[serde(untagged)]
13pub enum RequestId {
14 Number(i64),
15 String(String),
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct LspRequest<P> {
21 pub jsonrpc: String,
22 pub id: RequestId,
23 pub method: String,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub params: Option<P>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct LspResponse<R> {
31 pub jsonrpc: String,
32 pub id: RequestId,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub result: Option<R>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub error: Option<RpcError>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct LspNotification<P> {
42 pub jsonrpc: String,
43 pub method: String,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 pub params: Option<P>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct RpcError {
51 pub code: i32,
52 pub message: String,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub data: Option<serde_json::Value>,
55}
56
57#[derive(Debug, Clone)]
59pub enum LspMessage {
60 Request(LspRequest<serde_json::Value>),
61 Response(LspResponse<serde_json::Value>),
62 Notification(LspNotification<serde_json::Value>),
63}
64
65pub trait MessageContent {
67 fn content(&self) -> Result<String, LspError>;
68}
69
70impl<P: Serialize> MessageContent for LspRequest<P> {
71 fn content(&self) -> Result<String, LspError> {
72 serde_json::to_string(self).map_err(LspError::Json)
73 }
74}
75
76impl<R: Serialize> MessageContent for LspResponse<R> {
77 fn content(&self) -> Result<String, LspError> {
78 serde_json::to_string(self).map_err(LspError::Json)
79 }
80}
81
82impl<P: Serialize> MessageContent for LspNotification<P> {
83 fn content(&self) -> Result<String, LspError> {
84 serde_json::to_string(self).map_err(LspError::Json)
85 }
86}
87
88pub fn write_message<W: Write, M: MessageContent>(
90 output: &mut W,
91 message: &M,
92) -> Result<(), LspError> {
93 let content = message.content()?;
94 let header = format!("Content-Length: {}\r\n\r\n", content.len());
95 output.write_all(header.as_bytes())?;
96 output.write_all(content.as_bytes())?;
97 output.flush()?;
98 Ok(())
99}
100
101pub fn read_message<R: BufRead>(input: &mut R) -> Result<LspMessage, LspError> {
103 let mut content_length: Option<usize> = None;
105
106 loop {
107 let mut line = String::new();
108 input.read_line(&mut line)?;
109
110 let line = line.trim();
111 if line.is_empty() {
112 break;
113 }
114
115 if let Some(stripped) = line.strip_prefix("Content-Length:") {
116 let value = stripped.trim();
117 content_length = Some(
118 value
119 .parse()
120 .map_err(|_| LspError::InvalidMessage("Invalid Content-Length".to_string()))?,
121 );
122 }
123 }
124
125 let content_length = content_length
126 .ok_or_else(|| LspError::InvalidMessage("Missing Content-Length header".to_string()))?;
127
128 let mut content = vec![0u8; content_length];
130 input.read_exact(&mut content)?;
131
132 let content_str = String::from_utf8(content)
134 .map_err(|_| LspError::InvalidMessage("Invalid UTF-8 content".to_string()))?;
135
136 let value: serde_json::Value = serde_json::from_str(&content_str)?;
138
139 if let Some(_id) = value.get("id") {
140 if value.get("error").is_some() {
141 let response: LspResponse<serde_json::Value> = serde_json::from_str(&content_str)?;
142 Ok(LspMessage::Response(response))
143 } else {
144 let request: LspRequest<serde_json::Value> = serde_json::from_str(&content_str)?;
145 Ok(LspMessage::Request(request))
146 }
147 } else {
148 let notification: LspNotification<serde_json::Value> = serde_json::from_str(&content_str)?;
149 Ok(LspMessage::Notification(notification))
150 }
151}
152
153pub fn create_initialize_request(
155 id: RequestId,
156 root_uri: Option<String>,
157 client_name: &str,
158 client_version: &str,
159) -> LspRequest<InitializeParams> {
160 LspRequest {
161 jsonrpc: "2.0".to_string(),
162 id,
163 method: "initialize".to_string(),
164 params: Some(InitializeParams {
165 process_id: Some(std::process::id()),
166 client_info: Some(ClientInfo {
167 name: client_name.to_string(),
168 version: Some(client_version.to_string()),
169 }),
170 root_uri,
171 capabilities: ClientCapabilities {
172 text_document: Some(TextDocumentClientCapabilities {
173 completion: Some(CompletionClientCapabilities {
174 completion_item: Some(CompletionItemCapability {
175 documentation_format: Some(vec!["markdown".to_string()]),
176 }),
177 }),
178 hover: Some(HoverClientCapabilities {
179 content_format: Some(vec!["markdown".to_string()]),
180 }),
181 }),
182 },
183 }),
184 }
185}
186
187pub fn create_did_open_notification(
189 uri: DocumentUri,
190 language_id: &str,
191 version: i32,
192 text: &str,
193) -> LspNotification<DidOpenTextDocumentParams> {
194 LspNotification {
195 jsonrpc: "2.0".to_string(),
196 method: "textDocument/didOpen".to_string(),
197 params: Some(DidOpenTextDocumentParams {
198 text_document: TextDocumentItem {
199 uri,
200 language_id: language_id.to_string(),
201 version,
202 text: text.to_string(),
203 },
204 }),
205 }
206}
207
208pub fn create_did_change_notification(
210 uri: DocumentUri,
211 version: i32,
212 changes: Vec<TextDocumentContentChangeEvent>,
213) -> LspNotification<DidChangeTextDocumentParams> {
214 LspNotification {
215 jsonrpc: "2.0".to_string(),
216 method: "textDocument/didChange".to_string(),
217 params: Some(DidChangeTextDocumentParams {
218 text_document: VersionedTextDocumentIdentifier { uri, version },
219 content_changes: changes,
220 }),
221 }
222}
223
224pub fn create_did_close_notification(
226 uri: DocumentUri,
227) -> LspNotification<DidCloseTextDocumentParams> {
228 LspNotification {
229 jsonrpc: "2.0".to_string(),
230 method: "textDocument/didClose".to_string(),
231 params: Some(DidCloseTextDocumentParams {
232 text_document: TextDocumentIdentifier { uri },
233 }),
234 }
235}
236
237pub fn create_definition_request(
239 id: RequestId,
240 uri: DocumentUri,
241 position: Position,
242) -> LspRequest<DefinitionParams> {
243 LspRequest {
244 jsonrpc: "2.0".to_string(),
245 id,
246 method: "textDocument/definition".to_string(),
247 params: Some(DefinitionParams {
248 text_document: TextDocumentIdentifier { uri },
249 position,
250 }),
251 }
252}
253
254pub fn create_references_request(
256 id: RequestId,
257 uri: DocumentUri,
258 position: Position,
259 include_declaration: bool,
260) -> LspRequest<ReferenceParams> {
261 LspRequest {
262 jsonrpc: "2.0".to_string(),
263 id,
264 method: "textDocument/references".to_string(),
265 params: Some(ReferenceParams {
266 text_document: TextDocumentIdentifier { uri },
267 position,
268 context: ReferenceContext {
269 include_declaration,
270 },
271 }),
272 }
273}
274
275pub fn create_hover_request(
277 id: RequestId,
278 uri: DocumentUri,
279 position: Position,
280) -> LspRequest<HoverParams> {
281 LspRequest {
282 jsonrpc: "2.0".to_string(),
283 id,
284 method: "textDocument/hover".to_string(),
285 params: Some(HoverParams {
286 text_document: TextDocumentIdentifier { uri },
287 position,
288 }),
289 }
290}
291
292pub fn create_rename_request(
294 id: RequestId,
295 uri: DocumentUri,
296 position: Position,
297 new_name: &str,
298) -> LspRequest<RenameParams> {
299 LspRequest {
300 jsonrpc: "2.0".to_string(),
301 id,
302 method: "textDocument/rename".to_string(),
303 params: Some(RenameParams {
304 text_document: TextDocumentIdentifier { uri },
305 position,
306 new_name: new_name.to_string(),
307 }),
308 }
309}
310
311pub fn create_document_symbol_request(
313 id: RequestId,
314 uri: DocumentUri,
315) -> LspRequest<DocumentSymbolParams> {
316 LspRequest {
317 jsonrpc: "2.0".to_string(),
318 id,
319 method: "textDocument/documentSymbol".to_string(),
320 params: Some(DocumentSymbolParams {
321 text_document: TextDocumentIdentifier { uri },
322 }),
323 }
324}
325
326#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct DidOpenTextDocumentParams {
333 pub text_document: TextDocumentItem,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
338pub struct DidChangeTextDocumentParams {
339 pub text_document: VersionedTextDocumentIdentifier,
340 pub content_changes: Vec<TextDocumentContentChangeEvent>,
341}
342
343#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct DidCloseTextDocumentParams {
346 pub text_document: TextDocumentIdentifier,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct DefinitionParams {
352 pub text_document: TextDocumentIdentifier,
353 pub position: Position,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct ReferenceParams {
359 pub text_document: TextDocumentIdentifier,
360 pub position: Position,
361 pub context: ReferenceContext,
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct HoverParams {
367 pub text_document: TextDocumentIdentifier,
368 pub position: Position,
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct RenameParams {
374 pub text_document: TextDocumentIdentifier,
375 pub position: Position,
376 pub new_name: String,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct DocumentSymbolParams {
382 pub text_document: TextDocumentIdentifier,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
387pub struct WorkspaceSymbolParams {
388 pub query: String,
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize)]
393pub struct CodeActionParams {
394 pub text_document: TextDocumentIdentifier,
395 pub range: Range,
396 pub context: CodeActionContext,
397}
398
399#[derive(Debug, Clone, Serialize, Deserialize)]
401pub struct SignatureHelpParams {
402 pub text_document: TextDocumentIdentifier,
403 pub position: Position,
404}
405
406#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct DocumentHighlightParams {
409 pub text_document: TextDocumentIdentifier,
410 pub position: Position,
411}
412
413#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct DocumentFormattingParams {
416 pub text_document: TextDocumentIdentifier,
417 pub options: FormattingOptions,
418}
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
422pub struct DocumentRangeFormattingParams {
423 pub text_document: TextDocumentIdentifier,
424 pub range: Range,
425 pub options: FormattingOptions,
426}
427
428pub fn create_workspace_symbol_request(
430 id: RequestId,
431 query: &str,
432) -> LspRequest<WorkspaceSymbolParams> {
433 LspRequest {
434 jsonrpc: "2.0".to_string(),
435 id,
436 method: "workspace/symbol".to_string(),
437 params: Some(WorkspaceSymbolParams {
438 query: query.to_string(),
439 }),
440 }
441}
442
443pub fn create_code_action_request(
445 id: RequestId,
446 uri: DocumentUri,
447 range: Range,
448 context: CodeActionContext,
449) -> LspRequest<CodeActionParams> {
450 LspRequest {
451 jsonrpc: "2.0".to_string(),
452 id,
453 method: "textDocument/codeAction".to_string(),
454 params: Some(CodeActionParams {
455 text_document: TextDocumentIdentifier { uri },
456 range,
457 context,
458 }),
459 }
460}
461
462pub fn create_signature_help_request(
464 id: RequestId,
465 uri: DocumentUri,
466 position: Position,
467) -> LspRequest<SignatureHelpParams> {
468 LspRequest {
469 jsonrpc: "2.0".to_string(),
470 id,
471 method: "textDocument/signatureHelp".to_string(),
472 params: Some(SignatureHelpParams {
473 text_document: TextDocumentIdentifier { uri },
474 position,
475 }),
476 }
477}
478
479pub fn create_document_highlight_request(
481 id: RequestId,
482 uri: DocumentUri,
483 position: Position,
484) -> LspRequest<DocumentHighlightParams> {
485 LspRequest {
486 jsonrpc: "2.0".to_string(),
487 id,
488 method: "textDocument/documentHighlight".to_string(),
489 params: Some(DocumentHighlightParams {
490 text_document: TextDocumentIdentifier { uri },
491 position,
492 }),
493 }
494}
495
496pub fn create_formatting_request(
498 id: RequestId,
499 uri: DocumentUri,
500 options: FormattingOptions,
501) -> LspRequest<DocumentFormattingParams> {
502 LspRequest {
503 jsonrpc: "2.0".to_string(),
504 id,
505 method: "textDocument/formatting".to_string(),
506 params: Some(DocumentFormattingParams {
507 text_document: TextDocumentIdentifier { uri },
508 options,
509 }),
510 }
511}
512
513pub fn create_completion_request(
515 id: RequestId,
516 uri: DocumentUri,
517 position: Position,
518) -> LspRequest<CompletionParams> {
519 LspRequest {
520 jsonrpc: "2.0".to_string(),
521 id,
522 method: "textDocument/completion".to_string(),
523 params: Some(CompletionParams {
524 text_document: TextDocumentIdentifier { uri },
525 position,
526 }),
527 }
528}
529
530#[derive(Debug, Clone, Serialize, Deserialize)]
532pub struct CompletionParams {
533 pub text_document: TextDocumentIdentifier,
534 pub position: Position,
535}
536
537#[derive(Debug, Clone, Serialize, Deserialize)]
539pub struct ImplementationParams {
540 pub text_document: TextDocumentIdentifier,
541 pub position: Position,
542}
543
544#[derive(Debug, Clone, Serialize, Deserialize)]
546pub struct TypeDefinitionParams {
547 pub text_document: TextDocumentIdentifier,
548 pub position: Position,
549}
550
551pub fn create_implementation_request(
553 id: RequestId,
554 uri: DocumentUri,
555 position: Position,
556) -> LspRequest<ImplementationParams> {
557 LspRequest {
558 jsonrpc: "2.0".to_string(),
559 id,
560 method: "textDocument/implementation".to_string(),
561 params: Some(ImplementationParams {
562 text_document: TextDocumentIdentifier { uri },
563 position,
564 }),
565 }
566}
567
568pub fn create_type_definition_request(
570 id: RequestId,
571 uri: DocumentUri,
572 position: Position,
573) -> LspRequest<TypeDefinitionParams> {
574 LspRequest {
575 jsonrpc: "2.0".to_string(),
576 id,
577 method: "textDocument/typeDefinition".to_string(),
578 params: Some(TypeDefinitionParams {
579 text_document: TextDocumentIdentifier { uri },
580 position,
581 }),
582 }
583}