Skip to main content

qlue_ls/server/
mod.rs

1//! Core language server implementation.
2//!
3//! This module contains the [`Server`] struct and the main message handling loop.
4//! The server is platform-agnostic and works with both native stdio and WASM streams.
5//!
6//! # Key Types
7//!
8//! - [`Server`]: Holds server state, settings, capabilities, and the message send function
9//! - [`handle_message`]: Entry point for processing incoming LSP messages
10//!
11//! # Architecture
12//!
13//! The server uses a closure-based approach for output: callers provide a `Fn(String)`
14//! that handles sending responses. This allows the same server code to work with
15//! stdio (native) or Web Streams (WASM).
16//!
17//! Message dispatch happens in [`message_handler`], which routes method names to
18//! specific handlers. The server is wrapped in `Rc<Mutex<>>` for async access.
19//!
20//! # Submodules
21//!
22//! - [`state`]: Document storage, parse tree cache, backend registry
23//! - [`capabilities`]: LSP capability negotiation
24//! - [`configuration`]: Settings from `qlue-ls.toml`/`qlue-ls.yml`
25//! - [`message_handler`]: Request/notification dispatch
26//! - [`lsp`]: Protocol types and JSON-RPC serialization
27//! - [`analysis`]: Semantic analysis (completions, hover, etc.)
28
29mod analysis;
30mod capabilities;
31mod common;
32pub(crate) mod configuration;
33mod lsp;
34mod sparql_operations;
35mod state;
36mod tools;
37
38pub(crate) mod message_handler;
39
40use capabilities::create_capabilities;
41use configuration::Settings;
42use futures::lock::Mutex;
43use log::{error, info};
44use lsp::{
45    ServerInfo,
46    errors::{ErrorCode, LSPError},
47    rpc::{RecoverId, ResponseMessage},
48};
49use message_handler::dispatch;
50use serde::Serialize;
51use state::ServerState;
52use std::{any::type_name, fmt::Debug, rc::Rc};
53use tools::Tools;
54use wasm_bindgen::prelude::wasm_bindgen;
55
56use crate::server::lsp::LspMessage;
57
58#[wasm_bindgen]
59pub struct Server {
60    pub(crate) state: ServerState,
61    pub(crate) settings: Settings,
62    pub(crate) capabilities: lsp::capabilities::ServerCapabilities,
63    pub(crate) client_capabilities: Option<lsp::capabilities::ClientCapabilities>,
64    pub(crate) server_info: ServerInfo,
65    tools: Tools,
66    send_message_closure: Box<dyn Fn(String)>,
67}
68
69impl Server {
70    pub fn new(write_function: impl Fn(String) + 'static) -> Server {
71        let version = env!("CARGO_PKG_VERSION");
72        info!("Started Language Server: Qlue-ls - version: {}", version);
73        Self {
74            state: ServerState::new(),
75            settings: Settings::new(),
76            capabilities: create_capabilities(),
77            client_capabilities: None,
78            server_info: ServerInfo {
79                name: "Qlue-ls".to_string(),
80                version: Some(version.to_string()),
81            },
82            tools: Tools::init(),
83            send_message_closure: Box::new(write_function),
84        }
85    }
86
87    pub(crate) fn bump_request_id(&mut self) -> u32 {
88        self.state.bump_request_id()
89    }
90
91    pub fn get_version(&self) -> String {
92        self.server_info
93            .version
94            .clone()
95            .unwrap_or("not-specified".to_string())
96    }
97
98    fn send_message<T>(&self, message: T) -> Result<(), LSPError>
99    where
100        T: Serialize + LspMessage + Debug,
101    {
102        let message_string = serde_json::to_string(&message).map_err(|error| {
103            LSPError::new(
104                ErrorCode::ParseError,
105                &format!(
106                    "Could not deserialize RPC-message \"{}\"\n\n{}",
107                    type_name::<T>(),
108                    error
109                ),
110            )
111        })?;
112        (self.send_message_closure)(message_string);
113        Ok(())
114    }
115
116    /// Shortens a raw URI into its CURIE (Compact URI) form and retrieves related metadata.
117    ///
118    /// This method takes a raw URI as input, attempts to find its associated prefix and URI prefix
119    /// from the `uri_converter`, and shorten the URI into its CURIE form. If successful, it
120    /// returns a tuple containing:
121    /// - The prefix associated with the URI.
122    /// - The URI prefix corresponding to the namespace of the URI.
123    /// - The CURIE representation of the URI.
124    ///
125    /// # Parameters
126    /// - `uri`: A string slice representing the raw URI to be shortened.
127    ///
128    /// # Returns
129    /// - `Some((prefix, uri_prefix, curie))` if the URI can be successfully compacted:
130    ///   - `prefix`: A `String` representing the prefix associated with the URI.
131    ///   - `uri_prefix`: A `String` representing the URI namespace prefix.
132    ///   - `curie`: A `String` representing the compact CURIE form of the URI.
133    /// - `None` if the URI cannot be found or shortened.
134    ///
135    /// # Errors
136    /// Returns `None` if:
137    /// - The `uri_converter` fails to find a record associated with the URI.
138    /// - The `uri_converter` fails to shorten the URI into a CURIE.
139    pub(crate) fn shorten_uri(
140        &self,
141        uri: &str,
142        backend_name: Option<&str>,
143    ) -> Option<(String, String, String)> {
144        let converter = backend_name
145            .and_then(|name| self.state.get_converter(name))
146            .or(self.state.get_default_converter())?;
147        let record = converter.find_by_uri(uri).ok()?;
148        let curie = converter.compress(uri).ok()?;
149        Some((record.prefix.clone(), record.uri_prefix.clone(), curie))
150    }
151}
152
153async fn handle_error(server_rc: Rc<Mutex<Server>>, message: &str, error: LSPError) {
154    log::error!(
155        "Error occurred while handling message:\n\"{}\"\n\n{:?}\n{}",
156        message,
157        error.code,
158        error.message
159    );
160    if let Ok(id) = serde_json::from_str::<RecoverId>(message).map(|msg| msg.id)
161        && let Err(error) = server_rc
162            .lock()
163            .await
164            .send_message(ResponseMessage::error(&id, error))
165    {
166        error!(
167            "CRITICAL: could not serialize error message (this very bad):\n{:?}",
168            error
169        )
170    }
171}
172
173pub async fn handle_message(server_rc: Rc<Mutex<Server>>, message: String) {
174    if let Err(err) = dispatch(server_rc.clone(), &message).await {
175        handle_error(server_rc.clone(), &message, err).await;
176    }
177}