Skip to main content

swls_lang_sparql/ecs/
mod.rs

1use std::{
2    collections::{HashMap, HashSet},
3    sync::Arc,
4};
5
6use bevy_ecs::{prelude::*, world::World};
7use completion::{CompletionRequest, SimpleCompletion};
8use rdf_parsers::{IncrementalBias, PrevParseInfo};
9use rowan::NodeOrToken;
10use sophia_api::{
11    quad::Quad as _,
12    term::{Term as _, TermKind},
13};
14use swls_core::{
15    components::*,
16    lsp_types::CompletionItemKind,
17    prelude::*,
18};
19use swls_lang_rdf_base::traits::{NamedNodeExt, TurtleExt};
20
21use crate::Sparql;
22
23pub fn setup_parse(world: &mut World) {
24    use swls_core::feature::parse::*;
25    world.schedule_scope(Label, |_, schedule| {
26        schedule.add_systems((
27            parse_sparql_system,
28            derive_triples
29                .after(parse_sparql_system)
30                .before(prefixes)
31                .before(triples),
32        ));
33    });
34}
35
36pub fn setup_completion(world: &mut World) {
37    use swls_core::feature::completion::*;
38    world.schedule_scope(Label, |_, schedule| {
39        schedule.add_systems((
40            variable_completion.after(generate_completions),
41        ));
42    });
43}
44
45fn extract_sparql_cst_tokens(
46    node: &rowan::SyntaxNode<rdf_parsers::sparql::parser::Lang>,
47) -> Vec<swls_core::prelude::Spanned<rowan::SyntaxKind>> {
48    use rdf_parsers::sparql::parser::SyntaxKind;
49    let mut tokens = Vec::new();
50    for node_or_token in node.descendants_with_tokens() {
51        if let NodeOrToken::Token(t) = node_or_token {
52            let kind = t.kind();
53            if kind == SyntaxKind::WhiteSpace {
54                continue;
55            }
56            let range = t.text_range();
57            let span = usize::from(range.start())..usize::from(range.end());
58            tokens.push(swls_core::prelude::spanned(
59                rowan::SyntaxKind(kind as u16),
60                span,
61            ));
62        }
63    }
64    tokens
65}
66
67#[instrument(skip(query, commands, prev_infos, config))]
68fn parse_sparql_system(
69    query: Query<(Entity, &Source, &Label), (Changed<Source>, With<Sparql>)>,
70    mut commands: Commands,
71    mut prev_infos: Local<HashMap<String, PrevParseInfo>>,
72    config: Res<ServerConfig>,
73) {
74    if !config.config.sparql.unwrap_or(true) {
75        return;
76    }
77    for (entity, source, label) in &query {
78        use rdf_parsers::sparql::{
79            convert::convert,
80            parser::{Lang, Rule, SyntaxKind},
81        };
82
83        let prev = prev_infos.get(label.as_str());
84        let (parse, new_prev) = rdf_parsers::parse_incremental(
85            Rule::new(SyntaxKind::QueryUnit),
86            source.0.as_str(),
87            prev,
88            IncrementalBias::default(),
89        );
90        prev_infos.insert(label.to_string(), new_prev);
91
92        let syntax = parse.syntax::<Lang>();
93        let errors = collect_errors(&syntax);
94        let mut turtle_model = convert(&syntax);
95        turtle_model.set_base = Some(label.to_string());
96
97        let cst_tokens = extract_sparql_cst_tokens(&syntax);
98
99        let span = 0..source.0.len();
100        let element = Element(swls_core::prelude::spanned(turtle_model, span));
101
102        if errors.is_empty() {
103            commands
104                .entity(entity)
105                .insert((element, Errors(errors), CstTokens(cst_tokens)))
106                .remove::<Dirty>();
107        } else {
108            commands
109                .entity(entity)
110                .insert((element, Errors(errors), CstTokens(cst_tokens), Dirty));
111        }
112    }
113}
114
115fn collect_errors(
116    node: &rowan::SyntaxNode<rdf_parsers::sparql::parser::Lang>,
117) -> Vec<swls_lang_turtle::lang::parser::TurtleParseError> {
118    use rdf_parsers::sparql::parser::SyntaxKind;
119    use swls_lang_turtle::lang::parser::TurtleParseError;
120
121    let mut errors = Vec::new();
122    let mut stack = vec![node.clone()];
123    while let Some(current) = stack.pop() {
124        for child in current.children_with_tokens() {
125            match child {
126                NodeOrToken::Node(n) => {
127                    if n.kind() == SyntaxKind::Error {
128                        let range = rdf_parsers::effective_error_span::<
129                            rdf_parsers::sparql::parser::Lang,
130                        >(&n);
131                        let msg = n
132                            .parent()
133                            .map(|p| format!("Expected: {:?}", p.kind()))
134                            .unwrap_or_else(|| format!("Unexpected: {}", n.text()));
135                        errors.push(TurtleParseError { range, msg });
136                    } else {
137                        stack.push(n);
138                    }
139                }
140                NodeOrToken::Token(t) => {
141                    if t.kind() == SyntaxKind::Error {
142                        let r = t.text_range();
143                        errors.push(TurtleParseError {
144                            range: r.start().into()..r.end().into(),
145                            msg: format!("Unexpected: {}", t.text()),
146                        });
147                    }
148                }
149            }
150        }
151    }
152    errors
153}
154
155#[instrument(skip(query, commands))]
156fn derive_triples(
157    query: Query<(Entity, &Label, &Element), (Changed<Element>, With<Sparql>)>,
158    mut commands: Commands,
159) {
160    for (entity, label, el) in &query {
161        let turtle = el.0.value();
162
163        let prefixes: Vec<_> = turtle
164            .prefixes
165            .iter()
166            .flat_map(|prefix| {
167                let url = prefix.value().value.expand(turtle)?;
168                let url = swls_core::lsp_types::Url::parse(&url).ok()?;
169                Some(Prefix {
170                    url,
171                    prefix: prefix.value().prefix.value().clone(),
172                })
173            })
174            .collect();
175
176        commands
177            .entity(entity)
178            .insert(Prefixes(prefixes, label.0.clone()));
179
180        match el.0.get_simple_triples() {
181            Ok(triples) => {
182                let triples: Vec<_> = triples.iter().map(|x| x.to_owned()).collect();
183                commands.entity(entity).insert(Triples(Arc::new(triples)));
184            }
185            Err(e) => {
186                tracing::warn!("derive_triples error for {}: {:?}", label.as_str(), e);
187            }
188        }
189    }
190}
191
192#[instrument(skip(query))]
193pub fn variable_completion(
194    mut query: Query<(&Triples, &TokenComponent, &mut CompletionRequest), With<Sparql>>,
195) {
196    for (triples, token, mut req) in &mut query {
197        if token.text.starts_with('?') {
198            let var_set: HashSet<_> = triples
199                .0
200                .iter()
201                .flat_map(|q| [q.s(), q.p(), q.o()])
202                .filter(|t| t.kind() == TermKind::Variable)
203                .map(|t| t.value.clone())
204                .collect();
205
206            for name in var_set {
207                let t = format!("?{}", name);
208                let completion = SimpleCompletion::new(
209                    CompletionItemKind::VARIABLE,
210                    t.clone(),
211                    swls_core::lsp_types::TextEdit {
212                        range: token.range.clone(),
213                        new_text: t,
214                    },
215                );
216                req.push(completion);
217            }
218        }
219    }
220}