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 config: Res<ServerConfig>,
60) {
61 if !config.config.sparql.unwrap_or(true) {
62 return;
63 }
64 for (entity, source, tokens, label) in &query {
65 let (ref mut old_tokens, ref mut context) = old.entry(label.to_string()).or_default();
66
67 context.setup_current_to_prev(
68 TokenIdx { tokens: &tokens },
69 tokens.len(),
70 TokenIdx {
71 tokens: &old_tokens,
72 },
73 old_tokens.len(),
74 );
75 let ctx = context.ctx();
76
77 let (jsonld, es) = parse(source.as_str(), label.0.clone(), tokens.0.clone(), ctx);
78
79 *old_tokens = tokens.0.clone();
80 context.clear();
81
82 jsonld.add_to_context(context);
83
84 info!("{} triples ({} errors)", label.0, es.len());
86
87 if es.is_empty() {
88 let element = Element::<Sparql>(jsonld);
89 commands
90 .entity(entity)
91 .insert((element, Errors(es)))
92 .remove::<Dirty>();
93 } else {
94 let element = Element::<Sparql>(jsonld);
95 commands.entity(entity).insert((Errors(es), element, Dirty));
96 }
97 }
98}
99
100#[instrument(skip(query, commands))]
101fn derive_triples(
102 query: Query<(Entity, &Label, &Element<Sparql>), Changed<Element<Sparql>>>,
103 mut commands: Commands,
104) {
105 for (e, l, el) in &query {
106 let query = el.0.value();
107
108 let prefixes: Vec<_> = query
109 .prefixes
110 .iter()
111 .flat_map(|prefix| {
112 let url = prefix.value.expand(query)?;
113 let url = lsp_types::Url::parse(&url).ok()?;
114 Some(Prefix {
115 url,
116 prefix: prefix.prefix.value().clone(),
117 })
118 })
119 .collect();
120
121 commands.entity(e).insert(Prefixes(prefixes, l.0.clone()));
122
123 if let Ok(base) = BaseIri::new(query.base.to_string()) {
124 let mut builder = TriplesBuilder::new(query, base);
125 let _ = query.ingest_triples(&mut builder);
126 let triples: Vec<_> = builder.triples.into_iter().map(|x| x.to_owned()).collect();
127
128 commands.entity(e).insert(Triples(triples));
129 }
130 }
131}
132
133#[instrument(skip(query,))]
134pub fn variable_completion(
135 mut query: Query<(&Tokens, &TokenComponent, &mut CompletionRequest), With<Sparql>>,
136) {
137 for (tokens, token, mut req) in &mut query {
138 if token.text.starts_with('?') {
139 let token_set: HashSet<&str> = tokens
140 .0
141 .iter()
142 .flat_map(|x| match x.value() {
143 Token::Variable(x) => Some(x.as_str()),
144 _ => None,
145 })
146 .collect();
147
148 for x in token_set {
149 let t = format!("?{}", x);
150 let completion = SimpleCompletion::new(
151 CompletionItemKind::VARIABLE,
152 t.clone(),
153 lsp_types::TextEdit {
154 range: token.range.clone(),
155 new_text: t,
156 },
157 );
158 req.push(completion);
159 }
160 }
161 }
162}
163
164pub fn sparql_lov_undefined_prefix_completion(
165 mut query: Query<(
166 &TokenComponent,
167 &Element<Sparql>,
168 &Prefixes,
169 &mut CompletionRequest,
170 )>,
171) {
172 for (word, turtle, prefixes, mut req) in &mut query {
173 let mut start = Position::new(0, 0);
174
175 if turtle.base_statement.is_some() {
176 start = Position::new(1, 0);
177 }
178
179 use lsp_types::{Position, Range};
180 prefix_completion_helper(word, prefixes, &mut req.0, |name, location| {
181 Some(vec![lsp_types::TextEdit {
182 range: Range::new(start.clone(), start),
183 new_text: format!("PREFIX {}: <{}>\n", name, location),
184 }])
185 });
186 }
187}