Rust · 6674 bytes Raw Blame History
1 //! LSP message handling and callback management
2 //!
3 //! Handles routing of LSP responses to appropriate callbacks.
4 //!
5 //! Note: ParsedResponse helpers are for planned features.
6 #![allow(dead_code)]
7
8 use serde_json::Value;
9 use std::collections::HashMap;
10
11 use super::protocol::{LspMessage, ResponseError};
12 use super::types::{
13 CompletionItem, Diagnostic, DocumentSymbol, HoverInfo, Location, TextEdit, WorkspaceEdit,
14 };
15
16 /// Result type for LSP responses
17 pub type LspResult<T> = Result<T, ResponseError>;
18
19 /// Callback for LSP responses
20 pub type ResponseCallback = Box<dyn FnOnce(i64, LspResult<Value>) + Send>;
21
22 /// Callback for diagnostics notifications
23 pub type DiagnosticsCallback = Box<dyn Fn(String, Vec<Diagnostic>) + Send>;
24
25 /// Tracks pending requests and their callbacks
26 pub struct MessageHandler {
27 /// Pending request callbacks indexed by request ID
28 pending: HashMap<i64, ResponseCallback>,
29 /// Callback for diagnostics notifications
30 diagnostics_callback: Option<DiagnosticsCallback>,
31 }
32
33 impl MessageHandler {
34 pub fn new() -> Self {
35 Self {
36 pending: HashMap::new(),
37 diagnostics_callback: None,
38 }
39 }
40
41 /// Register a callback for a request
42 pub fn register_callback(&mut self, id: i64, callback: ResponseCallback) {
43 self.pending.insert(id, callback);
44 }
45
46 /// Set the diagnostics callback
47 pub fn set_diagnostics_callback(&mut self, callback: DiagnosticsCallback) {
48 self.diagnostics_callback = Some(callback);
49 }
50
51 /// Handle an incoming message
52 pub fn handle_message(&mut self, message: LspMessage) -> Option<LspMessage> {
53 match message {
54 LspMessage::Response { id, result, error } => {
55 self.handle_response(id, result, error);
56 None
57 }
58 LspMessage::Notification { method, params } => {
59 self.handle_notification(&method, params);
60 None
61 }
62 LspMessage::Request { id, method, params } => {
63 // Handle server-to-client requests
64 self.handle_server_request(id, &method, params)
65 }
66 }
67 }
68
69 /// Handle a response message
70 fn handle_response(&mut self, id: i64, result: Option<Value>, error: Option<ResponseError>) {
71 if let Some(callback) = self.pending.remove(&id) {
72 let response = if let Some(err) = error {
73 Err(err)
74 } else {
75 Ok(result.unwrap_or(Value::Null))
76 };
77 callback(id, response);
78 }
79 }
80
81 /// Handle a notification message
82 fn handle_notification(&mut self, method: &str, params: Option<Value>) {
83 match method {
84 "textDocument/publishDiagnostics" => {
85 if let (Some(params), Some(callback)) = (params, &self.diagnostics_callback) {
86 let (uri, diagnostics) = super::protocol::parse_diagnostics(&params);
87 callback(uri, diagnostics);
88 }
89 }
90 "window/logMessage" | "window/showMessage" => {
91 // Silently ignore server log messages
92 // These could be surfaced to the status bar via a callback if needed
93 let _ = params;
94 }
95 _ => {
96 // Ignore other notifications
97 }
98 }
99 }
100
101 /// Handle a server-to-client request (return a response if needed)
102 fn handle_server_request(
103 &mut self,
104 id: i64,
105 method: &str,
106 _params: Option<Value>,
107 ) -> Option<LspMessage> {
108 match method {
109 "workspace/configuration" => {
110 // Return empty configuration
111 Some(LspMessage::Response {
112 id,
113 result: Some(Value::Array(vec![])),
114 error: None,
115 })
116 }
117 "client/registerCapability" | "client/unregisterCapability" => {
118 // Acknowledge capability registration
119 Some(LspMessage::Response {
120 id,
121 result: Some(Value::Null),
122 error: None,
123 })
124 }
125 "window/workDoneProgress/create" => {
126 // Acknowledge progress creation
127 Some(LspMessage::Response {
128 id,
129 result: Some(Value::Null),
130 error: None,
131 })
132 }
133 _ => {
134 // Unknown request - return method not found error
135 Some(LspMessage::Response {
136 id,
137 result: None,
138 error: Some(ResponseError {
139 code: -32601, // Method not found
140 message: format!("Method not found: {}", method),
141 data: None,
142 }),
143 })
144 }
145 }
146 }
147
148 /// Check if there are pending requests
149 pub fn has_pending(&self) -> bool {
150 !self.pending.is_empty()
151 }
152
153 /// Get number of pending requests
154 pub fn pending_count(&self) -> usize {
155 self.pending.len()
156 }
157 }
158
159 impl Default for MessageHandler {
160 fn default() -> Self {
161 Self::new()
162 }
163 }
164
165 /// Parsed LSP response types for convenience
166 pub enum ParsedResponse {
167 Completions(Vec<CompletionItem>),
168 Hover(Option<HoverInfo>),
169 Locations(Vec<Location>),
170 Symbols(Vec<DocumentSymbol>),
171 TextEdits(Vec<TextEdit>),
172 WorkspaceEdit(WorkspaceEdit),
173 Empty,
174 }
175
176 impl ParsedResponse {
177 /// Parse a completion response
178 pub fn parse_completions(result: &Value) -> Self {
179 ParsedResponse::Completions(super::protocol::parse_completion_items(result))
180 }
181
182 /// Parse a hover response
183 pub fn parse_hover(result: &Value) -> Self {
184 ParsedResponse::Hover(super::protocol::parse_hover(result))
185 }
186
187 /// Parse a definition/references response
188 pub fn parse_locations(result: &Value) -> Self {
189 ParsedResponse::Locations(super::protocol::parse_locations(result))
190 }
191
192 /// Parse a document symbols response
193 pub fn parse_symbols(result: &Value) -> Self {
194 ParsedResponse::Symbols(super::protocol::parse_document_symbols(result))
195 }
196
197 /// Parse a formatting response
198 pub fn parse_text_edits(result: &Value) -> Self {
199 ParsedResponse::TextEdits(super::protocol::parse_text_edits(result))
200 }
201
202 /// Parse a rename response
203 pub fn parse_workspace_edit(result: &Value) -> Self {
204 ParsedResponse::WorkspaceEdit(super::protocol::parse_workspace_edit(result))
205 }
206 }
207