mod analysis;
mod capabilities;
mod common;
pub(crate) mod configuration;
mod lsp;
mod sparql_operations;
mod state;
mod tools;
pub(crate) mod message_handler;
use capabilities::create_capabilities;
use configuration::Settings;
use futures::lock::Mutex;
use lsp::{
ServerInfo,
errors::{ErrorCode, LSPError},
rpc::{RecoverId, ResponseMessage},
};
use message_handler::dispatch;
use serde::Serialize;
use state::ServerState;
use std::{any::type_name, collections::HashMap, fmt::Debug, rc::Rc};
use tools::Tools;
use tracing::{error, info};
use wasm_bindgen::prelude::wasm_bindgen;
use crate::server::{configuration::CompletionTemplate, lsp::LspMessage};
#[wasm_bindgen]
pub struct Server {
pub(crate) state: ServerState,
pub(crate) settings: Settings,
pub(crate) capabilities: lsp::capabilities::server::ServerCapabilities,
pub(crate) client_capabilities: Option<lsp::capabilities::client::ClientCapabilities>,
pub(crate) server_info: ServerInfo,
tools: Tools,
send_message_closure: Box<dyn Fn(String)>,
}
impl Server {
pub fn new(write_function: impl Fn(String) + 'static) -> Server {
let version = env!("CARGO_PKG_VERSION");
info!("Started Language Server: Qlue-ls - version: {}", version);
Self {
state: ServerState::new(),
settings: Settings::new(),
capabilities: create_capabilities(),
client_capabilities: None,
server_info: ServerInfo {
name: "Qlue-ls".to_string(),
version: Some(version.to_string()),
},
tools: Tools::init(),
send_message_closure: Box::new(write_function),
}
}
pub(crate) fn bump_request_id(&mut self) -> u32 {
self.state.bump_request_id()
}
pub fn get_version(&self) -> String {
self.server_info
.version
.clone()
.unwrap_or("not-specified".to_string())
}
fn send_message<T>(&self, message: T) -> Result<(), LSPError>
where
T: Serialize + LspMessage + Debug,
{
let message_string = serde_json::to_string(&message).map_err(|error| {
LSPError::new(
ErrorCode::ParseError,
&format!(
"Could not deserialize RPC-message \"{}\"\n\n{}",
type_name::<T>(),
error
),
)
})?;
(self.send_message_closure)(message_string);
Ok(())
}
pub(crate) fn shorten_uri(
&self,
uri: &str,
backend_name: Option<&str>,
) -> Option<(String, String, String)> {
let converter = backend_name
.and_then(|name| self.state.get_converter(name))
.or(self.state.get_default_converter())?;
let record = converter.find_by_uri(uri).ok()?;
let curie = converter.compress(uri).ok()?;
Some((record.prefix.clone(), record.uri_prefix.clone(), curie))
}
pub(crate) fn load_templates(
&mut self,
backend_name: &str,
templates: HashMap<CompletionTemplate, String>,
) -> Result<(), LSPError> {
for (key, value) in templates {
self.tools
.tera
.add_raw_template(&format!("{}-{}", &backend_name, &key), &value)
.map_err(|err| {
tracing::error!("{}", err);
LSPError::new(
ErrorCode::InvalidParams,
&format!(
"Could not load template: {} of backend {}",
&key, &backend_name
),
)
})?;
}
Ok(())
}
}
async fn handle_error(server_rc: Rc<Mutex<Server>>, message: &str, error: LSPError) {
tracing::error!(
"Error occurred while handling message:\n\"{}\"\n\n{:?}\n{}",
message,
error.code,
error.message
);
if let Ok(id) = serde_json::from_str::<RecoverId>(message).map(|msg| msg.id)
&& let Err(error) = server_rc
.lock()
.await
.send_message(ResponseMessage::error(&id, error))
{
error!(
"CRITICAL: could not serialize error message (this very bad):\n{:?}",
error
)
}
}
pub async fn handle_message(server_rc: Rc<Mutex<Server>>, message: String) {
if let Err(err) = dispatch(server_rc.clone(), &message).await {
handle_error(server_rc.clone(), &message, err).await;
}
}