bend_language_server/server/
mod.rs

1use dashmap::DashMap;
2use tower_lsp::jsonrpc::Result;
3use tower_lsp::lsp_types::{self as lsp, SemanticTokensRangeResult};
4use tower_lsp::{Client, LanguageServer};
5
6use crate::core::diagnostics;
7use crate::core::document::{self, Document};
8use crate::core::semantic_token;
9use crate::utils::lsp_log;
10
11pub struct Backend {
12    /// Connection to the client, used to send data
13    pub client: Client,
14    /// Currently open documents
15    pub open_docs: DashMap<lsp::Url, document::Document>,
16}
17
18#[tower_lsp::async_trait]
19impl LanguageServer for Backend {
20    // All of these represent messages the server may receive from the client.
21    // See the automatic documentation generated by `tower_lsp` to understand what each method does.
22
23    async fn initialize(&self, _: lsp::InitializeParams) -> Result<lsp::InitializeResult> {
24        let capabilities = Self::capabilities();
25
26        Ok(lsp::InitializeResult {
27            server_info: Some(lsp::ServerInfo {
28                name: "Bend Language Server".into(),
29                version: Some(env!("CARGO_PKG_VERSION").into()),
30            }),
31            offset_encoding: None,
32            capabilities,
33        })
34    }
35
36    async fn initialized(&self, _: lsp::InitializedParams) {
37        let values = self
38            .client
39            .configuration(vec![lsp::ConfigurationItem {
40                scope_uri: Some(lsp::Url::parse("file:///libraryPaths").unwrap()),
41                section: Some("bend-language-server".to_string()),
42            }])
43            .await;
44
45        if let Ok(_val) = values {
46            // TODO: configuration
47        }
48
49        self.publish_all_diagnostics().await;
50
51        lsp_log::info!(self.client, "bend-language-server initialized");
52    }
53
54    async fn shutdown(&self) -> Result<()> {
55        Ok(())
56    }
57
58    async fn did_open(&self, params: lsp::DidOpenTextDocumentParams) {
59        // This is called when the client opens a new document.
60        // We get the entire text of the document; let's store it.
61        lsp_log::info!(self.client, "opening file at {}", params.text_document.uri);
62
63        self.open_doc(params.text_document.uri.clone(), params.text_document.text);
64        self.publish_diagnostics(&params.text_document.uri).await;
65    }
66
67    async fn did_change_configuration(&self, _params: lsp::DidChangeConfigurationParams) {
68        lsp_log::info!(self.client, "changing language server configurations");
69
70        // TODO: configuration
71
72        self.publish_all_diagnostics().await;
73    }
74
75    async fn did_change(&self, params: lsp::DidChangeTextDocumentParams) {
76        lsp_log::log!(
77            self.client,
78            "getting new text from {}",
79            params.text_document.uri
80        );
81
82        self.update_document(&params.text_document.uri, |doc| {
83            for event in &params.content_changes {
84                doc.update_whole_text(&event.text);
85            }
86        });
87    }
88
89    async fn did_save(&self, params: lsp::DidSaveTextDocumentParams) {
90        // Called when document is saved.
91        // Update diagnostics (when we have them!)
92        lsp_log::log!(
93            self.client,
94            "document saved at {}",
95            params.text_document.uri
96        );
97
98        let url = &params.text_document.uri;
99        self.publish_diagnostics(url).await;
100    }
101
102    async fn semantic_tokens_full(
103        &self,
104        params: lsp::SemanticTokensParams,
105    ) -> Result<Option<lsp::SemanticTokensResult>> {
106        lsp_log::info!(self.client, "generating full semantic tokens");
107
108        let uri = params.text_document.uri;
109        let semantic_tokens =
110            self.read_document_mut(&uri, |doc| Some(semantic_token::semantic_tokens(doc, None)));
111
112        let token_amount = semantic_tokens.as_ref().map(|ts| ts.len()).unwrap_or(0);
113        lsp_log::info!(self.client, "got {} tokens", token_amount);
114
115        Ok(semantic_tokens.map(|tokens| {
116            lsp::SemanticTokensResult::Tokens(lsp::SemanticTokens {
117                result_id: None,
118                data: tokens,
119            })
120        }))
121    }
122
123    async fn semantic_tokens_range(
124        &self,
125        params: lsp::SemanticTokensRangeParams,
126    ) -> Result<Option<SemanticTokensRangeResult>> {
127        let range = params.range;
128        lsp_log::info!(
129            self.client,
130            "generating range {}:{}-{}:{} semantic tokens",
131            range.start.line,
132            range.start.character,
133            range.end.line,
134            range.end.character
135        );
136
137        let uri = params.text_document.uri;
138        let semantic_tokens = self.read_document_mut(&uri, |doc| {
139            Some(semantic_token::semantic_tokens(doc, Some(range)))
140        });
141
142        let token_amount = semantic_tokens.as_ref().map(|ts| ts.len()).unwrap_or(0);
143        lsp_log::info!(self.client, "got {} tokens", token_amount);
144        lsp_log::debug!(self.client, "tokens: {:?}", semantic_tokens);
145
146        Ok(semantic_tokens.map(|tokens| {
147            lsp::SemanticTokensRangeResult::Tokens(lsp::SemanticTokens {
148                result_id: None,
149                data: tokens,
150            })
151        }))
152    }
153
154    async fn completion(
155        &self,
156        _: lsp::CompletionParams,
157    ) -> Result<Option<lsp::CompletionResponse>> {
158        Ok(Some(lsp::CompletionResponse::Array(vec![
159            lsp::CompletionItem::new_simple("Hello".to_string(), "Some detail".to_string()),
160            lsp::CompletionItem::new_simple("Bye/Bye".to_string(), "More detail".to_string()),
161        ])))
162    }
163
164    async fn hover(&self, _: lsp::HoverParams) -> Result<Option<lsp::Hover>> {
165        Ok(Some(lsp::Hover {
166            contents: lsp::HoverContents::Scalar(lsp::MarkedString::String(
167                "You're hovering!".to_string(),
168            )),
169            range: None,
170        }))
171    }
172}
173
174impl Backend {
175    pub fn new(client: Client) -> Self {
176        Self {
177            client,
178            open_docs: DashMap::new(),
179        }
180    }
181
182    fn capabilities() -> lsp::ServerCapabilities {
183        lsp::ServerCapabilities {
184            text_document_sync: Some(lsp::TextDocumentSyncCapability::Options(
185                lsp::TextDocumentSyncOptions {
186                    open_close: Some(true),
187                    change: Some(lsp::TextDocumentSyncKind::FULL),
188                    will_save: None,
189                    will_save_wait_until: None,
190                    save: Some(lsp::TextDocumentSyncSaveOptions::Supported(true)),
191                },
192            )),
193            semantic_tokens_provider: Some(
194                lsp::SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(
195                    lsp::SemanticTokensRegistrationOptions {
196                        text_document_registration_options: {
197                            lsp::TextDocumentRegistrationOptions {
198                                document_selector: Some(vec![lsp::DocumentFilter {
199                                    language: Some("bend".into()),
200                                    scheme: Some("file".into()),
201                                    pattern: None,
202                                }]),
203                            }
204                        },
205                        semantic_tokens_options: lsp::SemanticTokensOptions {
206                            work_done_progress_options: lsp::WorkDoneProgressOptions::default(),
207                            legend: lsp::SemanticTokensLegend {
208                                token_types: semantic_token::LEGEND_TOKEN_TYPE.to_vec(),
209                                token_modifiers: vec![],
210                            },
211                            range: Some(true),
212                            full: Some(lsp::SemanticTokensFullOptions::Bool(true)),
213                        },
214                        static_registration_options: lsp::StaticRegistrationOptions::default(),
215                    },
216                ),
217            ),
218            // definition_provider: Some(lsp::OneOf::Left(true)),
219            // completion_provider: Some(lsp::CompletionOptions {
220            //     resolve_provider: Some(false),
221            //     trigger_characters: Some(vec!["/".into()]),
222            //     all_commit_characters: None,
223            //     work_done_progress_options: Default::default(),
224            //     completion_item: None,
225            // }),
226            // hover_provider: Some(lsp::HoverProviderCapability::Simple(false)),
227            ..Default::default()
228        }
229    }
230
231    /// Publish diagnostics for every open file.
232    async fn publish_all_diagnostics(&self) {
233        for refer in self.open_docs.iter() {
234            self.publish_diagnostics(refer.key()).await;
235        }
236    }
237
238    /// Publish diagnostics for document `url`.
239    async fn publish_diagnostics(&self, url: &lsp::Url) {
240        let diags = self
241            .read_document(url, |doc| {
242                Some(diagnostics::lsp_diagnostics(doc, &diagnostics::check(doc)))
243            })
244            .unwrap_or_default();
245
246        lsp_log::info!(self.client, "got diagnostics: {:?}", diags);
247
248        self.client
249            .publish_diagnostics(url.clone(), diags, None)
250            .await;
251    }
252
253    /// Update the document at `url` using function `updater`.
254    fn update_document<F>(&self, url: &lsp::Url, mut updater: F)
255    where
256        F: FnMut(&mut Document),
257    {
258        if let Some(mut doc) = self.open_docs.get_mut(url) {
259            updater(doc.value_mut());
260        }
261    }
262
263    /// Read the contents of `url` using function `reader`.
264    fn read_document<F, T>(&self, url: &lsp::Url, reader: F) -> Option<T>
265    where
266        F: Fn(&document::Document) -> Option<T>,
267    {
268        self.open_docs
269            .get(url)
270            .and_then(|refer| reader(refer.value()))
271    }
272
273    /// Read the contents of `url` using function `reader`, possibly changing the document.
274    fn read_document_mut<F, T>(&self, url: &lsp::Url, mut updater: F) -> Option<T>
275    where
276        F: FnMut(&mut Document) -> Option<T>,
277    {
278        self.open_docs
279            .get_mut(url)
280            .and_then(|mut refer| updater(refer.value_mut()))
281    }
282
283    /// Open a new document at `url` with its contents as a parameter.
284    fn open_doc(&self, url: lsp::Url, text: String) {
285        self.open_docs
286            .insert(url.clone(), Document::new_with_text(url, &text));
287    }
288
289    // Open a new document at `url` with its contents from the file system.
290    // fn open_doc_from_path(&self, url: lsp::Url) {
291    //     if let Ok(text) = fs::read_to_string(url.to_file_path().unwrap()) {
292    //         self.open_doc(url, text);
293    //     }
294    // }
295}