Skip to main content

swls_core/
backend.rs

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