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 crate::text::LineIndex;
16use tracing::{debug, error, info, instrument};
17
18use crate::{
19 client::Client,
20 feature::{
21 code_action::{CodeActionRequest, Label as CodeActionLabel},
22 goto_definition::GotoDefinitionRequest,
23 },
24 lsp_types::{request::SemanticTokensRefresh, *},
25 prelude::*,
26 Started, Startup,
27};
28
29#[derive(Debug)]
36pub enum ServerError {}
37
38pub type Result<T> = std::result::Result<T, ServerError>;
40
41pub struct Backend<C> {
47 entities: Arc<Mutex<HashMap<String, Entity>>>,
48 sender: CommandSender,
49 client: C,
50 semantic_tokens: Vec<SemanticTokenType>,
51}
52
53impl<C> std::fmt::Debug for Backend<C> {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 f.debug_struct("Backend")
56 .field("semantic_tokens", &self.semantic_tokens)
57 .finish_non_exhaustive()
58 }
59}
60
61impl<C: Client> Backend<C> {
62 pub fn new(sender: CommandSender, client: C, tokens: Vec<SemanticTokenType>) -> Self {
63 Self {
64 entities: Default::default(),
65 sender,
66 client,
67 semantic_tokens: tokens,
68 }
69 }
70
71 async fn run<T: Send + Sync + 'static>(
72 &self,
73 f: impl FnOnce(&mut World) -> T + Send + Sync + 'static,
74 ) -> Option<T> {
75 let (tx, rx) = futures::channel::oneshot::channel();
76 let mut commands = CommandQueue::default();
77 commands.push(move |world: &mut World| {
78 let o = f(world);
79 if let Err(_) = tx.send(o) {
80 error!("Failed to run schedule for {}", stringify!(T));
81 };
82 });
83
84 if let Err(e) = self.sender.0.unbounded_send(commands) {
85 error!("Failed to send commands {}", e);
86 return None;
87 }
88
89 rx.await.ok()
90 }
91
92 async fn run_schedule<T: Component>(
93 &self,
94 entity: Entity,
95 schedule: impl ScheduleLabel + Clone,
96 param: impl Bundle,
97 ) -> Option<T> {
98 let (tx, rx) = futures::channel::oneshot::channel();
99
100 let mut commands = CommandQueue::default();
101 commands.push(move |world: &mut World| {
102 world.entity_mut(entity).insert(param);
103 world.run_schedule(schedule.clone());
104 if let Err(_) = tx.send(world.entity_mut(entity).take::<T>()) {
105 error!(name: "Failed to run schedule", "Failed to run schedule {:?}", schedule);
106 };
107 });
108
109 if let Err(e) = self.sender.0.unbounded_send(commands) {
110 error!("Failed to send commands {}", e);
111 return None;
112 }
113
114 rx.await.unwrap_or_default()
115 }
116
117 async fn get_entity(&self, uri: &str) -> Option<Entity> {
118 let map = self.entities.lock().await;
119 map.get(uri).copied()
120 }
121
122 async fn is_disabled(&self, feature: Disabled) -> bool {
124 self.run(move |world| {
125 world
126 .resource::<ServerConfig>()
127 .config
128 .local
129 .is_disabled(feature)
130 })
131 .await
132 .unwrap_or(false)
133 }
134
135 fn adjust_position(pos: &mut Position) {
136 pos.character = pos.character.saturating_sub(1);
137 }
138}
139
140impl<C: Client> Backend<C> {
141 #[instrument(skip(self, init))]
142 pub async fn initialize(&self, init: InitializeParams) -> Result<InitializeResult> {
143 info!("Initialize");
144
145 let workspaces = init.workspace_folders.clone().unwrap_or_default();
146 let config: Config =
147 serde_json::from_value(init.initialization_options.clone().unwrap_or_default())
148 .unwrap_or_default();
149
150 let mut server_config = ServerConfig { config, workspaces };
151
152 let fs = self.run(|w| w.resource::<Fs>().clone()).await.unwrap();
153 if let Some(global) = LocalConfig::global(&fs).await {
154 server_config.config.local.combine(global);
155 }
156
157 if let Some(root) = init.root_uri.as_ref() {
158 if let Some(local) = LocalConfig::local(&fs, root).await {
159 server_config.config.local.combine(local);
160 }
161 }
162
163 info!("Initialize {:?}", server_config);
164 let document_selectors: Vec<_> = [
165 ("sparql", server_config.config.sparql.unwrap_or(true)),
166 ("turtle", server_config.config.turtle.unwrap_or(true)),
167 ("trig", server_config.config.trig.unwrap_or(true)),
168 ("n3", server_config.config.n3.unwrap_or(false)),
169 ("jsonld", server_config.config.jsonld.unwrap_or(true)),
170 ]
171 .into_iter()
172 .filter(|(_, x)| *x)
173 .map(|(x, _)| DocumentFilter {
174 language: Some(String::from(x)),
175 scheme: None,
176 pattern: None,
177 })
178 .collect();
179
180 let disabled = server_config.config.local.disabled.clone();
181 let is_enabled = |d: Disabled| !disabled.contains(&d);
182
183 self.run(|world| {
184 world.insert_resource(server_config);
185 world.run_schedule(Startup);
186 })
187 .await;
188
189 Ok(InitializeResult {
191 server_info: None,
192 capabilities: ServerCapabilities {
193 inlay_hint_provider: is_enabled(Disabled::InlayHint)
194 .then_some(OneOf::Left(true)),
195 text_document_sync: Some(TextDocumentSyncCapability::Kind(
196 TextDocumentSyncKind::FULL,
197 )),
198 code_action_provider: is_enabled(Disabled::CodeAction)
199 .then_some(CodeActionProviderCapability::Simple(true)),
200 completion_provider: is_enabled(Disabled::Completion).then_some(CompletionOptions {
201 resolve_provider: Some(false),
202 trigger_characters: Some(vec![String::from(":")]),
203 work_done_progress_options: Default::default(),
204 all_commit_characters: None,
205 completion_item: None,
206 }),
207 type_definition_provider: is_enabled(Disabled::GotoTypeDefinition)
209 .then_some(TypeDefinitionProviderCapability::Simple(true)),
210 references_provider: is_enabled(Disabled::References).then_some(OneOf::Left(true)),
211 hover_provider: is_enabled(Disabled::Hover)
212 .then_some(HoverProviderCapability::Simple(true)),
213 definition_provider: is_enabled(Disabled::GotoDefinition)
214 .then_some(OneOf::Left(true)),
215 document_formatting_provider: is_enabled(Disabled::Format)
216 .then_some(OneOf::Left(true)),
217 document_on_type_formatting_provider: is_enabled(Disabled::PrefixAutoInsert)
218 .then_some(DocumentOnTypeFormattingOptions {
219 first_trigger_character: ":".to_string(),
220 more_trigger_character: None,
221 }),
222 semantic_tokens_provider: is_enabled(Disabled::SemanticTokens).then_some(
223 SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(
224 SemanticTokensRegistrationOptions {
225 text_document_registration_options: {
226 TextDocumentRegistrationOptions {
227 document_selector: Some(document_selectors),
228 }
229 },
230 semantic_tokens_options: SemanticTokensOptions {
231 work_done_progress_options: WorkDoneProgressOptions::default(),
232 legend: SemanticTokensLegend {
233 token_types: self.semantic_tokens.clone(),
234 token_modifiers: vec![],
235 },
236 range: Some(false),
237 full: Some(SemanticTokensFullOptions::Bool(true)),
238 },
239 static_registration_options: StaticRegistrationOptions::default(),
240 },
241 ),
242 ),
243 rename_provider: is_enabled(Disabled::Rename).then_some(OneOf::Right(
244 RenameOptions {
245 prepare_provider: Some(true),
246 work_done_progress_options: Default::default(),
247 },
248 )),
249 execute_command_provider: Some(ExecuteCommandOptions {
250 commands: vec![crate::systems::ALLOW_PROPERTY_COMMAND.to_string()],
251 work_done_progress_options: Default::default(),
252 }),
253 ..ServerCapabilities::default()
254 },
255 })
256 }
257
258 pub async fn initialized(&self, _params: InitializedParams) {
259 self.run(|world| {
260 tracing::info!("initialized");
261 world.run_schedule(Started);
262 })
263 .await;
264 }
265
266 pub async fn did_change_workspace_folders(&self, params: DidChangeWorkspaceFoldersParams) -> () {
267 self.run(move |world| {
268 let mut config = world.resource_mut::<ServerConfig>();
269 let WorkspaceFoldersChangeEvent { added, removed } = params.event;
270
271 for r in removed {
272 if let Some(idx) = config.workspaces.iter().position(|x| x == &r) {
273 config.workspaces.remove(idx);
274 }
275 }
276
277 config.workspaces.extend(added);
279 })
280 .await;
281 ()
282 }
283
284 #[instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
285 pub async fn semantic_tokens_full(
286 &self,
287 params: SemanticTokensParams,
288 ) -> Result<Option<SemanticTokensResult>> {
289 debug!("semantic tokens full");
290 if self.is_disabled(Disabled::SemanticTokens).await {
291 return Ok(None);
292 }
293 let uri = params.text_document.uri.as_str();
294 let Some(entity) = self.get_entity(uri).await else {
295 debug!("Didn't find entity {} stopping", uri);
296 return Ok(None);
297 };
298
299 if let Some(res) = self
300 .run_schedule::<HighlightRequest>(entity, SemanticLabel, HighlightRequest(vec![]))
301 .await
302 {
303 Ok(Some(SemanticTokensResult::Tokens(
304 crate::lsp_types::SemanticTokens {
305 result_id: None,
306 data: res.0,
307 },
308 )))
309 } else {
310 debug!("resulting in no tokens");
311 Ok(None)
312 }
313 }
314
315 #[instrument(skip(self))]
316 pub async fn shutdown(&self) -> Result<()> {
317 info!("Shutting down!");
318
319 Ok(())
320 }
321
322 #[instrument(skip(self, params), fields(uri = %params.text_document_position.text_document.uri.as_str()))]
323 pub async fn references(&self, params: ReferenceParams) -> Result<Option<Vec<Location>>> {
324 if self.is_disabled(Disabled::References).await {
325 return Ok(None);
326 }
327 let Some(entity) = self
328 .get_entity(params.text_document_position.text_document.uri.as_str())
329 .await
330 else {
331 return Ok(None);
332 };
333
334 let mut pos = params.text_document_position.position;
335 Self::adjust_position(&mut pos);
336
337 let arr = self
338 .run_schedule::<ReferencesRequest>(
339 entity,
340 ReferencesLabel,
341 (PositionComponent(pos), ReferencesRequest(Vec::new())),
342 )
343 .await
344 .map(|x| x.0);
345
346 Ok(arr)
347 }
348
349 #[instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
350 pub async fn prepare_rename(
351 &self,
352 params: TextDocumentPositionParams,
353 ) -> Result<Option<PrepareRenameResponse>> {
354 if self.is_disabled(Disabled::Rename).await {
355 return Ok(None);
356 }
357 let Some(entity) = self.get_entity(params.text_document.uri.as_str()).await else {
358 return Ok(None);
359 };
360
361 let mut pos = params.position;
362 Self::adjust_position(&mut pos);
363
364 let resp = self
365 .run_schedule::<PrepareRenameRequest>(
366 entity,
367 PrepareRenameLabel,
368 PositionComponent(pos),
369 )
370 .await
371 .map(|x| PrepareRenameResponse::RangeWithPlaceholder {
372 range: x.range,
373 placeholder: x.placeholder,
374 });
375
376 Ok(resp)
377 }
378
379 #[instrument(skip(self, params), fields(uri = %params.text_document_position.text_document.uri.as_str()))]
380 pub async fn rename(&self, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
381 if self.is_disabled(Disabled::Rename).await {
382 return Ok(Some(WorkspaceEdit::new(HashMap::new())));
383 }
384 let Some(entity) = self
385 .get_entity(params.text_document_position.text_document.uri.as_str())
386 .await
387 else {
388 return Ok(None);
389 };
390
391 let mut pos = params.text_document_position.position;
392 Self::adjust_position(&mut pos);
393
394 let mut change_map: HashMap<crate::lsp_types::Url, Vec<TextEdit>> = HashMap::new();
395 if let Some(changes) = self
396 .run_schedule::<RenameEdits>(
397 entity,
398 RenameLabel,
399 (
400 PositionComponent(pos),
401 RenameEdits(Vec::new(), params.new_name),
402 ),
403 )
404 .await
405 {
406 for (url, change) in changes.0 {
407 let entry = change_map.entry(url);
408 entry.or_default().push(change);
409 }
410 }
411 Ok(Some(WorkspaceEdit::new(change_map)))
412 }
413
414 pub async fn hover(&self, params: HoverParams) -> Result<Option<crate::lsp_types::Hover>> {
415 if self.is_disabled(Disabled::Hover).await {
416 return Ok(None);
417 }
418 let request: HoverRequest = HoverRequest::default();
419
420 let Some(entity) = self
421 .get_entity(
422 params
423 .text_document_position_params
424 .text_document
425 .uri
426 .as_str(),
427 )
428 .await
429 else {
430 return Ok(None);
431 };
432
433 let mut pos = params.text_document_position_params.position;
434 Self::adjust_position(&mut pos);
435
436 if let Some(hover) = self
437 .run_schedule::<HoverRequest>(entity, HoverLabel, (request, PositionComponent(pos)))
438 .await
439 {
440 tracing::debug!("hover returned {} items", hover.0.len());
441 if hover.0.len() > 0 {
442 return Ok(Some(crate::lsp_types::Hover {
443 contents: crate::lsp_types::HoverContents::Markup(MarkupContent {
444 kind: MarkupKind::Markdown,
445 value: hover.0.join("\n\n---\n\n"),
446 }),
447 range: hover.1,
448 }));
449 }
450 }
451
452 Ok(None)
453 }
454
455 pub async fn inlay_hint(&self, params: InlayHintParams) -> Result<Option<Vec<InlayHint>>> {
456 debug!("Inlay hints called");
457 if self.is_disabled(Disabled::InlayHint).await {
458 return Ok(None);
459 }
460 let uri = params.text_document.uri.as_str();
461 let Some(entity) = self.get_entity(uri).await else {
462 debug!("Didn't find entity {}", uri);
463 return Ok(None);
464 };
465
466 let request = self
467 .run_schedule::<InlayRequest>(entity, InlayLabel, InlayRequest::default())
468 .await;
469
470 debug!(
471 "Inlay hints resolved {} hints",
472 request.as_ref().map(|x| x.0.len()).unwrap_or(0)
473 );
474
475 Ok(request.and_then(|x| Some(x.0)))
476 }
477
478 #[instrument(skip(self))]
479 pub async fn formatting(&self, params: DocumentFormattingParams) -> Result<Option<Vec<TextEdit>>> {
480 if self.is_disabled(Disabled::Format).await {
481 return Ok(None);
482 }
483 let uri = params.text_document.uri.as_str();
484 let Some(entity) = self.get_entity(uri).await else {
485 debug!("Didn't find entity {}", uri);
486 return Ok(None);
487 };
488
489 let request = self
490 .run_schedule::<FormatRequest>(entity, FormatLabel, FormatRequest(None))
491 .await;
492 Ok(request.and_then(|x| x.0))
493 }
494
495 #[instrument(skip(self, params), fields(uri = %params.text_document_position.text_document.uri.as_str()))]
496 pub async fn on_type_formatting(
497 &self,
498 params: DocumentOnTypeFormattingParams,
499 ) -> Result<Option<Vec<TextEdit>>> {
500 if self.is_disabled(Disabled::PrefixAutoInsert).await {
501 return Ok(None);
502 }
503 let uri = params.text_document_position.text_document.uri.as_str();
504 let Some(entity) = self.get_entity(uri).await else {
505 return Ok(None);
506 };
507
508 let pos = params.text_document_position.position;
511 let request = self
512 .run_schedule::<on_type_format::OnTypeFormatRequest>(
513 entity,
514 OnTypeFormatLabel,
515 (
516 PositionComponent(pos),
517 on_type_format::OnTypeFormatRequest(None),
518 ),
519 )
520 .await;
521 Ok(request.and_then(|x| x.0))
522 }
523
524 #[instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
525 pub async fn did_open(&self, params: DidOpenTextDocumentParams) {
526 let item = params.text_document;
527 let url = item.uri.as_str().to_string();
528
529 let lang_id = Some(item.language_id.clone());
530 let spawn = spawn_or_insert(
531 item.uri.clone(),
532 (
533 Source(item.text.clone()),
534 Label(item.uri.clone()),
535 RopeC(LineIndex::new(&item.text)),
536 Wrapped(item),
537 DocumentLinks(Vec::new()),
538 Open,
539 Types(HashMap::new()),
540 ),
541 lang_id,
542 (),
543 );
544
545 let entity = self
546 .run(|world| {
547 let id = spawn(world);
548 world.run_schedule(ParseLabel);
549 world.flush();
550 id
551 })
552 .await;
553
554 if let Some(entity) = entity {
555 self.entities.lock().await.insert(url, entity);
556 }
557
558 debug!("Requesting tokens refresh");
559 let _ = self.client.send_request::<SemanticTokensRefresh>(()).await;
560 debug!("Semantic tokens refresh");
561 }
562
563 #[instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
564 pub async fn did_change(&self, params: DidChangeTextDocumentParams) {
565 let Some(entity) = self.get_entity(params.text_document.uri.as_str()).await else {
566 debug!("Didn't find entity {}", params.text_document.uri.as_str());
567 return;
568 };
569
570 let change = {
571 if let Some(c) = params.content_changes.into_iter().next() {
572 c
573 } else {
574 return;
575 }
576 };
577
578 self.run(move |world| {
579 let rope_c = RopeC(LineIndex::new(&change.text));
580 world
581 .entity_mut(entity)
582 .insert((Source(change.text), rope_c));
583 world.run_schedule(ParseLabel);
584 world.flush();
585 })
586 .await;
587 }
588
589 #[instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
590 pub async fn did_save(&self, params: DidSaveTextDocumentParams) {
591 let _ = params;
592
593 self.run(move |world| {
594 world.run_schedule(SaveLabel);
595
596 debug!("Ran OnSave Schedule");
597 })
598 .await;
599 }
600
601 #[instrument(skip(self, params), fields(uri = %params.text_document_position_params.text_document.uri.as_str()))]
602 pub async fn goto_definition(
603 &self,
604 params: GotoDefinitionParams,
605 ) -> Result<Option<GotoDefinitionResponse>> {
606 if self.is_disabled(Disabled::GotoDefinition).await {
607 return Ok(None);
608 }
609 let Some(entity) = self
610 .get_entity(
611 params
612 .text_document_position_params
613 .text_document
614 .uri
615 .as_str(),
616 )
617 .await
618 else {
619 return Ok(None);
620 };
621
622 let mut pos = params.text_document_position_params.position;
623 Self::adjust_position(&mut pos);
624
625 let arr = self
626 .run_schedule::<GotoDefinitionRequest>(
627 entity,
628 GotoDefinitionLabel,
629 (PositionComponent(pos), GotoDefinitionRequest(Vec::new())),
630 )
631 .await
632 .map(|x| {
633 tracing::debug!("goto definition: {} locations", x.0.len());
634 GotoDefinitionResponse::Array(x.0)
635 });
636
637 Ok(arr)
638 }
639
640 #[instrument(skip(self, params), fields(uri = %params.text_document_position_params.text_document.uri.as_str()))]
641 pub async fn goto_type_definition(
642 &self,
643 params: GotoTypeDefinitionParams,
644 ) -> Result<Option<GotoTypeDefinitionResponse>> {
645 if self.is_disabled(Disabled::GotoTypeDefinition).await {
646 return Ok(None);
647 }
648 let Some(entity) = self
649 .get_entity(
650 params
651 .text_document_position_params
652 .text_document
653 .uri
654 .as_str(),
655 )
656 .await
657 else {
658 return Ok(None);
659 };
660
661 let mut pos = params.text_document_position_params.position;
662 Self::adjust_position(&mut pos);
663
664 let arr = self
665 .run_schedule::<GotoTypeRequest>(
666 entity,
667 GotoTypeLabel,
668 (PositionComponent(pos), GotoTypeRequest(Vec::new())),
669 )
670 .await
671 .map(|x| GotoTypeDefinitionResponse::Array(x.0));
672
673 Ok(arr)
674 }
675
676 #[instrument(skip(self, params), fields(uri = %params.text_document.uri.as_str()))]
677 pub async fn code_action(&self, params: CodeActionParams) -> Result<Option<CodeActionResponse>> {
678 if self.is_disabled(Disabled::CodeAction).await {
679 return Ok(None);
680 }
681 let uri = params.text_document.uri.as_str();
682 let Some(entity) = self.get_entity(uri).await else {
683 return Ok(None);
684 };
685
686 let pos = params.range.start;
687 let request = self
688 .run_schedule::<CodeActionRequest>(
689 entity,
690 CodeActionLabel,
691 (CodeActionRequest::default(), PositionComponent(pos)),
692 )
693 .await;
694
695 Ok(request.map(|r| {
696 r.0.into_iter()
697 .map(CodeActionOrCommand::CodeAction)
698 .collect()
699 }))
700 }
701
702 #[instrument(skip(self, params), fields(command = %params.command))]
708 pub async fn execute_command(
709 &self,
710 params: ExecuteCommandParams,
711 ) -> Result<Option<serde_json::Value>> {
712 if params.command == crate::systems::ALLOW_PROPERTY_COMMAND {
713 let Some(iri) = params
714 .arguments
715 .first()
716 .and_then(|v| v.as_str())
717 .map(String::from)
718 else {
719 return Ok(None);
720 };
721
722 self.run({
724 let iri = iri.clone();
725 move |world| {
726 world
727 .resource_mut::<ServerConfig>()
728 .config
729 .local
730 .allowed_properties
731 .insert(iri);
732 }
733 })
734 .await;
735
736 if let Some(fs) = self.run(|w| w.resource::<Fs>().clone()).await {
738 LocalConfig::persist_allowed_property(&fs, &iri).await;
739 }
740
741 self.run(|world| {
743 world.run_schedule(ParseLabel);
744 world.flush();
745 })
746 .await;
747 }
748
749 Ok(None)
750 }
751
752 #[instrument(skip(self, params), fields(uri = %params.text_document_position.text_document.uri.as_str()))]
753 pub async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
754 if self.is_disabled(Disabled::Completion).await {
755 return Ok(None);
756 }
757 let Some(entity) = self
758 .get_entity(params.text_document_position.text_document.uri.as_str())
759 .await
760 else {
761 return Ok(None);
762 };
763
764 let mut pos = params.text_document_position.position;
767 Self::adjust_position(&mut pos);
768
769 let completions: Option<Vec<crate::lsp_types::CompletionItem>> = self
770 .run_schedule::<CompletionRequest>(
771 entity,
772 CompletionLabel,
773 (CompletionRequest(vec![]), PositionComponent(pos)),
774 )
775 .await
776 .map(|x| x.0.into_iter().map(|x| x.into()).collect());
777
778 Ok(completions.map(|mut c| {
779 c.sort_by(|a, b| a.sort_text.cmp(&b.sort_text));
780
781 CompletionResponse::Array(c)
782 }))
783 }
784}