swls-core 0.1.0

Core LSP infrastructure for the Semantic Web Language Server
Documentation
use std::{borrow::Cow, collections::HashSet};

use bevy_ecs::prelude::*;
use completion::{CompletionRequest, SimpleCompletion};
use hover::HoverRequest;
use tracing::{instrument, trace};

use crate::{
    lsp_types::{CompletionItemKind, TextEdit},
    prelude::*,
};

#[instrument(skip(query, resource))]
pub fn complete_class(
    mut query: Query<(
        &TokenComponent,
        &TripleComponent,
        &Prefixes,
        &Types,
        &mut CompletionRequest,
    )>,
    hierarchy: Res<TypeHierarchy<'static>>,
    resource: Res<Ontologies>,
) {
    for (token, triple, prefixes, types, mut request) in &mut query {
        if triple.triple.predicate.value == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
            && triple.target == TripleTarget::Object
        {
            let tts = types.get(&triple.triple.subject.value);

            let superclasses: HashSet<_> = tts
                .iter()
                .flat_map(|x| x.iter())
                .flat_map(|t| hierarchy.iter_superclass(*t))
                .collect();

            for class in resource.classes.values() {
                let to_beat = prefixes
                    .shorten(&class.term.value)
                    .map(|x| Cow::Owned(x))
                    .unwrap_or(class.term.value.clone());

                if to_beat.starts_with(&token.text) {
                    let mut completion = SimpleCompletion::new(
                        CompletionItemKind::CLASS,
                        format!("{}", to_beat),
                        TextEdit {
                            range: token.range.clone(),
                            new_text: to_beat.to_string(),
                        },
                    )
                    .label_description(class.full_title())
                    .documentation(class.full_docs(&hierarchy, &prefixes));

                    if superclasses.contains(class.term.as_str()) {
                        completion.kind = CompletionItemKind::INTERFACE;
                        request.push(completion.sort_text(format!("0{}", to_beat)));
                    } else {
                        request.push(completion.sort_text(format!("1{}", to_beat)));
                    }
                }
            }
        }
    }
}

pub fn hover_class(
    mut query: Query<(&TokenComponent, &Prefixes, &mut HoverRequest)>,
    hierarchy: Res<TypeHierarchy<'static>>,
    resource: Res<Ontologies>,
) {
    for (token, prefixes, mut request) in &mut query {
        if let Some(target) = prefixes.expand(token.token.value()) {
            for class in resource.classes.values() {
                if class.term.value == target {
                    request.0.push(format!(
                        "## {}\n\n{}",
                        class.full_title(),
                        class.full_docs(&hierarchy, &prefixes)
                    ));
                }
            }
        }
    }
}

#[instrument(skip(query, hierarchy, resource, config))]
pub fn complete_properties(
    mut query: Query<(
        &TokenComponent,
        &TripleComponent,
        &Prefixes,
        &Label,
        &Types,
        &mut CompletionRequest,
    )>,
    hierarchy: Res<TypeHierarchy<'static>>,
    resource: Res<Ontologies>,
    config: Res<ServerConfig>,
) {
    for (token, triple, prefixes, _this_label, types, mut request) in &mut query {
        if triple.target == TripleTarget::Predicate {
            let tts = types.get(&triple.triple.subject.value);

            if let Some(tts) = tts.as_ref() {
                trace!("Types for {}", triple.triple.subject.value);
                for t in *tts {
                    trace!(
                        "{} {}",
                        triple.triple.subject.value,
                        hierarchy.type_name(*t)
                    );
                }
            } else {
                trace!("No types for {}", triple.triple.subject.value);
            }

            let subclasses: HashSet<_> = tts
                .iter()
                .flat_map(|x| x.iter())
                .flat_map(|t| hierarchy.iter_subclass(*t))
                .collect();

            for property in resource.properties.values() {
                let correct_domain = property
                    .domains
                    .iter()
                    .any(|domain| subclasses.contains(domain.as_str()));

                if !subclasses.is_empty()
                    && config
                        .config
                        .local
                        .completion
                        .correct_domain_required(&property.term.value)
                    && !correct_domain
                {
                    continue;
                }

                let to_beat = prefixes
                    .shorten(&property.term.value)
                    .map(|x| Cow::Owned(x))
                    .unwrap_or(property.term.value.clone());

                if to_beat.starts_with(&token.text) {
                    let mut completion = SimpleCompletion::new(
                        CompletionItemKind::ENUM_MEMBER,
                        format!("{}", to_beat),
                        TextEdit {
                            range: token.range.clone(),
                            new_text: to_beat.to_string(),
                        },
                    )
                    .label_description(&property.full_title())
                    .documentation(&property.full_docs(&prefixes));

                    if correct_domain {
                        completion.kind = CompletionItemKind::FIELD;
                        request.push(completion.sort_text(format!("0{}", to_beat)));
                    } else {
                        request.push(completion.sort_text(format!("1{}", to_beat)));
                    }
                }
            }
        }
    }
}

#[instrument(skip(query, resource))]
pub fn hover_property(
    mut query: Query<(
        &TokenComponent,
        &Prefixes,
        &DocumentLinks,
        &mut HoverRequest,
    )>,
    resource: Res<Ontologies>,
) {
    for (token, prefixes, _links, mut request) in &mut query {
        if let Some(target) = prefixes.expand(token.token.value()) {
            for c in resource
                .properties
                .values()
                .filter(|c| c.term.value == target)
            {
                request
                    .0
                    .push(format!("## {}\n\n{}", c.full_title(), c.full_docs(&prefixes)));
            }
        }
    }
}