lang_sparql/ecs/
mod.rs

1use std::collections::{HashMap, HashSet};
2
3use bevy_ecs::{prelude::*, world::World};
4use completion::{CompletionRequest, SimpleCompletion};
5use lang_turtle::lang::{
6    context::{Context, TokenIdx},
7    model::TriplesBuilder,
8};
9use lov::LocalPrefix;
10use lsp_core::{components::*, prelude::*, systems::prefix::prefix_completion_helper};
11use lsp_core::lsp_types::CompletionItemKind;
12use sophia_iri::resolve::BaseIri;
13
14use crate::{
15    lang::{parsing::parse, tokenizer::parse_tokens_str},
16    Sparql,
17};
18
19pub fn setup_parse(world: &mut World) {
20    use lsp_core::feature::parse::*;
21    world.schedule_scope(Label, |_, schedule| {
22        schedule.add_systems((
23            parse_source,
24            parse_sparql_system.after(parse_source),
25            derive_triples
26                .after(parse_sparql_system)
27                .before(prefixes)
28                .before(triples),
29        ));
30    });
31}
32
33pub fn setup_completion(world: &mut World) {
34    use lsp_core::feature::completion::*;
35    world.schedule_scope(Label, |_, schedule| {
36        schedule.add_systems((
37            sparql_lov_undefined_prefix_completion.after(get_current_token),
38            variable_completion.after(get_current_token),
39        ));
40    });
41}
42
43#[instrument(skip(query, commands))]
44fn parse_source(
45    query: Query<(Entity, &Source), (Changed<Source>, With<Sparql>)>,
46    mut commands: Commands,
47) {
48    for (entity, source) in &query {
49        let (tok, es) = parse_tokens_str(source.0.as_str());
50        info!("tokenized  {} tokens ({} errors)", tok.len(), es.len());
51        commands.entity(entity).insert((Tokens(tok), Errors(es)));
52    }
53}
54
55#[instrument(skip(query, commands))]
56fn parse_sparql_system(
57    query: Query<(Entity, &Source, &Tokens, &Label), (Changed<Tokens>, With<Sparql>)>,
58    mut commands: Commands,
59    mut old: Local<HashMap<String, (Vec<Spanned<Token>>, Context)>>,
60    config: Res<ServerConfig>,
61) {
62    if !config.config.sparql.unwrap_or(true) {
63        return;
64    }
65    for (entity, source, tokens, label) in &query {
66        let (ref mut old_tokens, ref mut context) = old.entry(label.to_string()).or_default();
67
68        context.setup_current_to_prev(
69            TokenIdx { tokens: &tokens },
70            tokens.len(),
71            TokenIdx {
72                tokens: &old_tokens,
73            },
74            old_tokens.len(),
75        );
76        let ctx = context.ctx();
77
78        let (jsonld, es) = parse(source.as_str(), label.0.clone(), tokens.0.clone(), ctx);
79
80        *old_tokens = tokens.0.clone();
81        context.clear();
82
83        jsonld.add_to_context(context);
84
85        // turtle.set_context(context);
86        info!("{} triples ({} errors)", label.0, es.len());
87
88        if es.is_empty() {
89            let element = Element::<Sparql>(jsonld);
90            commands
91                .entity(entity)
92                .insert((element, Errors(es)))
93                .remove::<Dirty>();
94        } else {
95            let element = Element::<Sparql>(jsonld);
96            commands.entity(entity).insert((Errors(es), element, Dirty));
97        }
98    }
99}
100
101#[instrument(skip(query, commands))]
102fn derive_triples(
103    query: Query<(Entity, &Label, &Element<Sparql>), Changed<Element<Sparql>>>,
104    mut commands: Commands,
105) {
106    for (e, l, el) in &query {
107        let query = el.0.value();
108
109        let prefixes: Vec<_> = query
110            .prefixes
111            .iter()
112            .flat_map(|prefix| {
113                let url = prefix.value.expand(query)?;
114                let url = lsp_core::lsp_types::Url::parse(&url).ok()?;
115                Some(Prefix {
116                    url,
117                    prefix: prefix.prefix.value().clone(),
118                })
119            })
120            .collect();
121
122        commands.entity(e).insert(Prefixes(prefixes, l.0.clone()));
123
124        if let Ok(base) = BaseIri::new(query.base.to_string()) {
125            let mut builder = TriplesBuilder::new(query, base);
126            let _ = query.ingest_triples(&mut builder);
127            let triples: Vec<_> = builder.triples.into_iter().map(|x| x.to_owned()).collect();
128
129            commands.entity(e).insert(Triples(triples));
130        }
131    }
132}
133
134#[instrument(skip(query,))]
135pub fn variable_completion(
136    mut query: Query<(&Tokens, &TokenComponent, &mut CompletionRequest), With<Sparql>>,
137) {
138    for (tokens, token, mut req) in &mut query {
139        if token.text.starts_with('?') {
140            let token_set: HashSet<&str> = tokens
141                .0
142                .iter()
143                .flat_map(|x| match x.value() {
144                    Token::Variable(x) => Some(x.as_str()),
145                    _ => None,
146                })
147                .collect();
148
149            for x in token_set {
150                let t = format!("?{}", x);
151                let completion = SimpleCompletion::new(
152                    CompletionItemKind::VARIABLE,
153                    t.clone(),
154                    lsp_core::lsp_types::TextEdit {
155                        range: token.range.clone(),
156                        new_text: t,
157                    },
158                );
159                req.push(completion);
160            }
161        }
162    }
163}
164
165pub fn sparql_lov_undefined_prefix_completion(
166    mut query: Query<(
167        &TokenComponent,
168        &Element<Sparql>,
169        &Prefixes,
170        &mut CompletionRequest,
171    )>,
172    lovs: Query<&LocalPrefix>,
173) {
174    for (word, turtle, prefixes, mut req) in &mut query {
175        let mut start = Position::new(0, 0);
176
177        if turtle.base_statement.is_some() {
178            start = Position::new(1, 0);
179        }
180
181        use lsp_core::lsp_types::{Position, Range};
182        prefix_completion_helper(
183            word,
184            prefixes,
185            &mut req.0,
186            |name, location| {
187                Some(vec![lsp_core::lsp_types::TextEdit {
188                    range: Range::new(start.clone(), start),
189                    new_text: format!("PREFIX {}: <{}>\n", name, location),
190                }])
191            },
192            lovs.iter(),
193        );
194    }
195}