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