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 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/// Error returned by [`Backend`] request handlers.
30///
31/// No handler currently fails at the protocol level, so this type is uninhabited.
32/// It exists so handlers can return a transport-agnostic [`Result`] that front-ends
33/// map onto their own error type (e.g. `tower_lsp::jsonrpc::Error`) without `swls-core`
34/// depending on any transport crate.
35#[derive(Debug)]
36pub enum ServerError {}
37
38/// Transport-agnostic result type for [`Backend`] request handlers.
39pub type Result<T> = std::result::Result<T, ServerError>;
40
41/// Transport-agnostic LSP request handler.
42///
43/// `Backend` exposes inherent `async` methods mirroring the LSP requests. Front-ends
44/// (the `swls` binary's `tower_lsp` adapter, or a future wasm dispatcher) call these
45/// directly. It is generic over the [`Client`] used to send server-to-client messages.
46pub 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    /// Whether the given LSP feature has been disabled via [`ServerConfig`].
123    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        // let triggers = L::TRIGGERS.iter().copied().map(String::from).collect();
190        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                // implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
208                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            // This is nice and all, but we don't bubble this event up in the world
278            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        // Use the exact position after the typed character — do NOT apply
509        // `adjust_position` here, the scan expects the cursor right after `:`.
510        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    /// Handle `workspace/executeCommand`.
703    ///
704    /// Currently only `swls.allowProperty`: adds the given property IRI to the
705    /// user's `allowed_properties`, both at runtime (clearing the warning) and on
706    /// disk (so the choice survives restarts), then refreshes diagnostics.
707    #[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            // 1. Update the in-memory config so the warning clears immediately.
723            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            // 2. Persist to the global config file.
737            if let Some(fs) = self.run(|w| w.resource::<Fs>().clone()).await {
738                LocalConfig::persist_allowed_property(&fs, &iri).await;
739            }
740
741            // 3. Re-run diagnostics for all open documents.
742            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        // Problem: when the cursor is at the end of an ident, that ident is not in range of the
765        // cursor
766        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}