dev: improve code quality of sync-lsp (#632)

This commit is contained in:
Myriad-Dreamin 2024-10-06 22:04:35 +08:00 committed by GitHub
parent 80c4bc8491
commit be9bf5ec66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 225 additions and 263 deletions

View file

@ -1,3 +1,4 @@
use core::fmt;
use std::any::Any; use std::any::Any;
use std::path::Path; use std::path::Path;
use std::pin::Pin; use std::pin::Pin;
@ -5,50 +6,54 @@ use std::sync::{Arc, Weak};
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
use futures::future::MaybeDone; use futures::future::MaybeDone;
use lsp_server::{ErrorCode, Message, Notification, Request, RequestId, Response, ResponseError}; use lsp_server::{ErrorCode, Message, Notification, Request, RequestId, Response};
use lsp_types::notification::{Notification as Notif, PublishDiagnostics}; use lsp_types::{notification::Notification as Notif, request::Request as Req, *};
use lsp_types::request::{self, RegisterCapability, Request as Req, UnregisterCapability};
use lsp_types::*;
use parking_lot::Mutex; use parking_lot::Mutex;
use reflexo::{time::Instant, ImmutPath}; use reflexo::{time::Instant, ImmutPath};
use serde::{de::DeserializeOwned, Serialize}; use serde::Serialize;
use serde_json::{from_value, Value as JsonValue}; use serde_json::{from_value, Value as JsonValue};
use tinymist_query::CompilerQueryResponse; use tinymist_query::CompilerQueryResponse;
pub mod req_queue; pub mod req_queue;
pub mod transport; pub mod transport;
pub type ReqHandler<S> = Box<dyn for<'a> FnOnce(&'a mut S, lsp_server::Response) + Send + Sync>; /// The common error type for the language server.
type ReqQueue<S> = req_queue::ReqQueue<(String, Instant), ReqHandler<S>>; pub use lsp_server::ResponseError;
/// The common result type for the language server.
pub type LspResult<Res> = Result<Res, ResponseError>; pub type LspResult<T> = Result<T, ResponseError>;
/// A future that may be done in place or not.
/// Returns Ok(Some()) -> Already responded
/// Returns Ok(None) -> Need to respond none
/// Returns Err(..) -> Need to respond error
pub type ScheduledResult = LspResult<Option<()>>;
pub type ResponseFuture<T> = MaybeDone<Pin<Box<dyn std::future::Future<Output = T> + Send>>>; pub type ResponseFuture<T> = MaybeDone<Pin<Box<dyn std::future::Future<Output = T> + Send>>>;
/// A future that may be rejected before actual started.
pub type LspResponseFuture<T> = LspResult<ResponseFuture<T>>; pub type LspResponseFuture<T> = LspResult<ResponseFuture<T>>;
/// A future that could be rejected by common error in `LspResponseFuture`.
pub type SchedulableResponse<T> = LspResponseFuture<LspResult<T>>;
/// The common future type for the language server.
pub type AnySchedulableResponse = SchedulableResponse<JsonValue>;
/// The result of a scheduled response which could be finally catched by
/// `schedule_tail`.
/// - Returns Ok(Some()) -> Already responded
/// - Returns Ok(None) -> Need to respond none
/// - Returns Err(..) -> Need to respond error
pub type ScheduledResult = LspResult<Option<()>>;
/// The future type for a lsp query.
pub type QueryFuture = anyhow::Result<ResponseFuture<anyhow::Result<CompilerQueryResponse>>>; pub type QueryFuture = anyhow::Result<ResponseFuture<anyhow::Result<CompilerQueryResponse>>>;
pub type SchedulableResponse<T> = LspResponseFuture<LspResult<T>>; /// A helper function to create a `LspResponseFuture`
pub type AnySchedulableResponse = SchedulableResponse<JsonValue>;
pub fn just_ok<T, E>(res: T) -> Result<ResponseFuture<Result<T, E>>, E> { pub fn just_ok<T, E>(res: T) -> Result<ResponseFuture<Result<T, E>>, E> {
Ok(futures::future::MaybeDone::Done(Ok(res))) Ok(futures::future::MaybeDone::Done(Ok(res)))
} }
/// A helper function to create a `LspResponseFuture`
pub fn just_result<T, E>(res: Result<T, E>) -> Result<ResponseFuture<Result<T, E>>, E> { pub fn just_result<T, E>(res: Result<T, E>) -> Result<ResponseFuture<Result<T, E>>, E> {
Ok(futures::future::MaybeDone::Done(res)) Ok(futures::future::MaybeDone::Done(res))
} }
/// A helper function to create a `LspResponseFuture`
pub fn just_future<T, E>( pub fn just_future<T, E>(
fut: impl std::future::Future<Output = Result<T, E>> + Send + 'static, fut: impl std::future::Future<Output = Result<T, E>> + Send + 'static,
) -> Result<ResponseFuture<Result<T, E>>, E> { ) -> Result<ResponseFuture<Result<T, E>>, E> {
Ok(futures::future::MaybeDone::Future(Box::pin(fut))) Ok(futures::future::MaybeDone::Future(Box::pin(fut)))
} }
/// Converts a `ScheduledResult` to a `SchedulableResponse`.
macro_rules! reschedule { macro_rules! reschedule {
($expr:expr) => { ($expr:expr) => {
match $expr { match $expr {
@ -63,11 +68,41 @@ macro_rules! reschedule {
type AnyCaster<S> = Arc<dyn Fn(&mut dyn Any) -> &mut S + Send + Sync>; type AnyCaster<S> = Arc<dyn Fn(&mut dyn Any) -> &mut S + Send + Sync>;
/// A Lsp client with typed service `S`.
pub struct TypedLspClient<S> { pub struct TypedLspClient<S> {
client: LspClient, client: LspClient,
caster: AnyCaster<S>, caster: AnyCaster<S>,
} }
impl<S> TypedLspClient<S> {
pub fn to_untyped(self) -> LspClient {
self.client
}
}
impl<S: 'static> TypedLspClient<S> {
/// Casts the service to another type.
pub fn cast<T: 'static>(&self, f: fn(&mut S) -> &mut T) -> TypedLspClient<T> {
let caster = self.caster.clone();
TypedLspClient {
client: self.client.clone(),
caster: Arc::new(move |s| f(caster(s))),
}
}
/// Sends a request to the client and registers a handler handled by the
/// service `S`.
pub fn send_request<R: Req>(
&self,
params: R::Params,
handler: impl FnOnce(&mut S, lsp_server::Response) + Send + Sync + 'static,
) {
let caster = self.caster.clone();
self.client
.send_request_::<R>(params, move |s, resp| handler(caster(s), resp))
}
}
impl<S> Clone for TypedLspClient<S> { impl<S> Clone for TypedLspClient<S> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
@ -85,32 +120,6 @@ impl<S> std::ops::Deref for TypedLspClient<S> {
} }
} }
impl<S> TypedLspClient<S> {
pub fn to_untyped(self) -> LspClient {
self.client
}
}
impl<S: 'static> TypedLspClient<S> {
pub fn cast<T: 'static>(&self, f: fn(&mut S) -> &mut T) -> TypedLspClient<T> {
let caster = self.caster.clone();
TypedLspClient {
client: self.client.clone(),
caster: Arc::new(move |s| f(caster(s))),
}
}
pub fn send_request<R: lsp_types::request::Request>(
&self,
params: R::Params,
handler: impl FnOnce(&mut S, lsp_server::Response) + Send + Sync + 'static,
) {
let caster = self.caster.clone();
self.client
.send_request_::<R>(params, move |s, resp| handler(caster(s), resp))
}
}
/// The root of the language server host. /// The root of the language server host.
/// Will close connection when dropped. /// Will close connection when dropped.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -137,6 +146,9 @@ impl LspClientRoot {
} }
} }
type ReqHandler = Box<dyn for<'a> FnOnce(&'a mut dyn Any, lsp_server::Response) + Send + Sync>;
type ReqQueue = req_queue::ReqQueue<(String, Instant), ReqHandler>;
/// The host for the language server, or known as the LSP client. /// The host for the language server, or known as the LSP client.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LspClient { pub struct LspClient {
@ -144,22 +156,25 @@ pub struct LspClient {
pub handle: tokio::runtime::Handle, pub handle: tokio::runtime::Handle,
sender: Weak<crossbeam_channel::Sender<Message>>, sender: Weak<crossbeam_channel::Sender<Message>>,
req_queue: Arc<Mutex<ReqQueue<dyn Any>>>, req_queue: Arc<Mutex<ReqQueue>>,
} }
impl LspClient { impl LspClient {
/// converts the client to a typed client.
pub fn to_typed<S: Any>(&self) -> TypedLspClient<S> { pub fn to_typed<S: Any>(&self) -> TypedLspClient<S> {
TypedLspClient { TypedLspClient {
client: self.clone(), client: self.clone(),
caster: Arc::new(|s| s.downcast_mut().unwrap()), caster: Arc::new(|s| s.downcast_mut().expect("invalid cast")),
} }
} }
/// Checks if there are pending requests.
pub fn has_pending_requests(&self) -> bool { pub fn has_pending_requests(&self) -> bool {
self.req_queue.lock().incoming.has_pending() self.req_queue.lock().incoming.has_pending()
} }
pub fn send_request_<R: lsp_types::request::Request>( /// Sends a request to the client and registers a handler.
pub fn send_request_<R: Req>(
&self, &self,
params: R::Params, params: R::Params,
handler: impl FnOnce(&mut dyn Any, lsp_server::Response) + Send + Sync + 'static, handler: impl FnOnce(&mut dyn Any, lsp_server::Response) + Send + Sync + 'static,
@ -178,6 +193,7 @@ impl LspClient {
log::warn!("failed to send request: {res:?}"); log::warn!("failed to send request: {res:?}");
} }
/// Completes an server2client request in the request queue.
pub fn complete_request<S: Any>(&self, service: &mut S, response: lsp_server::Response) { pub fn complete_request<S: Any>(&self, service: &mut S, response: lsp_server::Response) {
let mut req_queue = self.req_queue.lock(); let mut req_queue = self.req_queue.lock();
let Some(handler) = req_queue.outgoing.complete(response.id.clone()) else { let Some(handler) = req_queue.outgoing.complete(response.id.clone()) else {
@ -188,34 +204,16 @@ impl LspClient {
handler(service, response) handler(service, response)
} }
pub fn send_notification_(&self, notif: lsp_server::Notification) { /// Registers an client2server request in the request queue.
let Some(sender) = self.sender.upgrade() else { pub fn register_request(&self, request: &lsp_server::Request, received_at: Instant) {
log::warn!("failed to send request: connection closed");
return;
};
let Err(res) = sender.send(notif.into()) else {
return;
};
log::warn!("failed to send notification: {res:?}");
}
pub fn send_notification<N: lsp_types::notification::Notification>(&self, params: N::Params) {
self.send_notification_(lsp_server::Notification::new(N::METHOD.to_owned(), params));
}
pub fn register_request(&self, request: &lsp_server::Request, request_received: Instant) {
let mut req_queue = self.req_queue.lock(); let mut req_queue = self.req_queue.lock();
log::info!( let method = request.method.clone();
"handling {} - ({}) at {request_received:0.2?}", let req_id = request.id.clone();
request.method, log::info!("handling {method} - ({req_id}) at {received_at:0.2?}");
request.id, req_queue.incoming.register(req_id, (method, received_at));
);
req_queue.incoming.register(
request.id.clone(),
(request.method.clone(), request_received),
);
} }
/// Completes an client2server request in the request queue.
pub fn respond(&self, response: lsp_server::Response) { pub fn respond(&self, response: lsp_server::Response) {
let mut req_queue = self.req_queue.lock(); let mut req_queue = self.req_queue.lock();
if let Some((method, start)) = req_queue.incoming.complete(response.id.clone()) { if let Some((method, start)) = req_queue.incoming.complete(response.id.clone()) {
@ -233,49 +231,26 @@ impl LspClient {
} }
} }
pub fn publish_diagnostics( /// Sends an untyped notification to the client.
&self, pub fn send_notification_(&self, notif: lsp_server::Notification) {
uri: Url, let Some(sender) = self.sender.upgrade() else {
diagnostics: Vec<Diagnostic>, log::warn!("failed to send notification: connection closed");
version: Option<i32>, return;
) { };
self.send_notification::<PublishDiagnostics>(PublishDiagnosticsParams { let Err(res) = sender.send(notif.into()) else {
uri, return;
diagnostics, };
version, log::warn!("failed to send notification: {res:?}");
});
} }
// todo: handle error /// Sends a typed notification to the client.
pub fn register_capability(&self, registrations: Vec<Registration>) -> anyhow::Result<()> { pub fn send_notification<N: Notif>(&self, params: N::Params) {
self.send_request_::<RegisterCapability>( self.send_notification_(lsp_server::Notification::new(N::METHOD.to_owned(), params));
RegistrationParams { registrations },
|_, resp| {
if let Some(err) = resp.error {
log::error!("failed to register capability: {err:?}");
}
},
);
Ok(())
}
pub fn unregister_capability(
&self,
unregisterations: Vec<Unregistration>,
) -> anyhow::Result<()> {
self.send_request_::<UnregisterCapability>(
UnregistrationParams { unregisterations },
|_, resp| {
if let Some(err) = resp.error {
log::error!("failed to unregister capability: {err:?}");
}
},
);
Ok(())
} }
} }
impl LspClient { impl LspClient {
/// Schedules a query from the client.
pub fn schedule_query(&self, req_id: RequestId, query_fut: QueryFuture) -> ScheduledResult { pub fn schedule_query(&self, req_id: RequestId, query_fut: QueryFuture) -> ScheduledResult {
let fut = query_fut.map_err(|e| internal_error(e.to_string()))?; let fut = query_fut.map_err(|e| internal_error(e.to_string()))?;
let fut: AnySchedulableResponse = Ok(match fut { let fut: AnySchedulableResponse = Ok(match fut {
@ -293,6 +268,7 @@ impl LspClient {
self.schedule(req_id, fut) self.schedule(req_id, fut)
} }
/// Schedules a request from the client.
pub fn schedule<T: Serialize + 'static>( pub fn schedule<T: Serialize + 'static>(
&self, &self,
req_id: RequestId, req_id: RequestId,
@ -320,22 +296,26 @@ impl LspClient {
Ok(Some(())) Ok(Some(()))
} }
/// Catch the early rejected requests.
fn schedule_tail(&self, req_id: RequestId, resp: ScheduledResult) { fn schedule_tail(&self, req_id: RequestId, resp: ScheduledResult) {
match resp { match resp {
// Already responded
Ok(Some(())) => {} Ok(Some(())) => {}
// The requests that doesn't start.
_ => self.respond(result_to_response(req_id, resp)), _ => self.respond(result_to_response(req_id, resp)),
} }
} }
} }
type LspRawPureHandler<S, T> = fn(srv: &mut S, args: T) -> LspResult<()>; type AsyncHandler<S, T, R> = fn(srv: &mut S, args: T) -> SchedulableResponse<R>;
type LspRawHandler<S, T> = fn(srv: &mut S, req_id: RequestId, args: T) -> ScheduledResult; type PureHandler<S, T> = fn(srv: &mut S, args: T) -> LspResult<()>;
type LspBoxPureHandler<S, T> = Box<dyn Fn(&mut S, T) -> LspResult<()>>; type RawHandler<S, T> = fn(srv: &mut S, req_id: RequestId, args: T) -> ScheduledResult;
type LspBoxHandler<S, T> = Box<dyn Fn(&mut S, &LspClient, RequestId, T) -> ScheduledResult>; type BoxPureHandler<S, T> = Box<dyn Fn(&mut S, T) -> LspResult<()>>;
type ExecuteCmdMap<S> = HashMap<&'static str, LspBoxHandler<S, Vec<JsonValue>>>; type BoxHandler<S, T> = Box<dyn Fn(&mut S, &LspClient, RequestId, T) -> ScheduledResult>;
type RegularCmdMap<S> = HashMap<&'static str, LspBoxHandler<S, JsonValue>>; type ExecuteCmdMap<S> = HashMap<&'static str, BoxHandler<S, Vec<JsonValue>>>;
type NotifyCmdMap<S> = HashMap<&'static str, LspBoxPureHandler<S, JsonValue>>; type RegularCmdMap<S> = HashMap<&'static str, BoxHandler<S, JsonValue>>;
type ResourceMap<S> = HashMap<ImmutPath, LspBoxHandler<S, Vec<JsonValue>>>; type NotifyCmdMap<S> = HashMap<&'static str, BoxPureHandler<S, JsonValue>>;
type ResourceMap<S> = HashMap<ImmutPath, BoxHandler<S, Vec<JsonValue>>>;
pub trait Initializer { pub trait Initializer {
type I: for<'de> serde::Deserialize<'de>; type I: for<'de> serde::Deserialize<'de>;
@ -371,19 +351,16 @@ where
pub fn with_command_( pub fn with_command_(
mut self, mut self,
cmd: &'static str, cmd: &'static str,
handler: LspRawHandler<Args::S, Vec<JsonValue>>, handler: RawHandler<Args::S, Vec<JsonValue>>,
) -> Self { ) -> Self {
self.exec_cmds.insert( self.exec_cmds.insert(cmd, raw_to_boxed(handler));
cmd,
Box::new(move |s, _client, req_id, req| handler(s, req_id, req)),
);
self self
} }
pub fn with_command<T: Serialize + 'static>( pub fn with_command<R: Serialize + 'static>(
mut self, mut self,
cmd: &'static str, cmd: &'static str,
handler: fn(&mut Args::S, Vec<JsonValue>) -> SchedulableResponse<T>, handler: AsyncHandler<Args::S, Vec<JsonValue>, R>,
) -> Self { ) -> Self {
self.exec_cmds.insert( self.exec_cmds.insert(
cmd, cmd,
@ -394,31 +371,22 @@ where
pub fn with_notification_<R: Notif>( pub fn with_notification_<R: Notif>(
mut self, mut self,
handler: LspRawPureHandler<Args::S, JsonValue>, handler: PureHandler<Args::S, JsonValue>,
) -> Self { ) -> Self {
self.notify_cmds.insert(R::METHOD, Box::new(handler)); self.notify_cmds.insert(R::METHOD, Box::new(handler));
self self
} }
pub fn with_notification<R: Notif>( pub fn with_notification<R: Notif>(mut self, handler: PureHandler<Args::S, R::Params>) -> Self {
mut self,
handler: LspRawPureHandler<Args::S, R::Params>,
) -> Self {
self.notify_cmds.insert( self.notify_cmds.insert(
R::METHOD, R::METHOD,
Box::new(move |s, req| { Box::new(move |s, req| handler(s, from_json(req)?)),
let req = serde_json::from_value::<R::Params>(req).unwrap(); // todo: soft unwrap
handler(s, req)
}),
); );
self self
} }
pub fn with_raw_request<R: Req>(mut self, handler: LspRawHandler<Args::S, JsonValue>) -> Self { pub fn with_raw_request<R: Req>(mut self, handler: RawHandler<Args::S, JsonValue>) -> Self {
self.regular_cmds.insert( self.regular_cmds.insert(R::METHOD, raw_to_boxed(handler));
R::METHOD,
Box::new(move |s, _client, req_id, req| handler(s, req_id, req)),
);
self self
} }
@ -429,24 +397,19 @@ where
) -> Self { ) -> Self {
self.regular_cmds.insert( self.regular_cmds.insert(
R::METHOD, R::METHOD,
Box::new(move |s, _client, req_id, req| { Box::new(move |s, _client, req_id, req| handler(s, req_id, from_json(req)?)),
let req = serde_json::from_value::<R::Params>(req).unwrap(); // todo: soft unwrap
handler(s, req_id, req)
}),
); );
self self
} }
pub fn with_request<R: Req>( pub fn with_request<R: Req>(
mut self, mut self,
handler: fn(&mut Args::S, R::Params) -> SchedulableResponse<R::Result>, handler: AsyncHandler<Args::S, R::Params, R::Result>,
) -> Self { ) -> Self {
self.regular_cmds.insert( self.regular_cmds.insert(
R::METHOD, R::METHOD,
Box::new(move |s, client, req_id, req| { Box::new(move |s, client, req_id, req| {
let req = serde_json::from_value::<R::Params>(req).unwrap(); // todo: soft unwrap client.schedule(req_id, handler(s, from_json(req)?))
let res = handler(s, req);
client.schedule(req_id, res)
}), }),
); );
self self
@ -455,12 +418,9 @@ where
pub fn with_resource_( pub fn with_resource_(
mut self, mut self,
path: ImmutPath, path: ImmutPath,
handler: LspRawHandler<Args::S, Vec<JsonValue>>, handler: RawHandler<Args::S, Vec<JsonValue>>,
) -> Self { ) -> Self {
self.resource_routes.insert( self.resource_routes.insert(path, raw_to_boxed(handler));
path,
Box::new(move |s, _client, req_id, req| handler(s, req_id, req)),
);
self self
} }
@ -496,6 +456,22 @@ enum State<Args, S> {
ShuttingDown, ShuttingDown,
} }
impl<Args, S> State<Args, S> {
fn opt(&self) -> Option<&S> {
match &self {
State::Ready(s) => Some(s),
_ => None,
}
}
fn opt_mut(&mut self) -> Option<&mut S> {
match self {
State::Ready(s) => Some(s),
_ => None,
}
}
}
pub struct LspDriver<Args: Initializer> { pub struct LspDriver<Args: Initializer> {
/// State to synchronize with the client. /// State to synchronize with the client.
state: State<Args, Args::S>, state: State<Args, Args::S>,
@ -515,31 +491,21 @@ pub struct LspDriver<Args: Initializer> {
impl<Args: Initializer> LspDriver<Args> { impl<Args: Initializer> LspDriver<Args> {
pub fn state(&self) -> Option<&Args::S> { pub fn state(&self) -> Option<&Args::S> {
match &self.state { self.state.opt()
State::Ready(s) => Some(s),
_ => None,
}
} }
pub fn state_mut(&mut self) -> Option<&mut Args::S> { pub fn state_mut(&mut self) -> Option<&mut Args::S> {
match &mut self.state { self.state.opt_mut()
State::Ready(s) => Some(s),
_ => None,
}
} }
pub fn ready(&mut self, params: Args::I) -> AnySchedulableResponse { pub fn ready(&mut self, params: Args::I) -> AnySchedulableResponse {
let args = match &mut self.state { let args = match &mut self.state {
State::Uninitialized(args) => args, State::Uninitialized(args) => args,
_ => { _ => return just_result(Err(invalid_request("server is already initialized"))),
return just_result(Err(resp_err(
ErrorCode::InvalidRequest,
"Server is already initialized",
)))
}
}; };
let (s, res) = args.take().unwrap().initialize(params); let args = args.take().expect("already initialized");
let (s, res) = args.initialize(params);
self.state = State::Ready(s); self.state = State::Ready(s);
res res
@ -594,7 +560,7 @@ where
// } // }
while let Ok(msg) = inbox.recv() { while let Ok(msg) = inbox.recv() {
const EXIT_METHOD: &str = lsp_types::notification::Exit::METHOD; const EXIT_METHOD: &str = notification::Exit::METHOD;
let loop_start = Instant::now(); let loop_start = Instant::now();
match msg { match msg {
Message::Request(req) => self.on_request(loop_start, req), Message::Request(req) => self.on_request(loop_start, req),
@ -632,33 +598,38 @@ where
let resp = match (&mut self.state, &*req.method) { let resp = match (&mut self.state, &*req.method) {
(State::Uninitialized(args), request::Initialize::METHOD) => { (State::Uninitialized(args), request::Initialize::METHOD) => {
// todo: what will happen if the request cannot be deserialized? // todo: what will happen if the request cannot be deserialized?
let params = serde_json::from_value::<Args::I>(req.params).unwrap(); let params = serde_json::from_value::<Args::I>(req.params);
let (s, res) = args.take().unwrap().initialize(params); match params {
self.state = State::Initializing(s); Ok(params) => {
res let args = args.take().expect("already initialized");
let (s, res) = args.initialize(params);
self.state = State::Initializing(s);
res
}
Err(e) => just_result(Err(invalid_request(e))),
}
}
(State::Uninitialized(..) | State::Initializing(..), _) => {
just_result(Err(not_initialized()))
}
(_, request::Initialize::METHOD) => {
just_result(Err(invalid_request("server is already initialized")))
} }
(State::Uninitialized(..) | State::Initializing(..), _) => just_result(Err(resp_err(
ErrorCode::ServerNotInitialized,
"Server is not initialized yet",
))),
(_, request::Initialize::METHOD) => just_result(Err(resp_err(
ErrorCode::InvalidRequest,
"Server is already initialized",
))),
// todo: generalize this // todo: generalize this
(State::Ready(..), request::ExecuteCommand::METHOD) => { (State::Ready(..), request::ExecuteCommand::METHOD) => {
reschedule!(self.on_execute_command(req)) reschedule!(self.on_execute_command(req))
} }
(State::Ready(s), _) => { (State::Ready(s), _) => {
let is_shutdown = req.method == request::Shutdown::METHOD; let method = req.method.as_str();
let is_shutdown = method == request::Shutdown::METHOD;
let Some(handler) = self.requests.get(req.method.as_str()) else { let Some(handler) = self.requests.get(method) else {
log::warn!("unhandled request: {}", req.method); log::warn!("unhandled request: {method}");
return; return;
}; };
let result = handler(s, &self.client, req.id.clone(), req.params); let result = handler(s, &self.client, req_id.clone(), req.params);
self.client.schedule_tail(req.id, result); self.client.schedule_tail(req_id, result);
if is_shutdown { if is_shutdown {
self.state = State::ShuttingDown; self.state = State::ShuttingDown;
@ -666,10 +637,9 @@ where
return; return;
} }
(State::ShuttingDown, _) => just_result(Err(resp_err( (State::ShuttingDown, _) => {
ErrorCode::InvalidRequest, just_result(Err(invalid_request("server is shutting down")))
"Server is shutting down", }
))),
}; };
let result = self.client.schedule(req_id.clone(), resp); let result = self.client.schedule(req_id.clone(), resp);
@ -678,15 +648,7 @@ where
/// The entry point for the `workspace/executeCommand` request. /// The entry point for the `workspace/executeCommand` request.
fn on_execute_command(&mut self, req: Request) -> ScheduledResult { fn on_execute_command(&mut self, req: Request) -> ScheduledResult {
let s = match &mut self.state { let s = self.state.opt_mut().ok_or_else(not_initialized)?;
State::Ready(s) => s,
_ => {
return Err(resp_err(
ErrorCode::ServerNotInitialized,
"Server is not ready",
))
}
};
let params = from_value::<ExecuteCommandParams>(req.params) let params = from_value::<ExecuteCommandParams>(req.params)
.map_err(|e| invalid_params(e.to_string()))?; .map_err(|e| invalid_params(e.to_string()))?;
@ -710,15 +672,7 @@ where
/// Get static resources with help of tinymist service, for example, a /// Get static resources with help of tinymist service, for example, a
/// static help pages for some typst function. /// static help pages for some typst function.
pub fn get_resources(&mut self, req_id: RequestId, args: Vec<JsonValue>) -> ScheduledResult { pub fn get_resources(&mut self, req_id: RequestId, args: Vec<JsonValue>) -> ScheduledResult {
let s = match &mut self.state { let s = self.state.opt_mut().ok_or_else(not_initialized)?;
State::Ready(s) => s,
_ => {
return Err(resp_err(
ErrorCode::ServerNotInitialized,
"Server is not ready",
))
}
};
let path = let path =
from_value::<PathBuf>(args[0].clone()).map_err(|e| invalid_params(e.to_string()))?; from_value::<PathBuf>(args[0].clone()).map_err(|e| invalid_params(e.to_string()))?;
@ -748,19 +702,11 @@ where
let result = handler(s, not.params); let result = handler(s, not.params);
let request_duration = request_received.elapsed(); let request_duration = request_received.elapsed();
let method = &not.method;
if let Err(err) = result { if let Err(err) = result {
log::error!( log::error!("notifing {method} failed in {request_duration:0.2?}: {err:?}");
"notifing {} failed in {:0.2?}: {:?}",
not.method,
request_duration,
err
);
} else { } else {
log::info!( log::info!("notifing {method} succeeded in {request_duration:0.2?}");
"notifing {} succeeded in {:0.2?}",
not.method,
request_duration
);
} }
Ok(()) Ok(())
@ -802,58 +748,48 @@ where
} }
} }
fn resp_err(code: ErrorCode, msg: impl Into<String>) -> ResponseError { fn from_json<T: serde::de::DeserializeOwned>(json: JsonValue) -> LspResult<T> {
serde_json::from_value(json).map_err(invalid_request)
}
fn raw_to_boxed<S: 'static, T: 'static>(handler: RawHandler<S, T>) -> BoxHandler<S, T> {
Box::new(move |s, _client, req_id, req| handler(s, req_id, req))
}
fn resp_err(code: ErrorCode, msg: impl fmt::Display) -> ResponseError {
ResponseError { ResponseError {
code: code as i32, code: code as i32,
message: msg.into(), message: msg.to_string(),
data: None, data: None,
} }
} }
pub fn from_json<T: DeserializeOwned>( pub fn invalid_params(msg: impl fmt::Display) -> ResponseError {
what: &'static str, resp_err(ErrorCode::InvalidParams, msg)
json: &serde_json::Value,
) -> anyhow::Result<T> {
serde_json::from_value(json.clone())
.map_err(|e| anyhow::anyhow!("Failed to deserialize {what}: {e}; {json}"))
} }
pub fn invalid_params(msg: impl Into<String>) -> ResponseError { pub fn internal_error(msg: impl fmt::Display) -> ResponseError {
ResponseError { resp_err(ErrorCode::InternalError, msg)
code: ErrorCode::InvalidParams as i32,
message: msg.into(),
data: None,
}
} }
pub fn internal_error(msg: impl Into<String>) -> ResponseError { pub fn not_initialized() -> ResponseError {
ResponseError { resp_err(ErrorCode::ServerNotInitialized, "not initialized yet")
code: ErrorCode::InternalError as i32,
message: msg.into(),
data: None,
}
} }
pub fn method_not_found() -> ResponseError { pub fn method_not_found() -> ResponseError {
ResponseError { resp_err(ErrorCode::MethodNotFound, "method not found")
code: ErrorCode::MethodNotFound as i32, }
message: "Method not found".to_string(),
data: None, pub fn invalid_request(msg: impl fmt::Display) -> ResponseError {
} resp_err(ErrorCode::InvalidRequest, msg)
} }
pub fn result_to_response<T: Serialize>( pub fn result_to_response<T: Serialize>(
id: RequestId, id: RequestId,
result: Result<T, ResponseError>, result: Result<T, ResponseError>,
) -> Response { ) -> Response {
match result { match result.and_then(|t| serde_json::to_value(t).map_err(internal_error)) {
Ok(resp) => match serde_json::to_value(resp) { Ok(resp) => Response::new_ok(id, resp),
Ok(resp) => Response::new_ok(id, resp),
Err(e) => {
let e = internal_error(e.to_string());
Response::new_err(id, e.code, e.message)
}
},
Err(e) => Response::new_err(id, e.code, e.message), Err(e) => Response::new_err(id, e.code, e.message),
} }
} }

View file

@ -4,7 +4,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use log::info; use log::info;
use lsp_types::Url; use lsp_types::{notification::PublishDiagnostics, PublishDiagnosticsParams, Url};
use tinymist_query::{DiagnosticsMap, LspDiagnostic}; use tinymist_query::{DiagnosticsMap, LspDiagnostic};
use tokio::sync::mpsc; use tokio::sync::mpsc;
@ -136,7 +136,12 @@ impl EditorActor {
None => path_diags.remove(group), None => path_diags.remove(group),
}; };
self.client.publish_diagnostics(url, to_publish, None) self.client
.send_notification::<PublishDiagnostics>(PublishDiagnosticsParams {
uri: url,
diagnostics: to_publish,
version: None,
});
} }
} }
// Notification // Notification

View file

@ -17,6 +17,7 @@ use reflexo_typst::{
vfs::notify::{FileChangeSet, MemoryEvent}, vfs::notify::{FileChangeSet, MemoryEvent},
Bytes, Error, ImmutPath, TaskInputs, Time, Bytes, Error, ImmutPath, TaskInputs, Time,
}; };
use request::{RegisterCapability, UnregisterCapability};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{Map, Value as JsonValue}; use serde_json::{Map, Value as JsonValue};
use sync_lsp::*; use sync_lsp::*;
@ -295,6 +296,31 @@ impl LanguageState {
} }
impl LanguageState { impl LanguageState {
// todo: handle error
fn register_capability(&self, registrations: Vec<Registration>) -> anyhow::Result<()> {
self.client.send_request_::<RegisterCapability>(
RegistrationParams { registrations },
|_, resp| {
if let Some(err) = resp.error {
log::error!("failed to register capability: {err:?}");
}
},
);
Ok(())
}
fn unregister_capability(&self, unregisterations: Vec<Unregistration>) -> anyhow::Result<()> {
self.client.send_request_::<UnregisterCapability>(
UnregistrationParams { unregisterations },
|_, resp| {
if let Some(err) = resp.error {
log::error!("failed to unregister capability: {err:?}");
}
},
);
Ok(())
}
/// Registers or unregisters semantic tokens. /// Registers or unregisters semantic tokens.
fn enable_sema_token_caps(&mut self, enable: bool) -> anyhow::Result<()> { fn enable_sema_token_caps(&mut self, enable: bool) -> anyhow::Result<()> {
if !self.const_config().tokens_dynamic_registration { if !self.const_config().tokens_dynamic_registration {
@ -306,15 +332,13 @@ impl LanguageState {
(true, false) => { (true, false) => {
trace!("registering semantic tokens"); trace!("registering semantic tokens");
let options = get_semantic_tokens_options(); let options = get_semantic_tokens_options();
self.client self.register_capability(vec![get_semantic_tokens_registration(options)])
.register_capability(vec![get_semantic_tokens_registration(options)])
.inspect(|_| self.sema_tokens_registered = enable) .inspect(|_| self.sema_tokens_registered = enable)
.context("could not register semantic tokens") .context("could not register semantic tokens")
} }
(false, true) => { (false, true) => {
trace!("unregistering semantic tokens"); trace!("unregistering semantic tokens");
self.client self.unregister_capability(vec![get_semantic_tokens_unregistration()])
.unregister_capability(vec![get_semantic_tokens_unregistration()])
.inspect(|_| self.sema_tokens_registered = enable) .inspect(|_| self.sema_tokens_registered = enable)
.context("could not unregister semantic tokens") .context("could not unregister semantic tokens")
} }
@ -350,15 +374,13 @@ impl LanguageState {
match (enable, self.formatter_registered) { match (enable, self.formatter_registered) {
(true, false) => { (true, false) => {
trace!("registering formatter"); trace!("registering formatter");
self.client self.register_capability(vec![get_formatting_registration()])
.register_capability(vec![get_formatting_registration()])
.inspect(|_| self.formatter_registered = enable) .inspect(|_| self.formatter_registered = enable)
.context("could not register formatter") .context("could not register formatter")
} }
(false, true) => { (false, true) => {
trace!("unregistering formatter"); trace!("unregistering formatter");
self.client self.unregister_capability(vec![get_formatting_unregistration()])
.unregister_capability(vec![get_formatting_unregistration()])
.inspect(|_| self.formatter_registered = enable) .inspect(|_| self.formatter_registered = enable)
.context("could not unregister formatter") .context("could not unregister formatter")
} }
@ -409,7 +431,6 @@ impl LanguageState {
const CONFIG_METHOD_ID: &str = "workspace/didChangeConfiguration"; const CONFIG_METHOD_ID: &str = "workspace/didChangeConfiguration";
let err = self let err = self
.client
.register_capability(vec![Registration { .register_capability(vec![Registration {
id: CONFIG_REGISTRATION_ID.to_owned(), id: CONFIG_REGISTRATION_ID.to_owned(),
method: CONFIG_METHOD_ID.to_owned(), method: CONFIG_METHOD_ID.to_owned(),