bend_language_server/server/
mod.rs1use 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 pub client: Client,
14 pub open_docs: DashMap<lsp::Url, document::Document>,
16}
17
18#[tower_lsp::async_trait]
19impl LanguageServer for Backend {
20 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 }
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 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(¶ms.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 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(¶ms.text_document.uri, |doc| {
83 for event in ¶ms.content_changes {
84 doc.update_whole_text(&event.text);
85 }
86 });
87 }
88
89 async fn did_save(&self, params: lsp::DidSaveTextDocumentParams) {
90 lsp_log::log!(
93 self.client,
94 "document saved at {}",
95 params.text_document.uri
96 );
97
98 let url = ¶ms.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 ..Default::default()
228 }
229 }
230
231 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 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 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 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 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 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 }