Skip to main content

lsp_max/client/
server_handle.rs

1use crate::client::ClientError;
2use crate::lsp_types::*;
3use serde_json::json;
4use serde_json::Value;
5use std::collections::HashMap;
6use std::sync::atomic::{AtomicU64, Ordering};
7use std::sync::Arc;
8use tokio::sync::{mpsc, oneshot, Mutex};
9
10/// A handle to interact with the connected Language Server.
11/// This acts as the outbound proxy for the downstream client.
12#[derive(Debug, Clone)]
13pub struct ServerHandle {
14    pub(crate) sender: mpsc::Sender<Value>,
15    pub(crate) pending: Arc<Mutex<HashMap<u64, oneshot::Sender<Value>>>>,
16    pub(crate) next_id: Arc<AtomicU64>,
17}
18
19impl ServerHandle {
20    pub fn new(
21        sender: mpsc::Sender<Value>,
22        pending: Arc<Mutex<HashMap<u64, oneshot::Sender<Value>>>>,
23        next_id: Arc<AtomicU64>,
24    ) -> Self {
25        Self {
26            sender,
27            pending,
28            next_id,
29        }
30    }
31
32    /// Helper to send a notification
33    pub async fn notify(&self, method: &str, params: impl serde::Serialize) {
34        let msg = json!({
35            "jsonrpc": "2.0",
36            "method": method,
37            "params": params,
38        });
39        let _ = self.sender.send(msg).await;
40    }
41
42    /// Helper to send a request and await a correlated response.
43    pub async fn request<R>(
44        &self,
45        method: &str,
46        params: impl serde::Serialize,
47    ) -> Result<Option<R>, ClientError>
48    where
49        R: serde::de::DeserializeOwned,
50    {
51        let id = self.next_id.fetch_add(1, Ordering::SeqCst);
52        let msg = json!({
53            "jsonrpc": "2.0",
54            "id": id,
55            "method": method,
56            "params": params,
57        });
58
59        let (tx, rx) = oneshot::channel::<Value>();
60        self.pending.lock().await.insert(id, tx);
61
62        if self.sender.send(msg).await.is_err() {
63            self.pending.lock().await.remove(&id);
64            return Ok(None);
65        }
66
67        match tokio::time::timeout(std::time::Duration::from_secs(30), rx).await {
68            Ok(Ok(result)) => {
69                if result.is_null() {
70                    return Ok(None);
71                }
72                match serde_json::from_value::<R>(result) {
73                    Ok(v) => Ok(Some(v)),
74                    Err(_) => Ok(None),
75                }
76            }
77            Ok(Err(_)) => Ok(None), // sender dropped
78            Err(_) => {
79                // timeout — clean up
80                self.pending.lock().await.remove(&id);
81                Ok(None)
82            }
83        }
84    }
85
86    // --- Lifecycle ---
87
88    pub async fn initialize(
89        &self,
90        params: InitializeParams,
91    ) -> Result<InitializeResult, ClientError> {
92        match self
93            .request::<InitializeResult>("initialize", params)
94            .await?
95        {
96            Some(r) => Ok(r),
97            None => Ok(InitializeResult::default()),
98        }
99    }
100    pub async fn initialized(&self, params: InitializedParams) {
101        self.notify("initialized", params).await;
102    }
103    pub async fn shutdown(&self) -> Result<(), ClientError> {
104        let _ = self.request::<()>("shutdown", ()).await;
105        Ok(())
106    }
107    pub async fn exit(&self) {
108        self.notify("exit", ()).await;
109    }
110
111    // --- Text Document Synchronization ---
112
113    pub async fn did_open(&self, params: DidOpenTextDocumentParams) {
114        self.notify("textDocument/didOpen", params).await;
115    }
116    pub async fn did_change(&self, params: DidChangeTextDocumentParams) {
117        self.notify("textDocument/didChange", params).await;
118    }
119    pub async fn did_save(&self, params: DidSaveTextDocumentParams) {
120        self.notify("textDocument/didSave", params).await;
121    }
122    pub async fn did_close(&self, params: DidCloseTextDocumentParams) {
123        self.notify("textDocument/didClose", params).await;
124    }
125
126    // --- Language Features ---
127
128    pub async fn hover(&self, params: HoverParams) -> Result<Option<Hover>, ClientError> {
129        self.request("textDocument/hover", params).await
130    }
131    pub async fn completion(
132        &self,
133        params: CompletionParams,
134    ) -> Result<Option<CompletionResponse>, ClientError> {
135        self.request("textDocument/completion", params).await
136    }
137    pub async fn completion_resolve(
138        &self,
139        params: CompletionItem,
140    ) -> Result<CompletionItem, ClientError> {
141        match self
142            .request::<CompletionItem>("completionItem/resolve", params.clone())
143            .await?
144        {
145            Some(r) => Ok(r),
146            None => Ok(params),
147        }
148    }
149
150    pub async fn signature_help(
151        &self,
152        params: SignatureHelpParams,
153    ) -> Result<Option<SignatureHelp>, ClientError> {
154        self.request("textDocument/signatureHelp", params).await
155    }
156
157    pub async fn goto_definition(
158        &self,
159        params: GotoDefinitionParams,
160    ) -> Result<Option<GotoDefinitionResponse>, ClientError> {
161        self.request("textDocument/definition", params).await
162    }
163    pub async fn goto_declaration(
164        &self,
165        params: GotoDefinitionParams,
166    ) -> Result<Option<GotoDefinitionResponse>, ClientError> {
167        self.request("textDocument/declaration", params).await
168    }
169    pub async fn goto_implementation(
170        &self,
171        params: GotoDefinitionParams,
172    ) -> Result<Option<GotoDefinitionResponse>, ClientError> {
173        self.request("textDocument/implementation", params).await
174    }
175    pub async fn goto_type_definition(
176        &self,
177        params: GotoDefinitionParams,
178    ) -> Result<Option<GotoDefinitionResponse>, ClientError> {
179        self.request("textDocument/typeDefinition", params).await
180    }
181
182    pub async fn references(
183        &self,
184        params: ReferenceParams,
185    ) -> Result<Option<Vec<Location>>, ClientError> {
186        self.request("textDocument/references", params).await
187    }
188    pub async fn document_highlight(
189        &self,
190        params: DocumentHighlightParams,
191    ) -> Result<Option<Vec<DocumentHighlight>>, ClientError> {
192        self.request("textDocument/documentHighlight", params).await
193    }
194    pub async fn document_symbol(
195        &self,
196        params: DocumentSymbolParams,
197    ) -> Result<Option<DocumentSymbolResponse>, ClientError> {
198        self.request("textDocument/documentSymbol", params).await
199    }
200
201    pub async fn code_action(
202        &self,
203        params: CodeActionParams,
204    ) -> Result<Option<CodeActionResponse>, ClientError> {
205        self.request("textDocument/codeAction", params).await
206    }
207    pub async fn code_action_resolve(&self, params: CodeAction) -> Result<CodeAction, ClientError> {
208        match self
209            .request::<CodeAction>("codeAction/resolve", params.clone())
210            .await?
211        {
212            Some(r) => Ok(r),
213            None => Ok(params),
214        }
215    }
216
217    pub async fn code_lens(
218        &self,
219        params: CodeLensParams,
220    ) -> Result<Option<Vec<CodeLens>>, ClientError> {
221        self.request("textDocument/codeLens", params).await
222    }
223    pub async fn code_lens_resolve(&self, params: CodeLens) -> Result<CodeLens, ClientError> {
224        match self
225            .request::<CodeLens>("codeLens/resolve", params.clone())
226            .await?
227        {
228            Some(r) => Ok(r),
229            None => Ok(params),
230        }
231    }
232
233    pub async fn formatting(
234        &self,
235        params: DocumentFormattingParams,
236    ) -> Result<Option<Vec<TextEdit>>, ClientError> {
237        self.request("textDocument/formatting", params).await
238    }
239    pub async fn range_formatting(
240        &self,
241        params: DocumentRangeFormattingParams,
242    ) -> Result<Option<Vec<TextEdit>>, ClientError> {
243        self.request("textDocument/rangeFormatting", params).await
244    }
245    pub async fn rename(&self, params: RenameParams) -> Result<Option<WorkspaceEdit>, ClientError> {
246        self.request("textDocument/rename", params).await
247    }
248    pub async fn prepare_rename(
249        &self,
250        params: TextDocumentPositionParams,
251    ) -> Result<Option<PrepareRenameResponse>, ClientError> {
252        self.request("textDocument/prepareRename", params).await
253    }
254
255    pub async fn semantic_tokens_full(
256        &self,
257        params: SemanticTokensParams,
258    ) -> Result<Option<SemanticTokensResult>, ClientError> {
259        self.request("textDocument/semanticTokens/full", params)
260            .await
261    }
262    pub async fn semantic_tokens_full_delta(
263        &self,
264        params: SemanticTokensDeltaParams,
265    ) -> Result<Option<SemanticTokensFullDeltaResult>, ClientError> {
266        self.request("textDocument/semanticTokens/full/delta", params)
267            .await
268    }
269    pub async fn semantic_tokens_range(
270        &self,
271        params: SemanticTokensRangeParams,
272    ) -> Result<Option<SemanticTokensRangeResult>, ClientError> {
273        self.request("textDocument/semanticTokens/range", params)
274            .await
275    }
276
277    pub async fn inlay_hint(
278        &self,
279        params: InlayHintParams,
280    ) -> Result<Option<Vec<InlayHint>>, ClientError> {
281        self.request("textDocument/inlayHint", params).await
282    }
283    pub async fn inlay_hint_resolve(&self, params: InlayHint) -> Result<InlayHint, ClientError> {
284        match self
285            .request::<InlayHint>("inlayHint/resolve", params.clone())
286            .await?
287        {
288            Some(r) => Ok(r),
289            None => Ok(params),
290        }
291    }
292
293    // --- Workspace Features ---
294
295    pub async fn symbol(
296        &self,
297        params: WorkspaceSymbolParams,
298    ) -> Result<Option<Vec<SymbolInformation>>, ClientError> {
299        self.request("workspace/symbol", params).await
300    }
301    pub async fn execute_command(
302        &self,
303        params: ExecuteCommandParams,
304    ) -> Result<Option<Value>, ClientError> {
305        self.request("workspace/executeCommand", params).await
306    }
307    pub async fn did_change_configuration(&self, params: DidChangeConfigurationParams) {
308        self.notify("workspace/didChangeConfiguration", params)
309            .await;
310    }
311    pub async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) {
312        self.notify("workspace/didChangeWatchedFiles", params).await;
313    }
314    pub async fn did_change_workspace_folders(&self, params: DidChangeWorkspaceFoldersParams) {
315        self.notify("workspace/didChangeWorkspaceFolders", params)
316            .await;
317    }
318}