lang_turtle/ecs/
completion.rs

1use bevy_ecs::prelude::*;
2use completion::{CompletionRequest, SimpleCompletion};
3use lsp_core::{components::*, prelude::*, systems::prefix::prefix_completion_helper};
4use lsp_types::CompletionItemKind;
5use tracing::debug;
6
7use crate::{lang::model::NamedNode, TurtleLang};
8
9pub fn turtle_lov_undefined_prefix_completion(
10    mut query: Query<(
11        &TokenComponent,
12        &Element<TurtleLang>,
13        &Prefixes,
14        &mut CompletionRequest,
15    )>,
16) {
17    for (word, turtle, prefixes, mut req) in &mut query {
18        let mut start = Position::new(0, 0);
19
20        if turtle.base.is_some() {
21            start = Position::new(1, 0);
22        }
23
24        use lsp_types::{Position, Range};
25        prefix_completion_helper(word, prefixes, &mut req.0, |name, location| {
26            Some(vec![lsp_types::TextEdit {
27                range: Range::new(start.clone(), start),
28                new_text: format!("@prefix {}: <{}>.\n", name, location),
29            }])
30        });
31    }
32}
33
34pub fn subject_completion(
35    mut query: Query<(
36        &TokenComponent,
37        &Element<TurtleLang>,
38        &mut CompletionRequest,
39    )>,
40    triples: Query<(&Triples, &Label), With<Open>>,
41) {
42    for (word, turtle, mut req) in &mut query {
43        let m_expaned = match word.token.value() {
44            Token::PNameLN(pref, value) => NamedNode::Prefixed {
45                prefix: pref.clone().unwrap_or_default(),
46                value: value.clone(),
47                idx: 0,
48            }
49            .expand(turtle.0.value()),
50            _ => continue,
51        };
52        let Some(expanded) = m_expaned else { continue };
53
54        for (triples, label) in &triples {
55            for triple in &triples.0 {
56                debug!("Triple {} start with {}", triple.subject.as_str(), expanded);
57                let subj = triple.subject.as_str();
58                if subj.starts_with(&expanded) {
59                    let new_text = turtle.0.shorten(subj).unwrap_or_else(|| String::from(subj));
60
61                    if new_text != word.text {
62                        req.push(
63                            SimpleCompletion::new(
64                                CompletionItemKind::MODULE,
65                                subj.to_string(),
66                                lsp_types::TextEdit {
67                                    new_text,
68                                    range: word.range.clone(),
69                                },
70                            )
71                            .documentation(format!("Subject from {}", label.0)),
72                        );
73                    }
74                }
75            }
76        }
77    }
78}
79
80#[cfg(test)]
81mod tests {
82
83    use completion::CompletionRequest;
84    use futures::executor::block_on;
85    use lsp_core::{components::*, lang::LangHelper, prelude::*, Tasks};
86    use ropey::Rope;
87    use test_log::test;
88    use test_utils::{create_file, setup_world, TestClient};
89    use tracing::info;
90
91    use crate::TurtleHelper;
92
93    #[test]
94    fn completion_event_works() {
95        println!("completion_event_works");
96        let (mut world, _) = setup_world(TestClient::new(), crate::setup_world);
97
98        let t1 = "
99@prefix foaf: <http://xmlns.com/foaf/0.1/>.
100            ";
101
102        let t2 = "
103@prefix foaf: <http://xmlns.com/foaf/0.1/>.
104foa
105            ";
106
107        let entity = create_file(&mut world, t1, "http://example.com/ns#", "turtle", Open);
108
109        world
110            .entity_mut(entity)
111            .insert((Source(t2.to_string()), RopeC(Rope::from_str(t2))));
112        world.run_schedule(ParseLabel);
113
114        // start call completion
115        world.entity_mut(entity).insert((
116            CompletionRequest(vec![]),
117            PositionComponent(lsp_types::Position {
118                line: 2,
119                character: 0,
120            }),
121        ));
122
123        world.run_schedule(CompletionLabel);
124        let m_completions = world.entity_mut(entity).take::<CompletionRequest>();
125
126        assert!(m_completions.is_some());
127        let completions = m_completions.unwrap().0;
128        assert_eq!(completions.len(), 4 + TurtleHelper.keyword().len());
129    }
130
131    #[test_log::test]
132    fn completion_event_works_multiple_files() {
133        info!("Testing multiple files");
134        let (mut world, _) = setup_world(TestClient::new(), crate::setup_world);
135        let t1_1 = "
136@prefix foaf: <http://xmlns.com/foaf/0.1/>.
137            ";
138
139        let t1_2 = "
140@prefix foaf: <http://xmlns.com/foaf/0.1/>.
141foaf:
142            ";
143
144        let t2 = "
145@prefix foaf: <http://xmlns.com/foaf/0.1/>.
146
147foaf:me foaf:friend <#me>.
148            ";
149
150        let entity = create_file(
151            &mut world,
152            t1_1,
153            "http://example.com/first_file#",
154            "turtle",
155            Open,
156        );
157
158        create_file(
159            &mut world,
160            t2,
161            "http://example.com/second_file#",
162            "turtle",
163            Open,
164        );
165
166        world.entity_mut(entity).insert((
167            Source(t1_2.to_string()),
168            RopeC(Rope::from_str(t1_2)),
169            Open,
170        ));
171        world.run_schedule(ParseLabel);
172
173        // start call completion
174        world.entity_mut(entity).insert((
175            CompletionRequest(vec![]),
176            PositionComponent(lsp_types::Position {
177                line: 2,
178                character: 0,
179            }),
180        ));
181        world.run_schedule(CompletionLabel);
182
183        let completions = world
184            .entity_mut(entity)
185            .take::<CompletionRequest>()
186            .expect("Completions exists")
187            .0;
188
189        assert_eq!(completions.len(), 1 + TurtleHelper.keyword().len());
190    }
191
192    #[test_log::test]
193    fn test_autocomplete_classes() {
194        println!("completion_event_works");
195        let (mut world, _) = setup_world(TestClient::new(), crate::setup_world);
196
197        let t1 = "@prefix foaf: <http://xmlns.com/foaf/0.1/>.";
198
199        let t2 = "@prefix foaf: <http://xmlns.com/foaf/0.1/>.
200<> a foa";
201
202        let entity = create_file(&mut world, t1, "http://example.com/ns#", "turtle", Open);
203
204        let c = world.resource::<TestClient>().clone();
205        block_on(c.await_futures(|| world.run_schedule(Tasks)));
206
207        world
208            .entity_mut(entity)
209            .insert((Source(t2.to_string()), RopeC(Rope::from_str(t2)), Open));
210        world.run_schedule(ParseLabel);
211
212        block_on(c.await_futures(|| world.run_schedule(Tasks)));
213
214        // start call completion
215        world.entity_mut(entity).insert((
216            CompletionRequest(vec![]),
217            PositionComponent(lsp_types::Position {
218                line: 1,
219                character: 6,
220            }),
221        ));
222        world.run_schedule(CompletionLabel);
223        let completions = world
224            .entity_mut(entity)
225            .take::<CompletionRequest>()
226            .expect("competion request")
227            .0;
228
229        for c in &completions {
230            println!("c {:?} {:?}", c.label, c._documentation);
231        }
232        assert_eq!(
233            completions.len(),
234            4 /* prefix.cc */ + 14 /*completions */ + TurtleHelper.keyword().len()
235        );
236    }
237
238    #[test_log::test]
239    fn test_autocomplete_properties_3() {
240        println!("completion_event_works");
241        let (mut world, _) = setup_world(TestClient::new(), crate::setup_world);
242
243        let t1 = "@prefix foaf: <http://xmlns.com/foaf/0.1/>.";
244
245        let t2 = "@prefix foaf: <http://xmlns.com/foaf/0.1/>.
246<> foaf:";
247
248        let entity = create_file(&mut world, t1, "http://example.com/ns#", "turtle", Open);
249
250        let c = world.resource::<TestClient>().clone();
251        block_on(c.await_futures(|| world.run_schedule(Tasks)));
252
253        world
254            .entity_mut(entity)
255            .insert((Source(t2.to_string()), RopeC(Rope::from_str(t2)), Open));
256        world.run_schedule(ParseLabel);
257
258        block_on(c.await_futures(|| world.run_schedule(Tasks)));
259
260        // start call completion
261        world.entity_mut(entity).insert((
262            CompletionRequest(vec![]),
263            PositionComponent(lsp_types::Position {
264                line: 1,
265                character: 4,
266            }),
267        ));
268        world.run_schedule(CompletionLabel);
269        let completions = world
270            .entity_mut(entity)
271            .take::<CompletionRequest>()
272            .expect("competion request")
273            .0;
274
275        assert_eq!(completions.len(), 62 + TurtleHelper.keyword().len());
276    }
277
278    #[test_log::test]
279    fn test_autocomplete_properties_2() {
280        println!("completion_event_works");
281        let (mut world, _) = setup_world(TestClient::new(), crate::setup_world);
282
283        let t1 = "@prefix foaf: <http://xmlns.com/foaf/0.1/>.
284<> a foaf:Person;
285    foaf:name \"Arthur\".";
286
287        let t2 = "@prefix foaf: <http://xmlns.com/foaf/0.1/>.
288<> a foaf:Person;
289    foaf:
290    foaf:name \"Arthur\".";
291
292        let entity = create_file(&mut world, t1, "http://example.com/ns#", "turtle", Open);
293
294        let c = world.resource::<TestClient>().clone();
295        block_on(c.await_futures(|| world.run_schedule(Tasks)));
296
297        world
298            .entity_mut(entity)
299            .insert((Source(t2.to_string()), RopeC(Rope::from_str(t2)), Open));
300        world.run_schedule(ParseLabel);
301
302        block_on(c.await_futures(|| world.run_schedule(Tasks)));
303
304        // start call completion
305        world.entity_mut(entity).insert((
306            CompletionRequest(vec![]),
307            PositionComponent(lsp_types::Position {
308                line: 2,
309                character: 5,
310            }),
311        ));
312        world.run_schedule(CompletionLabel);
313        let completions = world
314            .entity_mut(entity)
315            .take::<CompletionRequest>()
316            .expect("competion request")
317            .0;
318
319        assert_eq!(completions.len(), 62 + TurtleHelper.keyword().len());
320    }
321}