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