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