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)]
5use bevy_ecs::{component::Component, resource::Resource, world::World};
6use swls_core::{
7 lang::{Lang, LangHelper},
8 lsp_types::SemanticTokenType,
9 prelude::*,
10};
11use swls_lang_rdf_base::register_rdf_lang;
12
13pub mod config;
14pub mod ecs;
15pub mod lang;
16pub mod prefix;
17
18use crate::{
19 config::{extract_known_prefixes_from_config, extract_known_shapes_from_config},
20 ecs::{setup_code_action, setup_completion, setup_formatting, setup_parsing},
21};
22
23#[derive(Component, Default)]
24pub struct TurtleLang;
25
26#[derive(Debug, Default)]
27pub struct TurtleHelper;
28impl LangHelper for TurtleHelper {
29 fn keyword(&self) -> &[&'static str] {
30 &["@prefix", "@base", "a"]
31 }
32 fn model_based_rename(&self) -> bool {
33 true
34 }
35 fn blank_node_code_actions(&self) -> bool {
36 true
37 }
38}
39
40pub fn setup_world<C: Client + ClientSync + Resource + Clone>(world: &mut World) {
41 register_rdf_lang::<TurtleLang, TurtleHelper>(
42 world,
43 &["turtle"],
44 &[".ttl", ".owl", ".nt"],
47 );
48
49 world.schedule_scope(swls_core::Startup, |_, schedule| {
50 schedule.add_systems((
51 extract_known_prefixes_from_config::<C>,
52 extract_known_shapes_from_config::<C>,
53 ));
54 });
55
56 setup_parsing(world);
57 setup_completion(world);
58 setup_formatting(world);
59 setup_code_action(world);
60}
61
62impl Lang for TurtleLang {
63 type ElementError = crate::lang::parser::TurtleParseError;
64
65 const LANG: &'static str = "turtle";
66 const TRIGGERS: &'static [&'static str] = &[":"];
67 const CODE_ACTION: bool = true;
68 const HOVER: bool = true;
69
70 const LEGEND_TYPES: &'static [swls_core::lsp_types::SemanticTokenType] = &[
71 semantic_token::BOOLEAN,
72 semantic_token::LANG_TAG,
73 SemanticTokenType::COMMENT,
74 SemanticTokenType::ENUM_MEMBER,
75 SemanticTokenType::ENUM,
76 SemanticTokenType::KEYWORD,
77 SemanticTokenType::NAMESPACE,
78 SemanticTokenType::NUMBER,
79 SemanticTokenType::PROPERTY,
80 SemanticTokenType::STRING,
81 SemanticTokenType::VARIABLE,
82 ];
83
84 const PATTERN: Option<&'static str> = None;
85
86 fn semantic_token_type(kind: rowan::SyntaxKind) -> Option<SemanticTokenType> {
87 use rdf_parsers::turtle::SyntaxKind as SK;
88
89 let k = kind.0;
91 if k == SK::Comment as u16 {
92 Some(SemanticTokenType::COMMENT)
93 } else if k == SK::Iriref as u16 {
94 Some(SemanticTokenType::PROPERTY)
95 } else if k == SK::Integer as u16 || k == SK::Decimal as u16 || k == SK::Double as u16 {
96 Some(SemanticTokenType::NUMBER)
97 } else if k == SK::StringLiteralQuote as u16
98 || k == SK::StringLiteralSingleQuote as u16
99 || k == SK::StringLiteralLongQuote as u16
100 || k == SK::StringLiteralLongSingleQuote as u16
101 {
102 Some(SemanticTokenType::STRING)
103 } else if k == SK::Langtag as u16 {
104 Some(semantic_token::LANG_TAG)
105 } else if k == SK::TrueLit as u16 || k == SK::FalseLit as u16 {
106 Some(semantic_token::BOOLEAN)
107 } else if k == SK::BaseToken as u16
108 || k == SK::PrefixToken as u16
109 || k == SK::SparqlBaseToken as u16
110 || k == SK::SparqlPrefixToken as u16
111 || k == SK::Alit as u16
112 {
113 Some(SemanticTokenType::KEYWORD)
114 } else {
115 None
116 }
117 }
118
119 fn semantic_token_spans(
120 kind: rowan::SyntaxKind,
121 span: std::ops::Range<usize>,
122 text: &str,
123 ) -> Vec<(SemanticTokenType, std::ops::Range<usize>)> {
124 use rdf_parsers::turtle::SyntaxKind as SK;
125 let k = kind.0;
126 let this_kind = SK::from(kind);
127 if k == SK::PnameLn as u16 {
128 let token_text = text.get(span.clone()).unwrap_or("");
132 if let Some((a, _)) = token_text.split_once(':') {
133 let (start, end) = (span.start, span.end);
134 vec![
135 (SemanticTokenType::NAMESPACE, start..start + a.len() + 1),
136 (SemanticTokenType::PROPERTY, start + a.len() + 1..end),
137 ]
138 } else {
139 vec![(SemanticTokenType::PROPERTY, span)]
140 }
141 } else if k == SK::PnameNs as u16 {
142 vec![(SemanticTokenType::NAMESPACE, span)]
143 } else if k == SK::BlankNodeLabel as u16 {
144 if span.len() > 2 {
146 vec![
147 (SemanticTokenType::NAMESPACE, span.start..span.start + 2),
148 (SemanticTokenType::PROPERTY, span.start + 2..span.end),
149 ]
150 } else {
151 vec![(SemanticTokenType::NAMESPACE, span)]
152 }
153 } else {
154 let output = Self::semantic_token_type(kind)
155 .map(|t| vec![(t, span.clone())])
156 .unwrap_or_default();
157 tracing::trace!("token kind {:?} {:?} {:?}", this_kind, span, output);
158 output
159 }
160 }
161}