use std::rc::Rc;
use crate::server::{
Server,
lsp::errors::{ErrorCode, LSPError},
message_handler::misc::resolve_backend_at_token,
sparql_operations::execute_query,
};
use futures::lock::Mutex;
use ll_sparql_parser::{
SyntaxNode, SyntaxToken,
ast::{AstNode, Iri, QueryUnit},
};
use tera::Context;
pub(super) async fn hover(
server_rc: Rc<Mutex<Server>>,
root: SyntaxNode,
hovered_token: SyntaxToken,
) -> Result<Option<String>, LSPError> {
let server = server_rc.lock().await;
let iri = match hovered_token.parent_ancestors().find_map(Iri::cast) {
Some(value) => value,
None => return Ok(None),
};
let mut context = Context::new();
context.insert("entity", &iri.text());
let query_unit = QueryUnit::cast(root).ok_or(LSPError::new(
ErrorCode::InternalError,
"Hover is currently only supported for Query operations",
))?;
let backend =
resolve_backend_at_token(&server, &query_unit, &hovered_token).ok_or(LSPError::new(
ErrorCode::InternalError,
"Could not determine backend for hover location",
))?;
if let Some(label) = server.state.label_memory.get(&iri.text()) {
Ok(Some(label.clone()))
} else {
let converter = server
.state
.get_converter(&backend.name)
.ok_or(LSPError::new(
ErrorCode::InternalError,
"Could not get uri converter",
))?;
context.insert(
"prefixes",
&iri.prefixed_name()
.and_then(|prefixed_name| converter.find_by_prefix(&prefixed_name.prefix()).ok())
.map(|record| vec![(record.prefix.clone(), record.uri_prefix.clone())])
.unwrap_or_default(),
);
let query = server
.tools
.tera
.render(&format!("{}-hover", backend.name), &context)
.map_err(|err| {
tracing::error!("{}", err);
LSPError::new(ErrorCode::InternalError, &err.to_string())
})?;
let method = server.state.get_backend_request_method(&backend.name);
let sparql_response = execute_query(
server_rc.clone(),
backend.url,
query,
None,
None,
Some(server.settings.completion.timeout_ms),
method,
None,
0,
false,
)
.await
.map_err(|_err| LSPError::new(ErrorCode::InternalError, "hover query failed"))?;
match sparql_response
.expect("Non-lazy request should always return a result.")
.results
.bindings
.first()
{
Some(binding) => binding
.get("qls_label")
.ok_or(LSPError::new(
ErrorCode::InternalError,
"No RDF literal \"qls_entity\" in result",
))
.map(|rdf_term| Some(rdf_term.value().to_string())),
None => Ok(None),
}
}
}