1use std::{collections::HashMap, sync::Arc};
2
3use bevy_ecs::{
4 bundle::Bundle,
5 component::Component,
6 entity::Entity,
7 schedule::ScheduleLabel,
8 world::{CommandQueue, World},
9};
10use completion::CompletionRequest;
11use futures::lock::Mutex;
12use goto_type::GotoTypeRequest;
13use references::ReferencesRequest;
14use request::{GotoTypeDefinitionParams, GotoTypeDefinitionResponse};
15use ropey::Rope;
16use tower_lsp::{jsonrpc::Result, LanguageServer};
17use tracing::info;
18
19use crate::{
20 feature::goto_definition::GotoDefinitionRequest,
21 lsp_types::{request::SemanticTokensRefresh, *},
22 prelude::*,
23 Startup,
24};
25
26#[derive(Debug)]
27pub struct Backend {
28 entities: Arc<Mutex<HashMap<String, Entity>>>,
29 sender: CommandSender,
30 #[allow(unused)]
31 client: tower_lsp::Client,
32 semantic_tokens: Vec<SemanticTokenType>,
33}
34
35impl Backend {
36 pub fn new(
37 sender: CommandSender,
38 client: tower_lsp::Client,
39 tokens: Vec<SemanticTokenType>,
40 ) -> Self {
41 Self {
42 entities: Default::default(),
43 sender,
44 client,
45 semantic_tokens: tokens,
46 }
47 }
48
49 async fn run<T: Send + Sync + 'static>(
50 &self,
51 f: impl FnOnce(&mut World) -> T + Send + Sync + 'static,
52 ) -> Option<T> {
53 let (tx, rx) = futures::channel::oneshot::channel();
54 let mut commands = CommandQueue::default();
55 commands.push(move |world: &mut World| {
56 let o = f(world);
57 if let Err(_) = tx.send(o) {
58 tracing::error!("Failed to run schedule for {}", stringify!(T));
59 };
60 });
61
62 if let Err(e) = self.sender.0.unbounded_send(commands) {
63 tracing::error!("Failed to send commands {}", e);
64 return None;
65 }
66
67 rx.await.ok()
68 }
69
70 async fn run_schedule<T: Component>(
71 &self,
72 entity: Entity,
73 schedule: impl ScheduleLabel + Clone,
74 param: impl Bundle,
75 ) -> Option<T> {
76 let (tx, rx) = futures::channel::oneshot::channel();
77
78 let mut commands = CommandQueue::default();
79 commands.push(move |world: &mut World| {
80 world.entity_mut(entity).insert(param);
81 world.run_schedule(schedule.clone());
82 if let Err(_) = tx.send(world.entity_mut(entity).take::<T>()) {
83 tracing::error!(name: "Failed to run schedule", "Failed to run schedule {:?}", schedule);
84 };
85 });
86
87 if let Err(e) = self.sender.0.unbounded_send(commands) {
88 tracing::error!("Failed to send commands {}", e);
89 return None;
90 }
91
92 rx.await.unwrap_or_default()
93 }
94}
95
96#[tower_lsp::async_trait]
97impl LanguageServer for Backend {
98 #[tracing::instrument(skip(self, init))]
99 async fn initialize(&self, init: InitializeParams) -> Result<InitializeResult> {
100 info!("Initialize");
101
102 let workspaces = init.workspace_folders.clone().unwrap_or_default();
103 let config: Config =
104 serde_json::from_value(init.initialization_options.clone().unwrap_or_default())
105 .unwrap_or_default();
106
107 let mut server_config = ServerConfig { config, workspaces };
108
109 let fs = self.run(|w| w.resource::<Fs>().clone()).await.unwrap();
110 if let Some(global) = LocalConfig::global(&fs).await {
111 server_config.config.local.combine(global);
112 }
113
114 if let Some(root) = init.root_uri.as_ref() {
115 if let Some(local) = LocalConfig::local(&fs, root).await {
116 server_config.config.local.combine(local);
117 }
118 }
119
120 info!("Initialize {:?}", server_config);
121 let document_selectors: Vec<_> = [
122 ("sparql", server_config.config.sparql.unwrap_or(true)),
123 ("turtle", server_config.config.turtle.unwrap_or(true)),
124 ("jsonld", server_config.config.jsonld.unwrap_or(true)),
125 ]
126 .into_iter()
127 .filter(|(_, x)| *x)
128 .map(|(x, _)| DocumentFilter {
129 language: Some(String::from(x)),
130 scheme: None,
131 pattern: None,
132 })
133 .collect();
134
135 self.run(|world| {
136 world.insert_resource(server_config);
137 world.run_schedule(Startup);
138 })
139 .await;
140
141 Ok(InitializeResult {
143 server_info: None,
144 capabilities: ServerCapabilities {
145 inlay_hint_provider: Some(OneOf::Left(true)),
146 text_document_sync: Some(TextDocumentSyncCapability::Kind(
147 TextDocumentSyncKind::FULL,
148 )),
149 code_action_provider: None,
150 completion_provider: Some(CompletionOptions {
151 resolve_provider: Some(false),
152 trigger_characters: Some(vec![String::from(":")]),
153 work_done_progress_options: Default::default(),
154 all_commit_characters: None,
155 completion_item: None,
156 }),
157 type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
159 references_provider: Some(OneOf::Left(true)),
160 hover_provider: Some(HoverProviderCapability::Simple(true)),
161 definition_provider: Some(OneOf::Left(true)),
162 document_formatting_provider: Some(OneOf::Left(true)),
163 semantic_tokens_provider: Some(
164 SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(
165 SemanticTokensRegistrationOptions {
166 text_document_registration_options: {
167 TextDocumentRegistrationOptions {
168 document_selector: Some(document_selectors),
169 }
170 },
171 semantic_tokens_options: SemanticTokensOptions {
172 work_done_progress_options: WorkDoneProgressOptions::default(),
173 legend: SemanticTokensLegend {
174 token_types: self.semantic_tokens.clone(),
175 token_modifiers: vec![],
176 },
177 range: Some(false),
178 full: Some(SemanticTokensFullOptions::Bool(true)),
179 },
180 static_registration_options: StaticRegistrationOptions::default(),
181 },
182 ),
183 ),
184 rename_provider: Some(OneOf::Right(RenameOptions {
185 prepare_provider: Some(true),
186 work_done_progress_options: Default::default(),
187 })),
188 ..ServerCapabilities::default()
189 },
190 })
191 }
192
193 async fn did_change_workspace_folders(&self, params: DidChangeWorkspaceFoldersParams) -> () {
194 self.run(move |world| {
195 let mut config = world.resource_mut::<ServerConfig>();
196 let WorkspaceFoldersChangeEvent { added, removed } = params.event;
197
198 for r in removed {
199 if let Some(idx) = config.workspaces.iter().position(|x| x == &r) {
200 config.workspaces.remove(idx);
201 }
202 }
203
204 config.workspaces.extend(added);
206 })
207 .await;
208 ()
209 }
210
211 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
212 async fn semantic_tokens_full(
213 &self,
214 params: SemanticTokensParams,
215 ) -> Result<Option<SemanticTokensResult>> {
216 info!("semantic tokens full");
217 let uri = params.text_document.uri.as_str();
218 let entity = {
219 let e = {
220 let map = self.entities.lock().await;
221 if let Some(entity) = map.get(uri) {
222 Some(entity.clone())
223 } else {
224 info!("Didn't find entity {} retrying", uri);
225 None
226 }
227 };
228
229 if let Some(e) = e {
230 e
231 } else {
232 let map = self.entities.lock().await;
233 if let Some(entity) = map.get(uri) {
234 entity.clone()
235 } else {
236 info!("Didn't find entty {} stopping", uri);
237 return Ok(None);
238 }
239 }
240 };
241
242 if let Some(res) = self
243 .run_schedule::<HighlightRequest>(entity, SemanticLabel, HighlightRequest(vec![]))
244 .await
245 {
246 Ok(Some(SemanticTokensResult::Tokens(
247 crate::lsp_types::SemanticTokens {
248 result_id: None,
249 data: res.0,
250 },
251 )))
252 } else {
253 info!("resulitng in no tokens");
254 Ok(None)
255 }
256 }
257
258 #[tracing::instrument(skip(self))]
259 async fn shutdown(&self) -> Result<()> {
260 info!("Shutting down!");
261
262 Ok(())
263 }
264
265 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document_position.text_document.uri.as_str()))]
266 async fn references(&self, params: ReferenceParams) -> Result<Option<Vec<Location>>> {
267 let entity = {
268 let map = self.entities.lock().await;
269 if let Some(entity) = map.get(params.text_document_position.text_document.uri.as_str())
270 {
271 entity.clone()
272 } else {
273 return Ok(None);
274 }
275 };
276
277 let mut pos = params.text_document_position.position;
278 pos.character = if pos.character > 0 {
279 pos.character - 1
280 } else {
281 pos.character
282 };
283
284 let arr = self
285 .run_schedule::<ReferencesRequest>(
286 entity,
287 ReferencesLabel,
288 (PositionComponent(pos), ReferencesRequest(Vec::new())),
289 )
290 .await
291 .map(|x| x.0);
292
293 Ok(arr)
294 }
295
296 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
297 async fn prepare_rename(
298 &self,
299 params: TextDocumentPositionParams,
300 ) -> Result<Option<PrepareRenameResponse>> {
301 let entity = {
302 let map = self.entities.lock().await;
303 if let Some(entity) = map.get(params.text_document.uri.as_str()) {
304 entity.clone()
305 } else {
306 return Ok(None);
307 }
308 };
309
310 let mut pos = params.position;
311 pos.character = if pos.character > 0 {
312 pos.character - 1
313 } else {
314 pos.character
315 };
316
317 let resp = self
318 .run_schedule::<PrepareRenameRequest>(
319 entity,
320 PrepareRenameLabel,
321 PositionComponent(pos),
322 )
323 .await
324 .map(|x| PrepareRenameResponse::RangeWithPlaceholder {
325 range: x.range,
326 placeholder: x.placeholder,
327 });
328
329 Ok(resp)
330 }
331
332 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document_position.text_document.uri.as_str()))]
333 async fn rename(&self, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
334 let entity = {
335 let map = self.entities.lock().await;
336 if let Some(entity) = map.get(params.text_document_position.text_document.uri.as_str())
337 {
338 entity.clone()
339 } else {
340 return Ok(None);
341 }
342 };
343
344 let mut pos = params.text_document_position.position;
345 pos.character = if pos.character > 0 {
346 pos.character - 1
347 } else {
348 pos.character
349 };
350
351 let mut change_map: HashMap<crate::lsp_types::Url, Vec<TextEdit>> = HashMap::new();
352 if let Some(changes) = self
353 .run_schedule::<RenameEdits>(
354 entity,
355 RenameLabel,
356 (
357 PositionComponent(pos),
358 RenameEdits(Vec::new(), params.new_name),
359 ),
360 )
361 .await
362 {
363 for (url, change) in changes.0 {
364 let entry = change_map.entry(url);
365 entry.or_default().push(change);
366 }
367 }
368 Ok(Some(WorkspaceEdit::new(change_map)))
369 }
370
371 async fn hover(&self, params: HoverParams) -> Result<Option<crate::lsp_types::Hover>> {
372 let request: HoverRequest = HoverRequest::default();
373
374 let entity = {
375 let map = self.entities.lock().await;
376 if let Some(entity) = map.get(
377 params
378 .text_document_position_params
379 .text_document
380 .uri
381 .as_str(),
382 ) {
383 entity.clone()
384 } else {
385 return Ok(None);
386 }
387 };
388
389 let mut pos = params.text_document_position_params.position;
390 pos.character = if pos.character > 0 {
391 pos.character - 1
392 } else {
393 pos.character
394 };
395
396 if let Some(hover) = self
397 .run_schedule::<HoverRequest>(entity, HoverLabel, (request, PositionComponent(pos)))
398 .await
399 {
400 if hover.0.len() > 0 {
401 return Ok(Some(crate::lsp_types::Hover {
402 contents: crate::lsp_types::HoverContents::Array(
403 hover.0.into_iter().map(MarkedString::String).collect(),
404 ),
405 range: hover.1,
406 }));
407 }
408 }
409
410 Ok(None)
411 }
412
413 async fn inlay_hint(&self, params: InlayHintParams) -> Result<Option<Vec<InlayHint>>> {
414 info!("Inlay hints called");
415 let uri = params.text_document.uri.as_str();
416 let entity = {
417 let map = self.entities.lock().await;
418 if let Some(entity) = map.get(uri) {
419 entity.clone()
420 } else {
421 info!("Didn't find entity {}", uri);
422 return Ok(None);
423 }
424 };
425
426 let request = self
427 .run_schedule::<InlayRequest>(entity, InlayLabel, InlayRequest(None))
428 .await;
429
430 Ok(request.and_then(|x| x.0))
431 }
432
433 #[tracing::instrument(skip(self))]
434 async fn formatting(&self, params: DocumentFormattingParams) -> Result<Option<Vec<TextEdit>>> {
435 let uri = params.text_document.uri.as_str();
436 let entity = {
437 let map = self.entities.lock().await;
438 if let Some(entity) = map.get(uri) {
439 entity.clone()
440 } else {
441 info!("Didn't find entity {}", uri);
442 return Ok(None);
443 }
444 };
445
446 let request = self
447 .run_schedule::<FormatRequest>(entity, FormatLabel, FormatRequest(None))
448 .await;
449 Ok(request.and_then(|x| x.0))
450 }
451
452 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
453 async fn did_open(&self, params: DidOpenTextDocumentParams) {
454 let item = params.text_document;
455 let url = item.uri.as_str().to_string();
456
457 tracing::info!("Did open");
458
459 let lang_id = Some(item.language_id.clone());
460 let spawn = spawn_or_insert(
461 item.uri.clone(),
462 (
463 Source(item.text.clone()),
464 Label(item.uri.clone()),
465 RopeC(Rope::from_str(&item.text)),
466 Wrapped(item),
467 DocumentLinks(Vec::new()),
468 Open,
469 Types(HashMap::new()),
470 ),
471 lang_id,
472 (),
473 );
474
475 let entity = self
476 .run(|world| {
477 let id = spawn(world);
478 world.run_schedule(ParseLabel);
479 world.flush();
480 info!("Running diagnostics");
481 world.run_schedule(DiagnosticsLabel);
482 info!("Done diagnostics");
483 id
484 })
485 .await;
486
487 if let Some(entity) = entity {
488 self.entities.lock().await.insert(url, entity);
489 }
490
491 info!("Requesting tokens refresh");
492 let _ = self.client.send_request::<SemanticTokensRefresh>(()).await;
493 info!("Semantic tokens refresh");
494 }
495
496 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
497 async fn did_change(&self, params: DidChangeTextDocumentParams) {
498 let entity = {
499 let map = self.entities.lock().await;
500 if let Some(entity) = map.get(params.text_document.uri.as_str()) {
501 entity.clone()
502 } else {
503 info!("Didn't find entity {}", params.text_document.uri.as_str());
504 return;
505 }
506 };
507
508 let change = {
509 if let Some(c) = params.content_changes.into_iter().next() {
510 c
511 } else {
512 return;
513 }
514 };
515
516 self.run(move |world| {
517 let rope_c = RopeC(Rope::from_str(&change.text));
518 world
519 .entity_mut(entity)
520 .insert((Source(change.text), rope_c));
521 world.run_schedule(ParseLabel);
522 world.flush();
523 info!("Running diagnostics");
524 world.run_schedule(DiagnosticsLabel);
525 info!("Running diagnostics done");
526 })
527 .await;
528 }
529
530 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
531 async fn did_save(&self, params: DidSaveTextDocumentParams) {
532 let _ = params;
533
534 info!("Did save");
535 self.run(move |world| {
536 world.run_schedule(SaveLabel);
537
538 info!("Ran OnSave Schedule");
539 })
540 .await;
541 }
542
543 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document_position_params.text_document.uri.as_str()))]
544 async fn goto_definition(
545 &self,
546 params: GotoDefinitionParams,
547 ) -> Result<Option<GotoDefinitionResponse>> {
548 let entity = {
549 let map = self.entities.lock().await;
550 if let Some(entity) = map.get(
551 params
552 .text_document_position_params
553 .text_document
554 .uri
555 .as_str(),
556 ) {
557 entity.clone()
558 } else {
559 return Ok(None);
560 }
561 };
562
563 let mut pos = params.text_document_position_params.position;
564 pos.character = if pos.character > 0 {
565 pos.character - 1
566 } else {
567 pos.character
568 };
569
570 let arr = self
571 .run_schedule::<GotoDefinitionRequest>(
572 entity,
573 GotoDefinitionLabel,
574 (PositionComponent(pos), GotoDefinitionRequest(Vec::new())),
575 )
576 .await
577 .map(|x| GotoDefinitionResponse::Array(x.0));
578
579 Ok(arr)
580 }
581
582 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document_position_params.text_document.uri.as_str()))]
583 async fn goto_type_definition(
584 &self,
585 params: GotoTypeDefinitionParams,
586 ) -> Result<Option<GotoTypeDefinitionResponse>> {
587 let entity = {
588 let map = self.entities.lock().await;
589 if let Some(entity) = map.get(
590 params
591 .text_document_position_params
592 .text_document
593 .uri
594 .as_str(),
595 ) {
596 entity.clone()
597 } else {
598 return Ok(None);
599 }
600 };
601
602 let mut pos = params.text_document_position_params.position;
603 pos.character = if pos.character > 0 {
604 pos.character - 1
605 } else {
606 pos.character
607 };
608
609 let arr = self
610 .run_schedule::<GotoTypeRequest>(
611 entity,
612 GotoTypeLabel,
613 (PositionComponent(pos), GotoTypeRequest(Vec::new())),
614 )
615 .await
616 .map(|x| GotoTypeDefinitionResponse::Array(x.0));
617
618 Ok(arr)
619 }
620
621 #[tracing::instrument(skip(self, params), fields(uri = %params.text_document_position.text_document.uri.as_str()))]
622 async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
623 let entity = {
624 let map = self.entities.lock().await;
625 if let Some(entity) = map.get(params.text_document_position.text_document.uri.as_str())
626 {
627 entity.clone()
628 } else {
629 return Ok(None);
630 }
631 };
632
633 let mut pos = params.text_document_position.position;
636 pos.character = if pos.character > 0 {
637 pos.character - 1
638 } else {
639 pos.character
640 };
641
642 let completions: Option<Vec<crate::lsp_types::CompletionItem>> = self
643 .run_schedule::<CompletionRequest>(
644 entity,
645 CompletionLabel,
646 (CompletionRequest(vec![]), PositionComponent(pos)),
647 )
648 .await
649 .map(|x| x.0.into_iter().map(|x| x.into()).collect());
650
651 Ok(completions.map(|c| CompletionResponse::Array(c)))
652 }
653}