1use bevy_ecs::{prelude::*, system::Query, world::World};
2use completion::{subject_completion, turtle_lov_undefined_prefix_completion};
3use format::format_turtle_system;
4use lsp_core::prelude::*;
5use parse::{derive_triples, parse_source, parse_turtle_system};
6
7use crate::TurtleLang;
8
9mod completion;
10mod format;
11mod parse;
12
13pub fn setup_parsing(world: &mut World) {
14 use lsp_core::feature::parse::*;
15 world.schedule_scope(ParseLabel, |_, schedule| {
16 schedule.add_systems((
17 parse_source,
18 parse_turtle_system.after(parse_source),
19 derive_prefixes.after(parse_turtle_system).before(prefixes),
20 derive_triples.after(parse_turtle_system).before(triples),
21 ));
22 });
23}
24
25pub fn setup_formatting(world: &mut World) {
26 world.schedule_scope(FormatLabel, |_, schedule| {
27 schedule.add_systems(format_turtle_system);
28 });
29}
30
31pub fn setup_completion(world: &mut World) {
32 use lsp_core::feature::completion::*;
33 world.schedule_scope(CompletionLabel, |_, schedule| {
34 schedule.add_systems((
35 turtle_lov_undefined_prefix_completion.after(get_current_token),
36 subject_completion.after(get_current_token),
37 ));
38 });
39}
40
41fn derive_prefixes(
42 query: Query<(Entity, &Label, &Element<TurtleLang>), Changed<Element<TurtleLang>>>,
43 mut commands: Commands,
44) {
45 for (entity, url, turtle) in &query {
46 let prefixes: Vec<_> = turtle
47 .prefixes
48 .iter()
49 .flat_map(|prefix| {
50 let url = prefix.value.expand(turtle.value())?;
51 let url = lsp_types::Url::parse(&url).ok()?;
52 Some(Prefix {
53 url,
54 prefix: prefix.prefix.value().clone(),
55 })
56 })
57 .collect();
58
59 let base = turtle
60 .base
61 .as_ref()
62 .and_then(|b| {
63 b.0 .1
64 .expand(turtle.value())
65 .and_then(|x| lsp_types::Url::parse(&x).ok())
66 })
67 .unwrap_or(url.0.clone());
68
69 commands.entity(entity).insert(Prefixes(prefixes, base));
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use chumsky::chain::Chain;
76 use futures::executor::block_on;
77 use lsp_core::{
78 components::*,
79 prelude::{diagnostics::DiagnosticItem, *},
80 };
81 use ropey::Rope;
82 use test_log::test;
83 use test_utils::{create_file, setup_world, TestClient};
84
85 #[test]
86 fn diagnostics_work() {
87 let (mut world, mut rx) = setup_world(TestClient::new(), crate::setup_world);
88
89 let t1 = "
90@prefix foaf: <>.
91 ";
92
93 let t2 = "
94@prefix foaf: <>.
95foaf:foaf
96 ";
97
98 let t3 = "
99@prefix foaf: <>.
100foa
101 ";
102
103 let entity = create_file(&mut world, t1, "http://example.com/ns#", "turtle", Open);
104 world.run_schedule(ParseLabel);
105 world.run_schedule(DiagnosticsLabel);
106
107 let mut get_diagnostics = move || {
108 let mut out: Vec<DiagnosticItem> = Vec::new();
109 while let Ok(Some(x)) = rx.try_next() {
110 out.push(x);
111 }
112 out
113 };
114 let items = get_diagnostics();
115 assert!(items[0].diagnostics.is_empty());
116
117 world
118 .entity_mut(entity)
119 .insert((Source(t2.to_string()), RopeC(Rope::from_str(t2))));
120 world.run_schedule(ParseLabel);
121 world.run_schedule(DiagnosticsLabel);
122
123 let items = get_diagnostics();
124
125 assert_eq!(items.len(), 2, "url: t2");
126 assert_eq!(items[0].diagnostics.len(), 2);
127 world
128 .entity_mut(entity)
129 .insert((Source(t3.to_string()), RopeC(Rope::from_str(t2))));
130 world.run_schedule(ParseLabel);
131 world.run_schedule(DiagnosticsLabel);
132
133 let items = get_diagnostics();
134 assert_eq!(items.len(), 2, "url: t3");
135 assert_eq!(items[0].diagnostics.len(), 4);
136 }
137
138 #[test_log::test]
139 fn fetch_lov_properties_test() {
140 let mut client = TestClient::new();
141 client.add_res("http://xmlns.com/foaf/0.1/", " @prefix foaf: <>. ");
142 let (mut world, _) = setup_world(TestClient::new(), crate::setup_world);
143
144 let t1 = " @prefix foaf: <http://xmlns.com/foaf/0.1/>.";
145 create_file(&mut world, t1, "http://example.com/ns#", "turtle", Open);
146
147 let c = world.resource::<TestClient>().clone();
149 block_on(c.await_futures(|| world.run_schedule(lsp_core::feature::ParseLabel)));
150
151 assert_eq!(world.entities().len(), 2 + 3);
153 }
154
155 #[test]
156 fn turtle_does_prefix_links() {
157 let (mut world, _) = setup_world(TestClient::new(), crate::setup_world);
158
159 let t1 = " @prefix foaf: <http://xmlns.com/foaf/0.1/>.";
160 let entity = create_file(&mut world, t1, "http://example.com/ns#", "turtle", Open);
161
162 let links: &DocumentLinks = world.entity(entity).get().expect("document links exists");
163 assert_eq!(links.len(), 1);
164 assert_eq!(links[0].0.as_str(), "file:///tmp/swls/test/foaf.ttl");
165 assert_eq!(links[0].1, "prefix import");
166 }
167}