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 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}