Skip to main content

swls_core/feature/
inlay.rs

1use std::collections::HashSet;
2
3use bevy_ecs::{
4    component::Component,
5    schedule::ScheduleLabel,
6    system::{Query, Res},
7    world::World,
8};
9use derive_more::{AsMut, AsRef, Deref, DerefMut};
10use sophia_api::{ns::rdf, prelude::Dataset};
11
12use crate::{
13    prelude::{Prefixes, RopeC, Triples, TypeHierarchy, Types},
14    util::offset_to_position,
15};
16
17/// [`Component`] indicating that the current document is currently handling a Inlay request.
18#[derive(Component, AsRef, Deref, AsMut, DerefMut, Debug, Default)]
19pub struct InlayRequest(pub Vec<crate::lsp_types::InlayHint>);
20
21/// [`ScheduleLabel`] related to the Inlay schedule
22#[derive(ScheduleLabel, Clone, Eq, PartialEq, Debug, Hash)]
23pub struct Label;
24pub fn setup_schedule(world: &mut World) {
25    let mut inlay = bevy_ecs::schedule::Schedule::new(Label);
26    inlay.add_systems(inlay_types);
27    world.add_schedule(inlay);
28}
29
30#[tracing::instrument(skip(query, hierarchy))]
31fn inlay_types(
32    query: Query<(&Triples, &Types, &RopeC, &Prefixes, &mut InlayRequest)>,
33    hierarchy: Res<TypeHierarchy<'static>>,
34) {
35    for (triples, types, rope, prefixes, mut request) in query {
36        let subjects: HashSet<_> = triples.subjects().flatten().collect();
37
38        let t = &mut request.0;
39
40        for s in subjects {
41            if let Some(types) = types.get(s.as_str()) {
42                let types: Vec<_> = types.iter().map(|e| hierarchy.type_name(*e)).collect();
43                let defined: HashSet<_> = triples.objects([s], [rdf::type_]).collect();
44                let defined_strings: HashSet<String> =
45                    defined.iter().map(|x| x.value.to_string()).collect();
46
47                let mut inlay_str = String::new();
48                for t in &types {
49                    if defined_strings.contains(t.as_ref()) {
50                        continue;
51                    }
52                    if !inlay_str.is_empty() {
53                        inlay_str += ", ";
54                    }
55                    if let Some(short) = prefixes.shorten(t.as_ref()) {
56                        inlay_str += short.as_str();
57                    } else {
58                        inlay_str += t.as_ref();
59                    }
60                }
61
62                if inlay_str.is_empty() {
63                    continue;
64                }
65                if let Some(pos) = defined.iter().max_by_key(|x| x.span.end) {
66                    if let Some(pos) = offset_to_position(pos.span.end, &rope) {
67                        t.push(crate::lsp_types::InlayHint {
68                            position: pos,
69                            label: crate::lsp_types::InlayHintLabel::String(format!(
70                                ", {}",
71                                inlay_str
72                            )),
73                            kind: None,
74                            text_edits: None,
75                            tooltip: None,
76                            padding_left: None,
77                            padding_right: None,
78                            data: None,
79                        });
80                    } else {
81                        tracing::warn!("Failed to convert pos to position {}", pos.span.end);
82                    }
83                } else {
84                    let offset = if rope.get_char(s.span.start) == Some('[') {
85                        s.span.start + 1
86                    } else {
87                        s.span.end
88                    };
89
90                    if let Some(pos) = offset_to_position(offset, &rope) {
91                        t.push(crate::lsp_types::InlayHint {
92                            position: pos,
93                            label: crate::lsp_types::InlayHintLabel::String(format!(
94                                " a {};",
95                                inlay_str
96                            )),
97                            kind: None,
98                            text_edits: None,
99                            tooltip: None,
100                            padding_left: None,
101                            padding_right: None,
102                            data: None,
103                        });
104                    } else {
105                        tracing::warn!("Failed to convert pos to position {}", s.span.end);
106                    }
107                }
108            }
109        }
110    }
111}