This commit is contained in:
Aleksey Kladov 2020-06-25 19:23:52 +02:00
parent dba11cb060
commit 86a4d4cb9c
3 changed files with 43 additions and 57 deletions

View File

@ -1,5 +1,5 @@
//! A visitor for downcasting arbitrary request (JSON) into a specific type.
use std::{panic, time::Instant};
use std::panic;
use serde::{de::DeserializeOwned, Serialize};
@ -13,7 +13,6 @@ use crate::{
pub(crate) struct RequestDispatcher<'a> {
pub(crate) req: Option<lsp_server::Request>,
pub(crate) global_state: &'a mut GlobalState,
pub(crate) request_received: Instant,
}
impl<'a> RequestDispatcher<'a> {
@ -34,12 +33,12 @@ impl<'a> RequestDispatcher<'a> {
}
};
let world = panic::AssertUnwindSafe(&mut *self.global_state);
let task = panic::catch_unwind(move || {
let response = panic::catch_unwind(move || {
let result = f(world.0, params);
result_to_task::<R>(id, result)
result_to_response::<R>(id, result)
})
.map_err(|_| format!("sync task {:?} panicked", R::METHOD))?;
self.global_state.on_task(task);
self.global_state.respond(response);
Ok(self)
}
@ -64,7 +63,7 @@ impl<'a> RequestDispatcher<'a> {
let world = self.global_state.snapshot();
move || {
let result = f(world, params);
result_to_task::<R>(id, result)
Task::Response(result_to_response::<R>(id, result))
}
});
@ -72,17 +71,14 @@ impl<'a> RequestDispatcher<'a> {
}
pub(crate) fn finish(&mut self) {
match self.req.take() {
None => (),
Some(req) => {
log::error!("unknown request: {:?}", req);
let resp = lsp_server::Response::new_err(
req.id,
lsp_server::ErrorCode::MethodNotFound as i32,
"unknown request".to_string(),
);
self.global_state.send(resp.into());
}
if let Some(req) = self.req.take() {
log::error!("unknown request: {:?}", req);
let response = lsp_server::Response::new_err(
req.id,
lsp_server::ErrorCode::MethodNotFound as i32,
"unknown request".to_string(),
);
self.global_state.respond(response)
}
}
@ -99,21 +95,20 @@ impl<'a> RequestDispatcher<'a> {
return None;
}
};
self.global_state
.req_queue
.incoming
.register(id.clone(), (R::METHOD, self.request_received));
Some((id, params))
}
}
fn result_to_task<R>(id: lsp_server::RequestId, result: Result<R::Result>) -> Task
fn result_to_response<R>(
id: lsp_server::RequestId,
result: Result<R::Result>,
) -> lsp_server::Response
where
R: lsp_types::request::Request + 'static,
R::Params: DeserializeOwned + 'static,
R::Result: Serialize + 'static,
{
let response = match result {
match result {
Ok(resp) => lsp_server::Response::new_ok(id, &resp),
Err(e) => match e.downcast::<LspError>() {
Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
@ -133,8 +128,7 @@ where
}
}
},
};
Task::Respond(response)
}
}
pub(crate) struct NotificationDispatcher<'a> {

View File

@ -253,13 +253,19 @@ impl GlobalState {
self.analysis_host.collect_garbage()
}
pub(crate) fn complete_request(&mut self, request: RequestMetrics) {
self.latest_requests.write().record(request)
}
pub(crate) fn send(&mut self, message: lsp_server::Message) {
self.sender.send(message).unwrap()
}
pub(crate) fn respond(&mut self, response: lsp_server::Response) {
if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
let duration = start.elapsed();
log::info!("handled req#{} in {:?}", response.id, duration);
let metrics =
RequestMetrics { id: response.id.clone(), method: method.to_string(), duration };
self.latest_requests.write().record(metrics);
self.send(response.into());
}
}
pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) {
show_message(typ, message, &self.sender)
}

View File

@ -23,7 +23,6 @@ use crate::{
lsp_utils::{
apply_document_changes, is_canceled, notification_is, notification_new, show_message,
},
request_metrics::RequestMetrics,
Result,
};
@ -147,7 +146,7 @@ impl fmt::Debug for Event {
return debug_verbose_not(not, f);
}
}
Event::Task(Task::Respond(resp)) => {
Event::Task(Task::Response(resp)) => {
return f
.debug_struct("Response")
.field("id", &resp.id)
@ -218,7 +217,13 @@ impl GlobalState {
}
},
Event::Task(task) => {
self.on_task(task);
match task {
Task::Response(response) => self.respond(response),
Task::Diagnostics(tasks) => {
tasks.into_iter().for_each(|task| on_diagnostic_task(task, self))
}
Task::Unit => (),
}
self.maybe_collect_garbage();
}
Event::Vfs(task) => match task {
@ -331,7 +336,9 @@ impl GlobalState {
}
fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
RequestDispatcher { req: Some(req), global_state: self, request_received }
self.req_queue.incoming.register(req.id.clone(), (req.method.clone(), request_received));
RequestDispatcher { req: Some(req), global_state: self }
.on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))?
.on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
.on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
@ -492,27 +499,6 @@ impl GlobalState {
.finish();
Ok(())
}
pub(crate) fn on_task(&mut self, task: Task) {
match task {
Task::Respond(response) => {
if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone())
{
let duration = start.elapsed();
log::info!("handled req#{} in {:?}", response.id, duration);
self.complete_request(RequestMetrics {
id: response.id.clone(),
method: method.to_string(),
duration,
});
self.send(response.into());
}
}
Task::Diagnostics(tasks) => {
tasks.into_iter().for_each(|task| on_diagnostic_task(task, self))
}
Task::Unit => (),
}
}
fn update_file_notifications_on_threadpool(&mut self, subscriptions: Vec<FileId>) {
log::trace!("updating notifications for {:?}", subscriptions);
if self.config.publish_diagnostics {
@ -548,13 +534,13 @@ impl GlobalState {
#[derive(Debug)]
pub(crate) enum Task {
Respond(Response),
Diagnostics(Vec<DiagnosticTask>),
Response(Response),
Diagnostics(()),
Unit,
}
pub(crate) type ReqHandler = fn(&mut GlobalState, Response);
pub(crate) type ReqQueue = lsp_server::ReqQueue<(&'static str, Instant), ReqHandler>;
pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
const DO_NOTHING: ReqHandler = |_, _| ();
fn on_diagnostic_task(task: DiagnosticTask, global_state: &mut GlobalState) {