lark_language_server/
lib.rs

1use lark_actor::{self, Actor, LspResponse, QueryRequest};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::collections::VecDeque;
5use std::io;
6use std::io::prelude::{Read, Write};
7use std::sync::mpsc::Sender;
8use url::Url;
9
10/// The command given by the IDE to the LSP server. These represent the actions of the user in the IDE,
11/// as well as actions the IDE might perform as a result of user actions (like cancelling a task)
12#[derive(Debug, Serialize, Deserialize)]
13#[serde(tag = "method")]
14#[allow(non_camel_case_types)]
15pub enum LSPCommand {
16    initialize {
17        id: usize,
18        params: languageserver_types::InitializeParams,
19    },
20    initialized,
21    #[serde(rename = "textDocument/didOpen")]
22    didOpen {
23        params: languageserver_types::DidOpenTextDocumentParams,
24    },
25    #[serde(rename = "textDocument/didChange")]
26    didChange {
27        params: languageserver_types::DidChangeTextDocumentParams,
28    },
29    #[serde(rename = "textDocument/hover")]
30    hover {
31        id: usize,
32        params: languageserver_types::TextDocumentPositionParams,
33    },
34    #[serde(rename = "textDocument/completion")]
35    completion {
36        id: usize,
37        params: languageserver_types::CompletionParams,
38    },
39    #[serde(rename = "textDocument/definition")]
40    definition {
41        id: usize,
42        params: languageserver_types::TextDocumentPositionParams,
43    },
44    #[serde(rename = "textDocument/references")]
45    references {
46        id: usize,
47        params: languageserver_types::TextDocumentPositionParams,
48    },
49    #[serde(rename = "textDocument/rename")]
50    rename {
51        id: usize,
52        params: languageserver_types::RenameParams,
53    },
54    #[serde(rename = "$/cancelRequest")]
55    cancelRequest {
56        params: languageserver_types::CancelParams,
57    },
58    #[serde(rename = "completionItem/resolve")]
59    completionItemResolve {
60        id: usize,
61        params: languageserver_types::CompletionItem,
62    },
63}
64
65/// A wrapper for responses back to the IDE from the LSP service. These must follow
66/// the JSON 2.0 RPC spec
67#[derive(Debug, Serialize, Deserialize)]
68pub struct JsonRPCResponse<T> {
69    jsonrpc: String,
70    pub id: usize,
71    pub result: T,
72}
73impl<T> JsonRPCResponse<T> {
74    pub fn new(id: usize, result: T) -> Self {
75        JsonRPCResponse {
76            jsonrpc: "2.0".into(),
77            id,
78            result,
79        }
80    }
81}
82
83/// A wrapper for proactive notifications to the IDE (eg. diagnostics). These must
84/// follow the JSON 2.0 RPC spec
85#[derive(Debug, Serialize, Deserialize)]
86pub struct JsonRPCNotification<T> {
87    jsonrpc: String,
88    pub method: String,
89    pub params: T,
90}
91impl<T> JsonRPCNotification<T> {
92    pub fn new(method: String, params: T) -> Self {
93        JsonRPCNotification {
94            jsonrpc: "2.0".into(),
95            method,
96            params,
97        }
98    }
99}
100
101/// Helper function to do the work of sending a result back to the IDE
102fn send_response<T: Serialize>(id: usize, result: T) {
103    let response = JsonRPCResponse::new(id, result);
104    let response_raw = serde_json::to_string(&response).unwrap();
105
106    print!("Content-Length: {}\r\n\r\n", response_raw.len());
107    print!("{}", response_raw);
108    let _ = io::stdout().flush();
109}
110
111/// Helper function to send a proactive notification back to the IDE
112fn send_notification<T: Serialize>(method: String, notice: T) {
113    let response = JsonRPCNotification::new(method, notice);
114    let response_raw = serde_json::to_string(&response).unwrap();
115
116    print!("Content-Length: {}\r\n\r\n", response_raw.len());
117    print!("{}", response_raw);
118    let _ = io::stdout().flush();
119}
120
121/// The LSP service is split into two parts:
122///   * The server, which handles incoming requests from the IDE
123///   * The responder, which sends out results when they're ready
124/// The server sends messages *to* the task manager for work that
125/// needs to be done. The responder receives messages *from* the
126/// task manager for work that has been accomplished.
127pub struct LspResponder;
128
129impl Actor for LspResponder {
130    type InMessage = LspResponse;
131
132    /// Receive messages from the task manager that contain the results of
133    /// a given task. This allows us to repond to the IDE in an orderly
134    /// manner.
135    fn receive_messages(&mut self, messages: &mut VecDeque<Self::InMessage>) {
136        match messages.pop_front().unwrap() {
137            LspResponse::Type(id, ty) => {
138                let result = languageserver_types::Hover {
139                    contents: languageserver_types::HoverContents::Scalar(
140                        languageserver_types::MarkedString::from_markdown(ty),
141                    ),
142                    range: None,
143                };
144
145                send_response(id, result);
146            }
147            LspResponse::Range(id, uri, range) => {
148                let result = languageserver_types::Location { uri, range };
149
150                send_response(id, result);
151            }
152            LspResponse::Ranges(id, vec_of_uri_range) => {
153                let result: Vec<languageserver_types::Location> = vec_of_uri_range
154                    .into_iter()
155                    .map(|x| languageserver_types::Location {
156                        uri: x.0,
157                        range: x.1,
158                    })
159                    .collect();
160
161                send_response(id, result);
162            }
163            LspResponse::WorkspaceEdits(id, vec_of_edits) => {
164                let mut map_of_edits: HashMap<Url, Vec<languageserver_types::TextEdit>> =
165                    HashMap::new();
166
167                for edit in vec_of_edits.into_iter() {
168                    let entry = map_of_edits.entry(edit.0).or_insert(vec![]);
169                    (*entry).push(languageserver_types::TextEdit {
170                        range: edit.1,
171                        new_text: edit.2,
172                    });
173                }
174
175                let result = languageserver_types::WorkspaceEdit {
176                    changes: Some(map_of_edits),
177                    document_changes: None,
178                };
179
180                send_response(id, result);
181            }
182            LspResponse::Nothing(id) => {
183                send_response(id, ());
184            }
185            LspResponse::Completions(id, completions) => {
186                let mut completion_items = vec![];
187
188                for completion in completions {
189                    completion_items.push(languageserver_types::CompletionItem::new_simple(
190                        completion.0,
191                        completion.1,
192                    ));
193                }
194
195                let result = languageserver_types::CompletionList {
196                    is_incomplete: false,
197                    items: completion_items,
198                };
199
200                send_response(id, result);
201            }
202            LspResponse::Initialized(id) => {
203                let result = languageserver_types::InitializeResult {
204                    capabilities: languageserver_types::ServerCapabilities {
205                        text_document_sync: Some(
206                            languageserver_types::TextDocumentSyncCapability::Kind(
207                                languageserver_types::TextDocumentSyncKind::Incremental,
208                            ),
209                        ),
210                        hover_provider: Some(true),
211                        /*
212                        completion_provider: Some(languageserver_types::CompletionOptions {
213                            resolve_provider: Some(true),
214                            trigger_characters: Some(vec![".".into()]),
215                        }),
216                        */
217                        completion_provider: None,
218                        signature_help_provider: None,
219                        definition_provider: Some(true),
220                        type_definition_provider: None,
221                        implementation_provider: None,
222                        references_provider: Some(true),
223                        document_highlight_provider: None,
224                        document_symbol_provider: None,
225                        workspace_symbol_provider: None,
226                        code_action_provider: None,
227                        code_lens_provider: None,
228                        document_formatting_provider: None,
229                        document_range_formatting_provider: None,
230                        document_on_type_formatting_provider: None,
231                        rename_provider: Some(
232                            languageserver_types::RenameProviderCapability::Simple(true),
233                        ),
234                        color_provider: None,
235                        folding_range_provider: None,
236                        execute_command_provider: None,
237                        workspace: None,
238                    },
239                };
240
241                send_response(id, result);
242            }
243            LspResponse::Diagnostics(url, diagnostics) => {
244                let lsp_diagnostics: Vec<languageserver_types::Diagnostic> = diagnostics
245                    .iter()
246                    .map(|(range, diag)| {
247                        languageserver_types::Diagnostic::new_simple(*range, diag.clone())
248                    })
249                    .collect();
250
251                let notice = languageserver_types::PublishDiagnosticsParams {
252                    uri: url,
253                    diagnostics: lsp_diagnostics,
254                };
255
256                send_notification("textDocument/publishDiagnostics".into(), notice);
257            }
258        }
259    }
260}
261
262/// The workhorse function for handling incoming requests from the IDE. This will
263/// take instructions from stdin sent by the IDE and then send them to the appropriate
264/// system.
265pub fn lsp_serve(send_to_query_channel: Sender<QueryRequest>) {
266    loop {
267        let mut input = String::new();
268        match io::stdin().read_line(&mut input) {
269            Ok(_) => {
270                let content_length_items: Vec<&str> = input.split(' ').collect();
271                if content_length_items[0] == "Content-Length:" {
272                    let num_bytes: usize = content_length_items[1].trim().parse().unwrap();
273                    let mut buffer = vec![0u8; num_bytes + 2];
274                    let _ = io::stdin().read_exact(&mut buffer);
275
276                    let buffer_string = String::from_utf8(buffer).unwrap();
277
278                    let command = serde_json::from_str::<LSPCommand>(&buffer_string);
279
280                    match command {
281                        Ok(LSPCommand::initialize { id, .. }) => {
282                            let _ = send_to_query_channel.send(QueryRequest::Initialize(id));
283                        }
284                        Ok(LSPCommand::initialized) => {
285                            //eprintln!("Initialized received");
286                        }
287                        Ok(LSPCommand::didOpen { params }) => {
288                            //eprintln!("didOpen: {:#?}", params);
289
290                            let _ = send_to_query_channel.send(QueryRequest::OpenFile(
291                                params.text_document.uri.clone(),
292                                params.text_document.text.clone(),
293                            ));
294                        }
295                        Ok(LSPCommand::didChange { params }) => {
296                            //eprintln!("didChange: {:#?}", params);
297
298                            let changes = params
299                                .content_changes
300                                .iter()
301                                .map(|x| (x.range.unwrap(), x.text.clone()))
302                                .collect();
303
304                            let _ = send_to_query_channel.send(QueryRequest::EditFile(
305                                params.text_document.uri.clone(),
306                                changes,
307                            ));
308                        }
309                        Ok(LSPCommand::hover { id, params }) => {
310                            //eprintln!("hover: id={} {:#?}", id, params);
311
312                            let _ = send_to_query_channel.send(QueryRequest::TypeAtPosition(
313                                id,
314                                params.text_document.uri.clone(),
315                                params.position.clone(),
316                            ));
317                        }
318                        Ok(LSPCommand::definition { id, params }) => {
319                            let _ = send_to_query_channel.send(QueryRequest::DefinitionAtPosition(
320                                id,
321                                params.text_document.uri.clone(),
322                                params.position.clone(),
323                            ));
324                        }
325                        Ok(LSPCommand::references { id, params }) => {
326                            let _ = send_to_query_channel.send(QueryRequest::ReferencesAtPosition(
327                                id,
328                                params.text_document.uri.clone(),
329                                params.position.clone(),
330                                true,
331                            ));
332                        }
333                        Ok(LSPCommand::rename { id, params }) => {
334                            let _ = send_to_query_channel.send(QueryRequest::RenameAtPosition(
335                                id,
336                                params.text_document.uri.clone(),
337                                params.position.clone(),
338                                params.new_name.clone(),
339                            ));
340                        }
341                        Ok(LSPCommand::completion { .. }) => {
342                            //eprintln!("completion: id={} {:#?}", id, params);
343                        }
344                        Ok(LSPCommand::completionItemResolve { .. }) => {
345                            //Note: this is here in case we need it, though it looks like it's only used
346                            //for more expensive computations on a completion (like fetching the docs)
347                            //eprintln!("resolve completion item: id={} {:#?}", id, params);
348                        }
349                        Ok(LSPCommand::cancelRequest {
350                            params: languageserver_types::CancelParams { id },
351                        }) => match id {
352                            languageserver_types::NumberOrString::Number(_num) => {
353                                //eprintln!("cancelling item: id={}", num);
354                                /* FIXME FIXME: removing cancelling for the time being
355                                let _ = send_to_manager_channel
356                                    .send(MsgToManager::Cancel(num as usize));
357                                */
358                            }
359                            _ => unimplemented!(
360                                "Non-number cancellation IDs not currently supported"
361                            ),
362                        },
363                        Err(e) => eprintln!("Error handling command: {:?}", e),
364                    }
365                }
366            }
367            Err(error) => eprintln!("error: {}", error),
368        }
369    }
370}