lsp_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::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        // let triggers = L::TRIGGERS.iter().copied().map(String::from).collect();
142        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                // implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
158                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            // This is nice and all, but we don't bubble this event up in the world
205            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        // Problem: whne the cursor is at the end of en ident, that ident is not in range of the
634        // cursor
635        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}