Skip to main content

swls_lang_turtle/
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)]
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        // .owl ontologies are commonly serialized as Turtle; N-Triples (.nt) is a
45        // strict subset of Turtle. (.n3 is handled by the dedicated N3 language.)
46        &[".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        // Convert rowan::SyntaxKind(u16) to turtle SyntaxKind via the raw discriminant.
90        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            // PnameLn = "prefix:local" — emit NAMESPACE for "prefix:" and PROPERTY for "local".
129            // Slice to just the token text so split_once finds the colon within the token,
130            // not the first colon in the whole document.
131            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            // "_:label" — "_:" is NAMESPACE, label is PROPERTY
145            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}