qlue-ls 3.3.1

A language server for SPARQL
Documentation
use std::rc::Rc;

use futures::{channel::oneshot, lock::Mutex};
use ll_sparql_parser::ast::AstNode;

#[cfg(not(target_arch = "wasm32"))]
use tokio::task::spawn_local;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_futures::spawn_local;

use crate::{
    Server,
    server::{
        lsp::CompletionList,
        message_handler::completion::{
            CompletionEnvironment, CompletionError, CompletionLocation,
            utils::{CompletionTemplate, dispatch_completion_query},
        },
    },
};

pub async fn completions(
    server_rc: Rc<Mutex<Server>>,
    environment: &CompletionEnvironment,
) -> Result<CompletionList, CompletionError> {
    let mut template_context = environment.template_context().await;
    template_context.insert("local_context", &local_context(environment)?);

    let (sender, receiver) = oneshot::channel::<CompletionList>();

    let server_rc_1 = server_rc.clone();
    let template_context_1 = template_context.clone();
    let environment_1 = environment.clone();
    spawn_local(async move {
        match dispatch_completion_query(
            server_rc_1,
            &environment_1,
            template_context_1,
            CompletionTemplate::ValuesCompletionContextInsensitive,
            false,
        )
        .await
        {
            Ok(res) => {
                if let Err(_err) = sender.send(res) {
                    // NOTE: This should happen if the context sensitive completion succeeds first.
                }
            }
            Err(err) => {
                tracing::info!("Context insensitive completion query failed:\n{:?}", err);
            }
        };
    });

    match dispatch_completion_query(
        server_rc,
        environment,
        template_context,
        CompletionTemplate::ValuesCompletionContextSensitive,
        false,
    )
    .await
    {
        Ok(res) => Ok(res),
        Err(err) => {
            tracing::info!("Context sensitive completion query failed:\n{:?}", err);
            receiver.await.map_err(|_e| err)
        }
    }
}

fn local_context(environment: &CompletionEnvironment) -> Result<String, CompletionError> {
    if let CompletionLocation::InlineData((ref inline_data, index)) = environment.location {
        let var = inline_data
            .visible_variables()
            .get(index)
            .ok_or(CompletionError::Resolve(
                "VALUES clause has no variable at index".to_string(),
            ))?
            .text();
        Ok(format!("BIND({} AS ?qls_entity)", var))
    } else {
        Err(CompletionError::Resolve(
            "inline_data completion called for non InlineData location".to_string(),
        ))
    }
}