lsp_core/
components.rs

1use std::{
2    borrow::Cow,
3    collections::{HashMap, HashSet},
4    fmt::Debug,
5    path::PathBuf,
6};
7
8use bevy_ecs::{prelude::*, world::CommandQueue};
9use derive_more::{AsMut, AsRef, Deref, DerefMut};
10use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
11use serde::Deserialize;
12
13use crate::{
14    lang::{Lang, LangHelper},
15    lsp_types::{Position, Url, WorkspaceFolder},
16    prelude::*,
17    systems::TypeId,
18};
19
20#[derive(Component, Default, Debug, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
21pub struct CurrentType(pub Vec<TypeId>);
22
23/// [`Component`] that contains the parsed semantic element (i.e. Turtle, JSONLD).
24#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
25pub struct Element<L: Lang>(pub Spanned<L::Element>);
26
27/// Simple wrapper structure that derives [`Component`]
28#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
29pub struct Wrapped<E>(pub E);
30
31/// Simple wrapper for errors that derives [`Component`]
32#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
33pub struct Errors<E>(pub Vec<E>);
34
35/// [`Component`] containing the current source code as [`String`]
36#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
37pub struct Source(pub String);
38
39/// [`Component`] containing the current source code as [`ropey::Rope`]
40#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
41pub struct RopeC(pub ropey::Rope);
42
43/// [`Component`] that allows for language specific implementation for certain things, reducing
44/// code duplication.
45#[derive(Component, Debug, AsRef, Deref)]
46pub struct DynLang(pub Box<dyn LangHelper + 'static + Send + Sync>);
47
48/// [`Component`] indicating whether or not the document is actually open.
49///
50/// Documents that are not [`Open`] don't publish diagnostics for example
51#[derive(Component, Debug)]
52pub struct Open;
53
54/// [`Component`] indicating whether or not the document is dirty, a dirty document parsed with
55/// errors.
56///
57/// A document is often Dirty, computational intens calculation can be done on documents that are
58/// not dirty, like [`derive_classes`](crate::prelude::systems::derive_classes) and [`derive_properties`](crate::prelude::systems::derive_properties).
59#[derive(Component, Debug)]
60pub struct Dirty;
61
62/// [`Component`] containing the [`lsp_types::Url`] of the current document.
63#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
64pub struct Label(pub crate::lsp_types::Url);
65
66/// [`Resource`] used to receive command queues. These command queues are handled with [`handle_tasks`](crate::prelude::systems::handle_tasks).
67#[derive(Resource, AsRef, Deref, AsMut, DerefMut, Debug)]
68pub struct CommandReceiver(pub UnboundedReceiver<CommandQueue>);
69
70/// [`Resource`] used to send command queues, allowing for async operations.
71#[derive(Resource, AsRef, Deref, AsMut, DerefMut, Debug, Clone)]
72pub struct CommandSender(pub UnboundedSender<CommandQueue>);
73
74/// [`Component`] used to remember the linked documents.
75///
76/// This is used, for example, to only suggest properties defined in a linked document.
77/// Or only validate with shapes found in linked documents.
78#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug, Clone)]
79pub struct DocumentLinks(pub Vec<(crate::lsp_types::Url, &'static str)>);
80
81/// [`Component`] used to wrap an incoming [`lsp_types::Position`].
82///
83/// This component is translated into [`TokenComponent`] and [`TripleComponent`]
84/// with [`get_current_token`]
85/// and [get_current_triple] respectively.
86#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
87pub struct PositionComponent(pub Position);
88
89/// [`Component`] containing the typical keywords for the current language.
90#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
91pub struct KeyWords(pub Vec<&'static str>);
92
93/// maps terms to all known correct types.
94#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug)]
95pub struct Types(pub HashMap<Cow<'static, str>, Vec<TypeId>>);
96
97/// [`Resource`] used to set and get all super and subtypes starting from a [`TypeId`]
98///
99/// Example
100/// ```
101/// use lsp_core::components::TypeHierarchy;
102///
103/// let mut hierarchy = TypeHierarchy::default();
104/// let image_id = hierarchy.get_id("http://xmlns.com/foaf/0.1/Image");
105/// let document_id = hierarchy.get_id("http://xmlns.com/foaf/0.1/Document");
106/// hierarchy.set_subclass_of(image_id, document_id);
107///
108/// for ty in hierarchy.iter_superclass(document_id) {
109///     // first "http://xmlns.com/foaf/0.1/Document"
110///     // then "http://xmlns.com/foaf/0.1/Image"
111///     println!("Type {}", ty);
112/// }
113/// ```
114#[derive(Resource, Debug, Default)]
115pub struct TypeHierarchy<'a> {
116    numbers: HashMap<Cow<'a, str>, TypeId>,
117    nodes: Vec<Cow<'a, str>>,
118    subclass: Vec<HashSet<TypeId>>,
119    superclass: Vec<HashSet<TypeId>>,
120}
121
122impl<'a> TypeHierarchy<'a> {
123    pub fn get_id(&mut self, class: &str) -> TypeId {
124        if let Some(id) = self.numbers.get(class) {
125            *id
126        } else {
127            let new_id = TypeId(self.nodes.len());
128            let class_cow: Cow<'a, str> = Cow::Owned(class.to_string());
129            self.nodes.push(class_cow.clone());
130            self.numbers.insert(class_cow, new_id);
131            self.subclass.push(HashSet::new());
132            self.superclass.push(HashSet::new());
133            new_id
134        }
135    }
136
137    pub fn get_id_ref(&self, class: &str) -> Option<TypeId> {
138        self.numbers.get(class).copied()
139    }
140
141    pub fn set_subclass_of(&mut self, class: TypeId, to: TypeId) {
142        self.subclass[class.0].insert(to);
143        self.superclass[to.0].insert(class);
144    }
145
146    pub fn iter_subclass<'b>(&'b self, id: TypeId) -> impl Iterator<Item = Cow<'a, str>> + 'b {
147        let mut stack = std::collections::VecDeque::new();
148        stack.push_back(id);
149        let mut done = HashSet::new();
150        std::iter::from_fn(move || {
151            while let Some(id) = stack.pop_front() {
152                if done.contains(&id) {
153                    continue;
154                }
155                done.insert(id);
156
157                self.subclass[id.0].iter().for_each(|i| stack.push_back(*i));
158                return Some(self.nodes[id.0].clone());
159            }
160
161            None
162        })
163    }
164
165    pub fn type_name(&self, id: TypeId) -> Cow<'a, str> {
166        self.nodes[id.0].clone()
167    }
168
169    pub fn iter_superclass<'b>(&'b self, id: TypeId) -> impl Iterator<Item = Cow<'a, str>> + 'b {
170        let mut stack = std::collections::VecDeque::new();
171        stack.push_back(id);
172        let mut done = HashSet::new();
173        std::iter::from_fn(move || {
174            while let Some(id) = stack.pop_front() {
175                if done.contains(&id) {
176                    continue;
177                }
178                done.insert(id);
179
180                self.superclass[id.0]
181                    .iter()
182                    .for_each(|i| stack.push_back(*i));
183                return Some(self.nodes[id.0].clone());
184            }
185
186            None
187        })
188    }
189}
190
191#[derive(Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
192pub enum Disabled {
193    #[serde(alias = "SHAPES", alias = "shapes")]
194    Shapes,
195}
196
197#[derive(Resource, Debug, Default)]
198pub struct ServerConfig {
199    pub workspaces: Vec<WorkspaceFolder>,
200    pub config: Config,
201}
202
203#[derive(Debug, Deserialize)]
204pub struct Config {
205    /// Log level
206    #[serde(default = "debug")]
207    pub log: String,
208    /// Enable turtle
209    pub turtle: Option<bool>,
210    /// Enable jsonld
211    pub jsonld: Option<bool>,
212    /// Enable sparql
213    pub sparql: Option<bool>,
214    /// Extra local configuration
215    #[serde(flatten)]
216    pub local: LocalConfig,
217}
218
219#[derive(Debug, Deserialize, Default)]
220#[serde(default)]
221pub struct LocalConfig {
222    /// Extra ontologies to import
223    pub ontologies: HashSet<String>,
224    /// Extra shapes to import
225    pub shapes: HashSet<String>,
226    /// Features to disable
227    pub disabled: HashSet<Disabled>,
228}
229
230impl LocalConfig {
231    /// Combines this config with another config, giving precedence to the other config
232    pub fn combine(&mut self, other: LocalConfig) {
233        self.ontologies.extend(other.ontologies);
234        self.shapes.extend(other.shapes);
235        self.disabled.extend(other.disabled);
236    }
237    #[cfg(target_arch = "wasm32")]
238    pub async fn global(_: &Fs) -> Option<Self> {
239        None
240    }
241    #[cfg(not(target_arch = "wasm32"))]
242    pub async fn global(fs: &Fs) -> Option<Self> {
243        let global_path = dirs::config_dir()
244            .unwrap_or_else(|| PathBuf::from("."))
245            .join("swls/config.json");
246        let url = crate::lsp_types::Url::from_file_path(global_path).ok()?;
247
248        tracing::debug!("Found global config url {}", url.as_str());
249        let content = fs.0.read_file(&url).await?;
250        tracing::debug!("Read global config content");
251
252        match serde_json::from_str(&content) {
253            Ok(x) => Some(x),
254            Err(e) => {
255                tracing::error!("Deserialize failed\n{:?}", e);
256                None
257            }
258        }
259    }
260
261    pub async fn local(fs: &Fs, url: &Url) -> Option<Self> {
262        let url = Url::parse(&format!("{}/.swls/config.json", url.as_str())).ok()?;
263        tracing::debug!("Found local config url {}", url.as_str());
264        let content = fs.0.read_file(&url).await?;
265        tracing::debug!("Read local config content");
266        match serde_json::from_str(&content) {
267            Ok(x) => Some(x),
268            Err(e) => {
269                tracing::error!("Deserialize failed\n{:?}", e);
270                None
271            }
272        }
273    }
274}
275
276impl Default for Config {
277    fn default() -> Self {
278        Self {
279            log: "debug".to_string(),
280            turtle: None,
281            jsonld: None,
282            sparql: None,
283            local: LocalConfig::default(),
284        }
285    }
286}
287
288fn debug() -> String {
289    String::from("debug")
290}