sync_ls/
server.rs

1//! A synchronous language server implementation.
2
3use core::fmt;
4use std::any::Any;
5use std::collections::HashMap;
6use std::path::{Path, PathBuf};
7use std::time::Instant;
8
9use serde::Serialize;
10use serde_json::{from_value, Value as JsonValue};
11
12#[cfg(feature = "dap")]
13mod dap_srv;
14
15#[cfg(feature = "lsp")]
16mod lsp_srv;
17#[cfg(feature = "lsp")]
18use lsp::{Notification, Request};
19
20use crate::msg::*;
21use crate::*;
22
23type Event = Box<dyn Any + Send>;
24type ImmutPath = Arc<Path>;
25
26type AsyncHandler<S, T, R> = fn(srv: &mut S, args: T) -> SchedulableResponse<R>;
27type RawHandler<S, T> = fn(srv: &mut S, req_id: RequestId, args: T) -> ScheduledResult;
28type BoxPureHandler<S, T> = Box<dyn Fn(&mut S, T) -> LspResult<()>>;
29type BoxHandler<S, T> = Box<dyn Fn(&mut S, &LspClient, RequestId, T) -> ScheduledResult>;
30type ExecuteCmdMap<S> = HashMap<&'static str, BoxHandler<S, Vec<JsonValue>>>;
31type RegularCmdMap<S> = HashMap<&'static str, BoxHandler<S, JsonValue>>;
32type NotifyCmdMap<S> = HashMap<&'static str, BoxPureHandler<S, JsonValue>>;
33type ResourceMap<S> = HashMap<ImmutPath, BoxHandler<S, Vec<JsonValue>>>;
34type MayInitBoxHandler<A, S, T> =
35    Box<dyn for<'a> Fn(ServiceState<'a, A, S>, &LspClient, T) -> anyhow::Result<()>>;
36type EventMap<A, S> = HashMap<core::any::TypeId, MayInitBoxHandler<A, S, Event>>;
37
38/// A trait that initializes the language server.
39pub trait Initializer {
40    /// The type of the initialization request.
41    type I: for<'de> serde::Deserialize<'de>;
42    /// The type of the service.
43    type S;
44
45    /// Handles the initialization request.
46    /// If the behind protocol is the standard LSP, the request is
47    /// `InitializeParams`.
48    fn initialize(self, req: Self::I) -> (Self::S, AnySchedulableResponse);
49}
50
51/// The builder pattern for the language server.
52pub struct LsBuilder<M, Args: Initializer> {
53    /// The extra initialization arguments.
54    pub args: Args,
55    /// The client surface for the implementing language server.
56    pub client: LspClient,
57    /// The event handlers.
58    pub events: EventMap<Args, Args::S>,
59    /// The command handlers.
60    pub command_handlers: ExecuteCmdMap<Args::S>,
61    /// The notification handlers.
62    pub notif_handlers: NotifyCmdMap<Args::S>,
63    /// The LSP request handlers.
64    pub req_handlers: RegularCmdMap<Args::S>,
65    /// The resource handlers.
66    pub resource_handlers: ResourceMap<Args::S>,
67    _marker: std::marker::PhantomData<M>,
68}
69
70/// The language server builder serving LSP.
71#[cfg(feature = "lsp")]
72pub type LspBuilder<Args> = LsBuilder<LspMessage, Args>;
73/// The language server builder serving DAP.
74#[cfg(feature = "dap")]
75pub type DapBuilder<Args> = LsBuilder<DapMessage, Args>;
76
77impl<M, Args: Initializer> LsBuilder<M, Args>
78where
79    Args::S: 'static,
80{
81    /// Creates a new language server builder.
82    pub fn new(args: Args, client: LspClient) -> Self {
83        Self {
84            args,
85            client,
86            events: EventMap::new(),
87            command_handlers: ExecuteCmdMap::new(),
88            notif_handlers: NotifyCmdMap::new(),
89            req_handlers: RegularCmdMap::new(),
90            resource_handlers: ResourceMap::new(),
91            _marker: std::marker::PhantomData,
92        }
93    }
94
95    /// Registers an event handler.
96    pub fn with_event<T: std::any::Any>(
97        mut self,
98        ins: &T,
99        handler: impl for<'a> Fn(ServiceState<'a, Args, Args::S>, T) -> anyhow::Result<()> + 'static,
100    ) -> Self {
101        self.events.insert(
102            ins.type_id(),
103            Box::new(move |s, _client, req| handler(s, *req.downcast().unwrap())),
104        );
105        self
106    }
107
108    /// Registers a raw resource handler.
109    pub fn with_resource_(
110        mut self,
111        path: ImmutPath,
112        handler: RawHandler<Args::S, Vec<JsonValue>>,
113    ) -> Self {
114        self.resource_handlers.insert(path, raw_to_boxed(handler));
115        self
116    }
117
118    /// Registers an async resource handler.
119    pub fn with_resource(
120        mut self,
121        path: &'static str,
122        handler: fn(&mut Args::S, Vec<JsonValue>) -> AnySchedulableResponse,
123    ) -> Self {
124        self.resource_handlers.insert(
125            Path::new(path).into(),
126            Box::new(move |s, client, req_id, req| client.schedule(req_id, handler(s, req))),
127        );
128        self
129    }
130
131    /// Builds the language server driver.
132    pub fn build(self) -> LsDriver<M, Args> {
133        LsDriver {
134            state: State::Uninitialized(Some(Box::new(self.args))),
135            events: self.events,
136            client: self.client,
137            commands: self.command_handlers,
138            notifications: self.notif_handlers,
139            requests: self.req_handlers,
140            resources: self.resource_handlers,
141            _marker: std::marker::PhantomData,
142        }
143    }
144}
145
146/// An enum to represent the state of the language server.
147pub enum ServiceState<'a, A, S> {
148    /// The service is uninitialized.
149    Uninitialized(Option<&'a mut A>),
150    /// The service is initializing.
151    Ready(&'a mut S),
152}
153
154impl<A, S> ServiceState<'_, A, S> {
155    /// Converts the state to an option holding the ready service.
156    pub fn ready(&mut self) -> Option<&mut S> {
157        match self {
158            ServiceState::Ready(s) => Some(s),
159            _ => None,
160        }
161    }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
165#[allow(dead_code)]
166enum State<Args, S> {
167    Uninitialized(Option<Box<Args>>),
168    Initializing(S),
169    Ready(S),
170    ShuttingDown,
171}
172
173impl<Args, S> State<Args, S> {
174    fn opt(&self) -> Option<&S> {
175        match &self {
176            State::Ready(s) => Some(s),
177            _ => None,
178        }
179    }
180
181    fn opt_mut(&mut self) -> Option<&mut S> {
182        match self {
183            State::Ready(s) => Some(s),
184            _ => None,
185        }
186    }
187}
188
189/// The language server driver.
190pub struct LsDriver<M, Args: Initializer> {
191    /// State to synchronize with the client.
192    state: State<Args, Args::S>,
193    /// The language server client.
194    pub client: LspClient,
195
196    // Handle maps
197    /// Events for dispatching.
198    pub events: EventMap<Args, Args::S>,
199    /// Extra commands provided with `textDocument/executeCommand`.
200    pub commands: ExecuteCmdMap<Args::S>,
201    /// Notifications for dispatching.
202    pub notifications: NotifyCmdMap<Args::S>,
203    /// Requests for dispatching.
204    pub requests: RegularCmdMap<Args::S>,
205    /// Resources for dispatching.
206    pub resources: ResourceMap<Args::S>,
207    _marker: std::marker::PhantomData<M>,
208}
209
210impl<M, Args: Initializer> LsDriver<M, Args> {
211    /// Gets the state of the language server.
212    pub fn state(&self) -> Option<&Args::S> {
213        self.state.opt()
214    }
215
216    /// Gets the mutable state of the language server.
217    pub fn state_mut(&mut self) -> Option<&mut Args::S> {
218        self.state.opt_mut()
219    }
220
221    /// Makes the language server ready.
222    pub fn ready(&mut self, params: Args::I) -> AnySchedulableResponse {
223        let args = match &mut self.state {
224            State::Uninitialized(args) => args,
225            _ => return just_result(Err(invalid_request("server is already initialized"))),
226        };
227
228        let args = args.take().expect("already initialized");
229        let (s, res) = args.initialize(params);
230        self.state = State::Ready(s);
231
232        res
233    }
234
235    /// Get static resources with help of tinymist service, for example, a
236    /// static help pages for some typst function.
237    pub fn get_resources(&mut self, req_id: RequestId, args: Vec<JsonValue>) -> ScheduledResult {
238        let s = self.state.opt_mut().ok_or_else(not_initialized)?;
239
240        let path =
241            from_value::<PathBuf>(args[0].clone()).map_err(|e| invalid_params(e.to_string()))?;
242
243        let Some(handler) = self.resources.get(path.as_path()) else {
244            log::error!("asked for unknown resource: {path:?}");
245            return Err(method_not_found());
246        };
247
248        // Note our redirection will keep the first path argument in the args vec.
249        handler(s, &self.client, req_id, args)
250    }
251}
252
253fn from_json<T: serde::de::DeserializeOwned>(json: JsonValue) -> LspResult<T> {
254    serde_json::from_value(json).map_err(invalid_request)
255}
256
257fn raw_to_boxed<S: 'static, T: 'static>(handler: RawHandler<S, T>) -> BoxHandler<S, T> {
258    Box::new(move |s, _client, req_id, req| handler(s, req_id, req))
259}
260
261fn resp_err(code: ErrorCode, msg: impl fmt::Display) -> ResponseError {
262    ResponseError {
263        code: code as i32,
264        message: msg.to_string(),
265        data: None,
266    }
267}
268
269/// Creates an invalid params error.
270pub fn invalid_params(msg: impl fmt::Display) -> ResponseError {
271    resp_err(ErrorCode::InvalidParams, msg)
272}
273
274/// Creates an internal error.
275pub fn internal_error(msg: impl fmt::Display) -> ResponseError {
276    resp_err(ErrorCode::InternalError, msg)
277}
278
279/// Creates a not initialized error.
280pub fn not_initialized() -> ResponseError {
281    resp_err(ErrorCode::ServerNotInitialized, "not initialized yet")
282}
283
284/// Creates a method not found error.
285pub fn method_not_found() -> ResponseError {
286    resp_err(ErrorCode::MethodNotFound, "method not found")
287}
288
289/// Creates an invalid request error.
290pub fn invalid_request(msg: impl fmt::Display) -> ResponseError {
291    resp_err(ErrorCode::InvalidRequest, msg)
292}