swls_lang_turtle/ecs/
mod.rs1use bevy_ecs::{prelude::*, world::World};
2use completion::{infer_predicate_position_from_cst, subject_completion};
3use format::format_turtle_system;
4use swls_core::prelude::*;
5
6use crate::TurtleLang;
7
8mod code_action;
9mod completion;
10mod format;
11pub mod parse;
12
13pub fn setup_parsing(world: &mut World) {
14 use swls_core::feature::parse::*;
15 world.schedule_scope(ParseLabel, |_, schedule| {
16 schedule.add_systems((
17 parse::parse_turtle_system,
18 swls_lang_rdf_base::parse::derive_prefixes_system::<TurtleLang>
19 .after(parse::parse_turtle_system)
20 .before(prefixes),
21 parse::derive_triples_system::<TurtleLang>
22 .after(parse::parse_turtle_system)
23 .before(triples),
24 ));
25 });
26}
27
28pub fn setup_formatting(world: &mut World) {
29 world.schedule_scope(FormatLabel, |_, schedule| {
30 schedule.add_systems(format_turtle_system);
31 });
32}
33
34pub fn setup_code_action(world: &mut World) {
35 use swls_core::feature::code_action::Label as CodeActionLabel;
36 world.schedule_scope(CodeActionLabel, |_, schedule| {
37 schedule.add_systems(code_action::organize_imports);
38 });
39}
40
41pub fn setup_completion(world: &mut World) {
42 use swls_core::feature::completion::*;
43 world.schedule_scope(CompletionLabel, |_, schedule| {
44 schedule.add_systems((
45 subject_completion.after(generate_completions),
46 infer_predicate_position_from_cst.after(generate_completions),
47 ));
48 });
49}
50
51#[cfg(test)]
52mod tests {
53 use futures::executor::block_on;
54 use swls_core::text::LineIndex;
55 use swls_core::{
56 components::*,
57 prelude::{diagnostics::DiagnosticItem, *},
58 };
59 use swls_test_utils::{create_file, setup_world, TestClient};
60 use test_log::test;
61
62 #[test]
63 fn diagnostics_work() {
64 let (mut world, mut rx) = setup_world(TestClient::new(), crate::setup_world::<TestClient>);
65
66 let t2 = "\n@prefix foaf: <>.\nfoaf:foaf\n ";
68 let t3 = "\n@prefix foaf: <>.\nfoa\n ";
70
71 let mut last_diags = move || -> Vec<swls_core::lsp_types::Diagnostic> {
72 let mut items: Vec<DiagnosticItem> = Vec::new();
73 while let Ok(Some(x)) = rx.try_next() {
74 items.push(x);
75 }
76 items
77 .into_iter()
78 .last()
79 .map(|i| i.diagnostics)
80 .unwrap_or_default()
81 };
82
83 let entity = create_file(&mut world, t2, "http://example.com/ns#", "turtle", Open);
84 world.run_schedule(ParseLabel);
85
86 let diags = last_diags();
88 assert!(
89 !diags.is_empty(),
90 "t2: expected syntax errors for incomplete triple"
91 );
92
93 world
95 .entity_mut(entity)
96 .insert((Source(t3.to_string()), RopeC(LineIndex::new(t3))));
97 world.run_schedule(ParseLabel);
98
99 let diags = last_diags();
100 assert!(
101 !diags.is_empty(),
102 "t3: expected syntax errors for invalid token"
103 );
104 }
105
106 #[test_log::test]
107 fn fetch_lov_properties_test() {
108 let mut client = TestClient::new();
109 client.add_res("http://xmlns.com/foaf/0.1/", " @prefix foaf: <>. ");
110 let (mut world, _) = setup_world(TestClient::new(), crate::setup_world::<TestClient>);
111
112 let t1 = " @prefix foaf: <http://xmlns.com/foaf/0.1/>.";
113 create_file(&mut world, t1, "http://example.com/ns#", "turtle", Open);
114
115 let c = world.resource::<TestClient>().clone();
116 block_on(c.await_futures(|| world.run_schedule(swls_core::feature::ParseLabel)));
117
118 assert!(world.entities().len() >= 2 + 3);
120 }
121
122 #[test]
123 fn turtle_does_prefix_links() {
124 let (mut world, _) = setup_world(TestClient::new(), crate::setup_world::<TestClient>);
125
126 let t1 = " @prefix foaf: <http://xmlns.com/foaf/0.1/>.";
127 let entity = create_file(&mut world, t1, "http://example.com/ns#", "turtle", Open);
128
129 let links: &DocumentLinks = world.entity(entity).get().expect("document links exists");
130 assert_eq!(links.len(), 1);
131 assert_eq!(links[0].0.as_str(), "file:///tmp/swls/test/foaf.ttl");
132 assert_eq!(links[0].1, "prefix import");
133 }
134}
135
136#[cfg(test)]
141mod prefix_datatype_tests {
142 use swls_core::lsp_types::Url;
143 use swls_core::prelude::*;
144 use swls_core::systems::prefix_diagnostic_helper;
145 use swls_core::text::LineIndex;
146
147 use crate::lang::parser::parse_new;
148
149 const BASE: &str = "http://example.com/";
150
151 fn run(src: &str, declared: &[&str], report_unused: bool) -> Vec<String> {
152 let (turtle, ..) = parse_new(src, BASE, None);
153 let prefixes = Prefixes(
154 declared
155 .iter()
156 .map(|p| Prefix {
157 prefix: p.to_string(),
158 url: Url::parse("http://www.w3.org/2001/XMLSchema#").unwrap(),
159 })
160 .collect(),
161 Url::parse(BASE).unwrap(),
162 );
163 let rope = RopeC(LineIndex::new(src));
164 let label = Label(Url::parse(BASE).unwrap());
165
166 let (diags, _) = prefix_diagnostic_helper(
167 &turtle,
168 &prefixes,
169 &rope,
170 &label,
171 std::iter::empty(),
172 std::iter::empty(),
173 |_, _| Vec::new(),
174 true,
175 report_unused,
176 );
177 diags.into_iter().map(|d| d.message).collect()
178 }
179
180 #[test]
181 fn datatype_prefix_counts_as_used() {
182 let src = "@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.\n<s> <p> \"5\"^^xsd:integer .\n";
183 let msgs = run(src, &["xsd"], true);
184 assert!(
185 !msgs.iter().any(|m| m.contains("never used")),
186 "xsd used only in a datatype must not be reported unused: {msgs:?}",
187 );
188 }
189
190 #[test]
191 fn undefined_datatype_prefix_is_flagged() {
192 let src = "<s> <p> \"5\"^^xsd:integer .\n";
193 let msgs = run(src, &[], false);
194 assert!(
195 msgs.iter().any(|m| m.contains("Undefined prefix \"xsd\"")),
196 "xsd in a datatype but undeclared must be flagged: {msgs:?}",
197 );
198 }
199}