1use bevy_ecs::{component::Component, resource::Resource, world::World};
2use swls_core::{
3 lang::{Lang, LangHelper},
4 lsp_types::SemanticTokenType,
5 prelude::*,
6};
7use swls_lang_rdf_base::register_rdf_lang;
8use swls_lang_turtle::lang::parser::TurtleParseError;
9
10pub mod ecs;
11use crate::ecs::{format_trig_system, setup_parsing};
12
13#[derive(Component, Default)]
14pub struct TriGLang;
15
16#[derive(Debug, Default)]
17pub struct TriGHelper;
18
19impl LangHelper for TriGHelper {
20 fn keyword(&self) -> &[&'static str] {
21 &["@prefix", "@base", "a", "GRAPH"]
22 }
23 fn model_based_rename(&self) -> bool {
24 true
25 }
26 fn blank_node_code_actions(&self) -> bool {
27 true
28 }
29}
30
31pub fn setup_world<C: Client + ClientSync + Resource + Clone>(world: &mut World) {
32 register_rdf_lang::<TriGLang, TriGHelper>(world, &["trig"], &[".trig"]);
33 setup_parsing(world);
34
35 world.schedule_scope(FormatLabel, |_, schedule| {
36 schedule.add_systems(format_trig_system);
37 });
38}
39
40impl Lang for TriGLang {
41 type ElementError = TurtleParseError;
42
43 const LANG: &'static str = "trig";
44 const TRIGGERS: &'static [&'static str] = &[":"];
45 const CODE_ACTION: bool = false;
46 const HOVER: bool = true;
47 const PATTERN: Option<&'static str> = None;
48
49 const LEGEND_TYPES: &'static [SemanticTokenType] = &[
50 semantic_token::BOOLEAN,
51 semantic_token::LANG_TAG,
52 SemanticTokenType::COMMENT,
53 SemanticTokenType::ENUM_MEMBER,
54 SemanticTokenType::KEYWORD,
55 SemanticTokenType::NAMESPACE,
56 SemanticTokenType::NUMBER,
57 SemanticTokenType::PROPERTY,
58 SemanticTokenType::STRING,
59 ];
60
61 fn semantic_token_type(kind: rowan::SyntaxKind) -> Option<SemanticTokenType> {
62 use rdf_parsers::trig::parser::SyntaxKind as SK;
63 match SK::from(kind) {
64 SK::Comment => Some(SemanticTokenType::COMMENT),
65 SK::Iriref => Some(SemanticTokenType::PROPERTY),
66 SK::Integer | SK::Decimal | SK::Double => Some(SemanticTokenType::NUMBER),
67 SK::StringLiteralQuote
68 | SK::StringLiteralSingleQuote
69 | SK::StringLiteralLongQuote
70 | SK::StringLiteralLongSingleQuote => Some(SemanticTokenType::STRING),
71 SK::Langtag => Some(semantic_token::LANG_TAG),
72 SK::TrueLit | SK::FalseLit => Some(semantic_token::BOOLEAN),
73 SK::BaseToken
74 | SK::PrefixToken
75 | SK::SparqlBaseToken
76 | SK::SparqlPrefixToken
77 | SK::GraphToken
78 | SK::Alit => Some(SemanticTokenType::KEYWORD),
79 _ => None,
80 }
81 }
82
83 fn semantic_token_spans(
84 kind: rowan::SyntaxKind,
85 span: std::ops::Range<usize>,
86 text: &str,
87 ) -> Vec<(SemanticTokenType, std::ops::Range<usize>)> {
88 use rdf_parsers::trig::parser::SyntaxKind as SK;
89 match SK::from(kind) {
90 SK::PnameLn => {
91 if let Some((a, _)) = text.get(span.clone()).and_then(|s| s.split_once(':')) {
93 let (start, end) = (span.start, span.end);
94 vec![
95 (SemanticTokenType::NAMESPACE, start..start + a.len() + 1),
96 (SemanticTokenType::PROPERTY, start + a.len() + 1..end),
97 ]
98 } else {
99 vec![(SemanticTokenType::PROPERTY, span)]
100 }
101 }
102 SK::PnameNs => vec![(SemanticTokenType::NAMESPACE, span)],
103 SK::BlankNodeLabel => {
104 if span.len() > 2 {
106 vec![
107 (SemanticTokenType::NAMESPACE, span.start..span.start + 2),
108 (SemanticTokenType::PROPERTY, span.start + 2..span.end),
109 ]
110 } else {
111 vec![(SemanticTokenType::NAMESPACE, span)]
112 }
113 }
114 _ => Self::semantic_token_type(kind)
115 .map(|t| vec![(t, span)])
116 .unwrap_or_default(),
117 }
118 }
119}