1use std::{collections::HashSet, path::PathBuf};
2
3use bevy_ecs::prelude::*;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 lsp_types::{Url, WorkspaceFolder},
8 util::fs::Fs,
9};
10
11#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub enum Disabled {
13 #[serde(alias = "SHAPES", alias = "shapes")]
14 Shapes,
15
16 #[serde(alias = "UNDEFINED_PREFIX", alias = "undefined_prefix")]
19 UndefinedPrefix,
20 #[serde(alias = "UNUSED_PREFIX", alias = "unused_prefix")]
22 UnusedPrefix,
23 #[serde(alias = "NAMESPACE_PROPERTIES", alias = "namespace_properties")]
26 NamespaceProperties,
27 #[serde(alias = "SYNTAX_DIAGNOSTICS", alias = "syntax_diagnostics")]
29 SyntaxDiagnostics,
30
31 #[serde(alias = "COMPLETION", alias = "completion")]
36 Completion,
37 #[serde(alias = "COMPLETION_KEYWORD", alias = "completion_keyword")]
39 CompletionKeyword,
40 #[serde(alias = "COMPLETION_CLASS", alias = "completion_class")]
42 CompletionClass,
43 #[serde(alias = "COMPLETION_PROPERTY", alias = "completion_property")]
45 CompletionProperty,
46 #[serde(alias = "COMPLETION_PREFIX", alias = "completion_prefix")]
49 CompletionPrefix,
50 #[serde(alias = "COMPLETION_SUBJECT", alias = "completion_subject")]
53 CompletionSubject,
54
55 #[serde(alias = "HOVER", alias = "hover")]
59 Hover,
60 #[serde(alias = "HOVER_TYPE", alias = "hover_type")]
62 HoverType,
63 #[serde(alias = "HOVER_CLASS", alias = "hover_class")]
65 HoverClass,
66 #[serde(alias = "HOVER_PROPERTY", alias = "hover_property")]
68 HoverProperty,
69 #[serde(
72 alias = "HOVER_EXCLUDED_PROPERTY",
73 alias = "hover_excluded_property"
74 )]
75 HoverExcludedProperty,
76
77 #[serde(alias = "GOTO_DEFINITION", alias = "goto_definition")]
80 GotoDefinition,
81 #[serde(
84 alias = "GOTO_DEFINITION_COMPONENTS_JS",
85 alias = "goto_definition_components_js"
86 )]
87 GotoDefinitionComponentsJs,
88 #[serde(alias = "GOTO_TYPE_DEFINITION", alias = "goto_type_definition")]
89 GotoTypeDefinition,
90 #[serde(alias = "REFERENCES", alias = "references")]
91 References,
92 #[serde(alias = "RENAME", alias = "rename")]
93 Rename,
94 #[serde(alias = "SEMANTIC_TOKENS", alias = "semantic_tokens")]
95 SemanticTokens,
96 #[serde(alias = "FORMAT", alias = "format")]
97 Format,
98 #[serde(
101 alias = "PREFIX_AUTO_INSERT",
102 alias = "prefix_auto_insert",
103 alias = "ON_TYPE_FORMAT",
104 alias = "on_type_format"
105 )]
106 PrefixAutoInsert,
107 #[serde(alias = "CODE_ACTION", alias = "code_action")]
113 CodeAction,
114 #[serde(
116 alias = "CODE_ACTION_ORGANIZE_IMPORTS",
117 alias = "code_action_organize_imports"
118 )]
119 CodeActionOrganizeImports,
120 #[serde(
122 alias = "CODE_ACTION_BLANK_NODE_REFACTOR",
123 alias = "code_action_blank_node_refactor"
124 )]
125 CodeActionBlankNodeRefactor,
126 #[serde(alias = "INLAY_HINT", alias = "inlay_hint")]
127 InlayHint,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize, Serialize)]
136#[serde(rename_all = "lowercase")]
137pub enum PrefixFormat {
138 #[default]
140 Turtle,
141 Sparql,
143}
144
145#[derive(Resource, Debug, Default)]
146pub struct ServerConfig {
147 pub workspaces: Vec<WorkspaceFolder>,
148 pub config: Config,
149}
150
151#[derive(Debug, Deserialize)]
152pub struct Config {
153 #[serde(default = "debug")]
155 pub log: String,
156 pub turtle: Option<bool>,
158 pub trig: Option<bool>,
160 pub n3: Option<bool>,
162 pub jsonld: Option<bool>,
164 pub sparql: Option<bool>,
166 #[serde(default)]
169 pub format: FormatConfig,
170 #[serde(flatten)]
172 pub local: LocalConfig,
173}
174
175#[derive(Debug, Deserialize, Serialize, Default)]
183#[serde(default)]
184pub struct FormatConfig {
185 pub turtle: Option<bool>,
187 pub trig: Option<bool>,
189 pub n3: Option<bool>,
191 pub jsonld: Option<bool>,
193}
194
195impl FormatConfig {
196 pub fn turtle(&self) -> bool {
198 self.turtle.unwrap_or(true)
199 }
200 pub fn trig(&self) -> bool {
202 self.trig.unwrap_or(false)
203 }
204 pub fn n3(&self) -> bool {
206 self.n3.unwrap_or(false)
207 }
208 pub fn jsonld(&self) -> bool {
210 self.jsonld.unwrap_or(false)
211 }
212}
213
214#[derive(Debug, Deserialize, Serialize, Default)]
215#[serde(default)]
216pub struct LocalConfig {
217 pub ontologies: HashSet<String>,
219 pub shapes: HashSet<String>,
221 pub disabled: HashSet<Disabled>,
223 pub prefix_disabled: HashSet<String>,
225 pub completion: CompletionConfig,
227 pub prefix_format: Option<PrefixFormat>,
229 pub closed_namespaces: HashSet<String>,
233 pub allowed_properties: HashSet<String>,
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
252#[serde(untagged)]
253pub enum CompletionConfig {
254 Mode(CompletionMode),
256
257 Except(ExceptRules),
259
260 Strict(StrictRules),
262}
263
264impl Default for CompletionConfig {
265 fn default() -> Self {
266 Self::Mode(CompletionMode::None)
267 }
268}
269
270impl CompletionConfig {
271 fn combine(&mut self, other: CompletionConfig) {
272 use CompletionConfig::*;
273
274 if matches!(other, Mode(CompletionMode::None)) {
275 return;
276 }
277
278 if matches!(self, Mode(CompletionMode::None)) {
279 *self = other;
280 return;
281 }
282
283 if let Strict(r) = self {
284 if let Strict(r2) = other {
285 r.strict.extend(r2.strict);
286 return;
287 }
288 }
289
290 if let Except(r) = self {
291 if let Except(r2) = other {
292 r.loose.extend(r2.loose);
293 return;
294 }
295 }
296
297 *self = other;
298 }
299 pub fn correct_domain_required(&self, property: &str) -> bool {
300 match self {
301 CompletionConfig::Mode(CompletionMode::Loose)
302 | CompletionConfig::Mode(CompletionMode::None) => false,
303 CompletionConfig::Mode(CompletionMode::Strict) => true,
304 CompletionConfig::Except(completion_rules) => !completion_rules
305 .loose
306 .iter()
307 .any(|x| property.starts_with(x)),
308 CompletionConfig::Strict(completion_rules) => completion_rules
309 .strict
310 .iter()
311 .any(|x| property.starts_with(x)),
312 }
313 }
314}
315
316#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize, Serialize)]
317#[serde(rename_all = "lowercase")]
318pub enum CompletionMode {
319 #[default]
320 None,
321 Loose,
322 Strict,
323}
324
325#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
326#[serde(deny_unknown_fields)]
327pub struct ExceptRules {
328 #[serde(default)]
329 pub loose: Vec<String>,
330}
331#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Default)]
332#[serde(deny_unknown_fields)]
333pub struct StrictRules {
334 #[serde(default)]
335 pub strict: Vec<String>,
336}
337
338impl LocalConfig {
339 pub fn is_disabled(&self, d: Disabled) -> bool {
341 self.disabled.contains(&d)
342 }
343
344 pub fn combine(&mut self, other: LocalConfig) {
346 self.ontologies.extend(other.ontologies);
347 self.shapes.extend(other.shapes);
348 self.disabled.extend(other.disabled);
349 self.prefix_disabled.extend(other.prefix_disabled);
350 self.completion.combine(other.completion);
351 if other.prefix_format.is_some() {
352 self.prefix_format = other.prefix_format;
353 }
354 self.closed_namespaces.extend(other.closed_namespaces);
355 self.allowed_properties.extend(other.allowed_properties);
356 }
357 #[cfg(target_arch = "wasm32")]
358 pub async fn global(_: &Fs) -> Option<Self> {
359 None
360 }
361
362 #[cfg(not(target_arch = "wasm32"))]
363 pub fn global_url() -> Option<Url> {
364 let global_path = dirs::config_dir()
365 .unwrap_or_else(|| PathBuf::from("."))
366 .join("swls/config.json");
367 crate::lsp_types::Url::from_file_path(global_path).ok()
368 }
369
370 #[cfg(target_arch = "wasm32")]
371 pub fn global_url() -> Option<Url> {
372 None
373 }
374
375 #[cfg(not(target_arch = "wasm32"))]
376 pub async fn global(fs: &Fs) -> Option<Self> {
377 let url = Self::global_url()?;
378
379 tracing::debug!("Found global config url {}", url.as_str());
380 let content = fs.0.read_file(&url).await?;
381 tracing::debug!("Read global config content");
382
383 match serde_json::from_str(&content) {
384 Ok(x) => Some(x),
385 Err(e) => {
386 tracing::error!("Deserialize failed\n{:?}", e);
387 None
388 }
389 }
390 }
391
392 #[cfg(not(target_arch = "wasm32"))]
396 pub async fn persist_allowed_property(fs: &Fs, iri: &str) -> Option<()> {
397 let url = Self::global_url()?;
398 let mut existing = Self::global(fs).await.unwrap_or_default();
399 existing.allowed_properties.insert(iri.to_string());
400 let content = serde_json::to_string_pretty(&existing).ok()?;
401 fs.0.write_file(&url, &content).await
402 }
403
404 #[cfg(target_arch = "wasm32")]
405 pub async fn persist_allowed_property(_: &Fs, _: &str) -> Option<()> {
406 None
407 }
408
409 pub async fn local(fs: &Fs, url: &Url) -> Option<Self> {
410 let url = Url::parse(&format!("{}/.swls/config.json", url.as_str())).ok()?;
411 tracing::debug!("Found local config url {}", url.as_str());
412 let content = fs.0.read_file(&url).await?;
413 tracing::debug!("Read local config content");
414 match serde_json::from_str(&content) {
415 Ok(x) => Some(x),
416 Err(e) => {
417 tracing::error!("Deserialize failed\n{:?}", e);
418 None
419 }
420 }
421 }
422}
423
424impl Default for Config {
425 fn default() -> Self {
426 Self {
427 log: "debug".to_string(),
428 turtle: None,
429 trig: None,
430 n3: None,
431 jsonld: None,
432 sparql: None,
433 format: FormatConfig::default(),
434 local: LocalConfig::default(),
435 }
436 }
437}
438
439fn debug() -> String {
440 String::from("debug")
441}