Skip to main content

swls_core/
lib.rs

1#![doc(
2    html_logo_url = "https://ajuvercr.github.io/semantic-web-lsp/assets/icons/favicon.png",
3    html_favicon_url = "https://ajuvercr.github.io/semantic-web-lsp/assets/icons/favicon.ico"
4)]
5//! Core and common implementation for the semantic web language server.
6//!
7//! Proivdes the backbone for the [semantic web lsp binary](../lsp_bin/index.html) and [semantic web
8//! lsp wasm](../lsp_web/index.html).
9//!
10//! With the language server protocol, each different request is handled by an ECS schedule,
11//! combining different systems together.
12//! A system can generate new data and attach it to an entity, a document, or use this data to
13//! respond to requests.
14//!
15//! Language specific implementations that handle things like tokenizing and parsing are
16//! implemented in separate crates. The binary currently supports [Turtle](../swls_lang_turtle/index.html), [JSON-LD](../swls_lang_jsonld/index.html) and [SPARQL](../swls_lang_sparql/index.html).
17//! The goal is that each language at least generates [`Tokens`], [`Triples`] and
18//! [`Prefixes`].
19//! These components are then used to derive properties for autcompletion but also derive
20//! [`TokenComponent`] and [`TripleComponent`] enabling completion.
21//!
22//! The different schedules can be found at [`prelude::feature`].
23//!
24//! ## Example add completion for all subjects that start with `a`
25//! ```
26//! use bevy_ecs::prelude::*;
27//! use swls_core::prelude::*;
28//! # use sophia_api::dataset::Dataset;
29//! # use sophia_api::prelude::Quad;
30//!
31//! // Define the extra data struct
32//! #[derive(Component)]
33//! struct MyCompletions {
34//!     subjects: Vec<String>,
35//! }
36//!
37//! // Define the generating system
38//! // Don't forget to add it to the ecs later
39//! fn generate_my_completion(
40//!   // Only derive the completions when the document is parsed fully, aka is not Dirty
41//!   query: Query<(Entity, &Triples), (Changed<Triples>, Without<Dirty>)>,
42//!   mut commands: Commands,
43//! ) {
44//!   for (e, triples) in &query {
45//!     let mut subjects = Vec::new();
46//!     for q in triples.quads().flatten() {
47//!       if q.s().as_str().starts_with('a') {
48//!         subjects.push(q.s().as_str().to_string());
49//!       }
50//!     }
51//!     commands.entity(e).insert(MyCompletions { subjects });
52//!   }
53//! }
54//!
55//! // Define a system that adds these completions to the completion request
56//! fn complete_my_completion(
57//!   mut query: Query<(
58//!     &TokenComponent, &TripleComponent, &MyCompletions, &mut CompletionRequest
59//!   )>,
60//! ) {
61//!   for (token, this_triple, completions, mut request) in &mut query {
62//!     if this_triple.target == TripleTarget::Subject {
63//!       for my_completion in &completions.subjects {
64//!         request.push(
65//!           SimpleCompletion::new(
66//!             swls_core::lsp_types::CompletionItemKind::FIELD,
67//!             my_completion.clone(),
68//!             swls_core::lsp_types::TextEdit {
69//!               range: token.range.clone(),
70//!               new_text: my_completion.clone(),
71//!             }
72//!           )
73//!         )
74//!       }
75//!     }
76//!   }
77//! }
78//! ```
79//! Note that [`Prefixes`] can help expand and shorten iri's in a document.
80//!
81//!
82
83use bevy_ecs::{prelude::*, schedule::ScheduleLabel};
84use prelude::SemanticTokensDict;
85use systems::{init_ontology_extractor, populate_known_ontologies, OntologyExtractor};
86pub use lsp_types;
87
88use crate::prelude::*;
89
90/// Main language server implementation.
91///
92/// [`Backend`](struct@backend::Backend) exposes transport-agnostic request handlers.
93/// The `swls` binary adapts these to `tower_lsp::LanguageServer`.
94/// Each incoming request a schedule is ran on the main [`World`].
95pub mod backend;
96
97/// Handle platform specific implementations for fetching and spawning tasks.
98pub mod client;
99pub mod store;
100/// Minimal text indexing for LSP position math (replaces ropey).
101pub mod text;
102/// Common utils
103///
104/// Includes range transformations between [`std::ops::Range`] and [`swls_core::lsp_types::Range`].
105/// And commonly used [`Spanned`].
106pub mod util;
107
108/// Defines all common [`Component`]s and [`Resource`]s
109///
110/// In this [`World`], [Entity]s are documents and [`Components`](`Component`) are derived from these documents.
111/// Different [`System`]s derive new [`Components`](`Component`) from existing [`Components`](`Component`), that are added to
112/// the [`Entity`].
113/// For example, if [`Triples`] are defined, [systems::derive_classes] will
114/// derive [`DefinedClass`](struct@systems::DefinedClass) from them and add them to the [`Entity`].
115pub mod components;
116/// Hosts all common features of the semantic language server.
117pub mod feature;
118/// Defines common language traits
119pub mod lang;
120pub mod prelude;
121pub mod systems;
122
123/// Initializes a [`World`], including [`Resources`](`Resource`) and [`Schedules`].
124/// All systems defined in [`crate`] are added to the [`World`].
125pub fn setup_schedule_labels<C: Client + Resource>(world: &mut World) {
126    world.init_resource::<SemanticTokensDict>();
127    world.init_resource::<TypeHierarchy>();
128    world.init_resource::<Ontologies>();
129    world.insert_resource(OntologyExtractor::new());
130
131    parse::setup_schedule::<C>(world);
132    hover::setup_schedule(world);
133    completion::setup_schedule(world);
134    rename::setup_schedules(world);
135    save::setup_schedule(world);
136    format::setup_schedule(world);
137    on_type_format::setup_schedule(world);
138    references::setup_schedule(world);
139    inlay::setup_schedule(world);
140    goto_definition::setup_schedule(world);
141    goto_type::setup_schedule(world);
142    code_action::setup_schedule(world);
143
144    semantic::setup_world(world);
145
146    world.add_schedule(Schedule::new(Tasks));
147
148    let mut schedule = Schedule::new(Startup);
149    schedule.add_systems((
150        init_ontology_extractor,
151        populate_known_ontologies,
152        // extract_known_prefixes_from_config::<C>,
153    ));
154
155    world.add_schedule(schedule);
156    world.add_schedule(Schedule::new(Started));
157}
158
159/// Event triggers when a document is opened
160///
161/// Example
162/// ```rust
163/// # use swls_core::components::DynLang;
164/// # use swls_core::CreateEvent;
165/// # use swls_core::lang::LangHelper;
166/// # use bevy_ecs::event::EntityEvent;
167/// # use bevy_ecs::prelude::{Commands, On, World, Component};
168///
169/// #[derive(Component)]
170/// pub struct TurtleLang;
171///
172/// #[derive(Debug)]
173/// pub struct TurtleHelper;
174/// impl LangHelper for TurtleHelper {
175///     fn keyword(&self) -> &[&'static str] {
176///         &[
177///             "@prefix",
178///             "@base",
179///             "a",
180///         ]
181///     }
182/// }
183///
184/// let mut world = World::new();
185/// // This example tells the ECS system that the document is Turtle,
186/// // adding Turtle specific components
187/// world.add_observer(|trigger: On<CreateEvent>, mut commands: Commands| {
188///     match &trigger.event().language_id {
189///         Some(x) if x == "turtle" => {
190///             commands
191///                 .entity(trigger.event_target())
192///                 .insert((TurtleLang, DynLang(Box::new(TurtleHelper))));
193///             return;
194///         }
195///         _ => {}
196///     }
197///     if trigger.event().url.as_str().ends_with(".ttl") {
198///         commands
199///             .entity(trigger.event_target())
200///             .insert((TurtleLang, DynLang(Box::new(TurtleHelper))));
201///         return;
202///     }
203/// });
204/// ```
205///
206#[derive(EntityEvent)]
207pub struct CreateEvent {
208    pub url: crate::lsp_types::Url,
209    pub language_id: Option<String>,
210    #[event_target]
211    pub entity: Entity,
212}
213
214/// [`ScheduleLabel`] related to the Tasks schedule
215/// This schedule is used for async tasks, things that should be done at some point.
216///
217/// For example [`systems::handle_tasks`] spawns command queues sent with
218/// [`CommandSender`]
219#[derive(ScheduleLabel, Clone, Eq, PartialEq, Debug, Hash)]
220pub struct Tasks;
221
222#[derive(ScheduleLabel, Clone, Eq, PartialEq, Debug, Hash)]
223pub struct Startup;
224
225#[derive(ScheduleLabel, Clone, Eq, PartialEq, Debug, Hash)]
226pub struct Started;