lang_turtle/ecs/
mod.rs

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        // assert_eq!(world.entities().len(), 1);
148        let c = world.resource::<TestClient>().clone();
149        block_on(c.await_futures(|| world.run_schedule(lsp_core::feature::ParseLabel)));
150
151        // We added 3 ontologies that are always present
152        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}