Type safer requests

This commit is contained in:
Aleksey Kladov 2020-06-26 17:07:14 +02:00
parent 1893289e5c
commit 9d15e8fc4f
4 changed files with 73 additions and 49 deletions

View file

@ -7,7 +7,7 @@ use std::{sync::Arc, time::Instant};
use crossbeam_channel::{unbounded, Receiver, Sender}; use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle; use flycheck::FlycheckHandle;
use lsp_types::Url; use lsp_types::{request::Request as _, Url};
use parking_lot::RwLock; use parking_lot::RwLock;
use ra_db::{CrateId, VfsPath}; use ra_db::{CrateId, VfsPath};
use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
@ -18,6 +18,7 @@ use crate::{
diagnostics::{CheckFixes, DiagnosticCollection}, diagnostics::{CheckFixes, DiagnosticCollection},
from_proto, from_proto,
line_endings::LineEndings, line_endings::LineEndings,
lsp_utils::notification_new,
main_loop::Task, main_loop::Task,
reload::SourceRootConfig, reload::SourceRootConfig,
request_metrics::{LatestRequests, RequestMetrics}, request_metrics::{LatestRequests, RequestMetrics},
@ -57,6 +58,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
/// Note that this struct has more than on impl in various modules! /// Note that this struct has more than on impl in various modules!
pub(crate) struct GlobalState { pub(crate) struct GlobalState {
sender: Sender<lsp_server::Message>, sender: Sender<lsp_server::Message>,
req_queue: ReqQueue,
pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>, pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>,
@ -66,7 +68,6 @@ pub(crate) struct GlobalState {
pub(crate) mem_docs: FxHashSet<VfsPath>, pub(crate) mem_docs: FxHashSet<VfsPath>,
pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
pub(crate) status: Status, pub(crate) status: Status,
pub(crate) req_queue: ReqQueue,
pub(crate) source_root_config: SourceRootConfig, pub(crate) source_root_config: SourceRootConfig,
pub(crate) proc_macro_client: ProcMacroClient, pub(crate) proc_macro_client: ProcMacroClient,
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
@ -102,16 +103,16 @@ impl GlobalState {
let analysis_host = AnalysisHost::new(config.lru_capacity); let analysis_host = AnalysisHost::new(config.lru_capacity);
GlobalState { GlobalState {
sender, sender,
req_queue: ReqQueue::default(),
task_pool, task_pool,
loader, loader,
flycheck: None,
config, config,
analysis_host, analysis_host,
flycheck: None,
diagnostics: Default::default(), diagnostics: Default::default(),
mem_docs: FxHashSet::default(), mem_docs: FxHashSet::default(),
vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
status: Status::default(), status: Status::default(),
req_queue: ReqQueue::default(),
source_root_config: SourceRootConfig::default(), source_root_config: SourceRootConfig::default(),
proc_macro_client: ProcMacroClient::dummy(), proc_macro_client: ProcMacroClient::dummy(),
workspaces: Arc::new(Vec::new()), workspaces: Arc::new(Vec::new()),
@ -168,8 +169,39 @@ impl GlobalState {
} }
} }
pub(crate) fn send(&mut self, message: lsp_server::Message) { pub(crate) fn send_request<R: lsp_types::request::Request>(
self.sender.send(message).unwrap() &mut self,
params: R::Params,
handler: ReqHandler,
) {
let request = self.req_queue.outgoing.register(
lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(),
params,
handler,
);
self.send(request.into());
}
pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
let handler = self.req_queue.outgoing.complete(response.id.clone());
handler(self, response)
}
pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
&mut self,
params: N::Params,
) {
let not = notification_new::<N>(params);
self.send(not.into());
}
pub(crate) fn register_request(
&mut self,
request: &lsp_server::Request,
request_received: Instant,
) {
self.req_queue
.incoming
.register(request.id.clone(), (request.method.clone(), request_received));
} }
pub(crate) fn respond(&mut self, response: lsp_server::Response) { pub(crate) fn respond(&mut self, response: lsp_server::Response) {
if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
@ -181,6 +213,15 @@ impl GlobalState {
self.send(response.into()); self.send(response.into());
} }
} }
pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
if let Some(response) = self.req_queue.incoming.cancel(request_id) {
self.send(response.into());
}
}
fn send(&mut self, message: lsp_server::Message) {
self.sender.send(message).unwrap()
}
} }
impl Drop for GlobalState { impl Drop for GlobalState {

View file

@ -2,7 +2,6 @@
use std::{error::Error, ops::Range}; use std::{error::Error, ops::Range};
use lsp_server::Notification; use lsp_server::Notification;
use lsp_types::request::Request;
use ra_db::Canceled; use ra_db::Canceled;
use ra_ide::LineIndex; use ra_ide::LineIndex;
use serde::Serialize; use serde::Serialize;
@ -43,9 +42,9 @@ impl Progress {
impl GlobalState { impl GlobalState {
pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) {
let message = message.into(); let message = message.into();
let params = lsp_types::ShowMessageParams { typ, message }; self.send_notification::<lsp_types::notification::ShowMessage>(
let not = notification_new::<lsp_types::notification::ShowMessage>(params); lsp_types::ShowMessageParams { typ, message },
self.send(not.into()); )
} }
pub(crate) fn report_progress( pub(crate) fn report_progress(
@ -61,12 +60,10 @@ impl GlobalState {
let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
let work_done_progress = match state { let work_done_progress = match state {
Progress::Begin => { Progress::Begin => {
let work_done_progress_create = self.req_queue.outgoing.register( self.send_request::<lsp_types::request::WorkDoneProgressCreate>(
lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(),
lsp_types::WorkDoneProgressCreateParams { token: token.clone() }, lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
|_, _| (), |_, _| (),
); );
self.send(work_done_progress_create.into());
lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
title: title.into(), title: title.into(),
@ -86,12 +83,10 @@ impl GlobalState {
lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message }) lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
} }
}; };
let notification = self.send_notification::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams { token,
token, value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress), });
});
self.send(notification.into());
} }
} }

View file

@ -7,7 +7,7 @@ use std::{
use crossbeam_channel::{never, select, Receiver}; use crossbeam_channel::{never, select, Receiver};
use lsp_server::{Connection, Notification, Request, Response}; use lsp_server::{Connection, Notification, Request, Response};
use lsp_types::{notification::Notification as _, request::Request as _}; use lsp_types::notification::Notification as _;
use ra_db::VfsPath; use ra_db::VfsPath;
use ra_ide::{Canceled, FileId}; use ra_ide::{Canceled, FileId};
use ra_prof::profile; use ra_prof::profile;
@ -18,7 +18,7 @@ use crate::{
from_proto, from_proto,
global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, global_state::{file_id_to_url, url_to_file_id, GlobalState, Status},
handlers, lsp_ext, handlers, lsp_ext,
lsp_utils::{apply_document_changes, is_canceled, notification_is, notification_new, Progress}, lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
Result, Result,
}; };
@ -143,10 +143,7 @@ impl GlobalState {
lsp_server::Message::Notification(not) => { lsp_server::Message::Notification(not) => {
self.on_notification(not)?; self.on_notification(not)?;
} }
lsp_server::Message::Response(resp) => { lsp_server::Message::Response(resp) => self.complete_request(resp),
let handler = self.req_queue.outgoing.complete(resp.id.clone());
handler(self, resp)
}
}, },
Event::Task(task) => { Event::Task(task) => {
match task { match task {
@ -250,10 +247,9 @@ impl GlobalState {
for file_id in diagnostic_changes { for file_id in diagnostic_changes {
let url = file_id_to_url(&self.vfs.read().0, file_id); let url = file_id_to_url(&self.vfs.read().0, file_id);
let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect();
let params = self.send_notification::<lsp_types::notification::PublishDiagnostics>(
lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }; lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None },
let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); );
self.send(not.into());
} }
} }
@ -271,7 +267,7 @@ impl GlobalState {
} }
fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
self.req_queue.incoming.register(req.id.clone(), (req.method.clone(), request_received)); self.register_request(&req, request_received);
RequestDispatcher { req: Some(req), global_state: self } RequestDispatcher { req: Some(req), global_state: self }
.on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))? .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))?
@ -335,9 +331,7 @@ impl GlobalState {
lsp_types::NumberOrString::Number(id) => id.into(), lsp_types::NumberOrString::Number(id) => id.into(),
lsp_types::NumberOrString::String(id) => id.into(), lsp_types::NumberOrString::String(id) => id.into(),
}; };
if let Some(response) = this.req_queue.incoming.cancel(id) { this.cancel(id);
this.send(response.into());
}
Ok(()) Ok(())
})? })?
.on::<lsp_types::notification::DidOpenTextDocument>(|this, params| { .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
@ -372,13 +366,13 @@ impl GlobalState {
this.loader.handle.invalidate(path.to_path_buf()); this.loader.handle.invalidate(path.to_path_buf());
} }
} }
let params = lsp_types::PublishDiagnosticsParams { this.send_notification::<lsp_types::notification::PublishDiagnostics>(
uri: params.text_document.uri, lsp_types::PublishDiagnosticsParams {
diagnostics: Vec::new(), uri: params.text_document.uri,
version: None, diagnostics: Vec::new(),
}; version: None,
let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); },
this.send(not.into()); );
Ok(()) Ok(())
})? })?
.on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| { .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| {
@ -390,8 +384,7 @@ impl GlobalState {
.on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
// As stated in https://github.com/microsoft/language-server-protocol/issues/676, // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
// this notification's parameters should be ignored and the actual config queried separately. // this notification's parameters should be ignored and the actual config queried separately.
let request = this.req_queue.outgoing.register( this.send_request::<lsp_types::request::WorkspaceConfiguration>(
lsp_types::request::WorkspaceConfiguration::METHOD.to_string(),
lsp_types::ConfigurationParams { lsp_types::ConfigurationParams {
items: vec![lsp_types::ConfigurationItem { items: vec![lsp_types::ConfigurationItem {
scope_uri: None, scope_uri: None,
@ -419,7 +412,6 @@ impl GlobalState {
} }
}, },
); );
this.send(request.into());
return Ok(()); return Ok(());
})? })?

View file

@ -3,7 +3,6 @@ use std::sync::Arc;
use crossbeam_channel::unbounded; use crossbeam_channel::unbounded;
use flycheck::FlycheckHandle; use flycheck::FlycheckHandle;
use lsp_types::request::Request;
use ra_db::{CrateGraph, SourceRoot, VfsPath}; use ra_db::{CrateGraph, SourceRoot, VfsPath};
use ra_ide::AnalysisChange; use ra_ide::AnalysisChange;
use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace};
@ -78,13 +77,10 @@ impl GlobalState {
method: "workspace/didChangeWatchedFiles".to_string(), method: "workspace/didChangeWatchedFiles".to_string(),
register_options: Some(serde_json::to_value(registration_options).unwrap()), register_options: Some(serde_json::to_value(registration_options).unwrap()),
}; };
let params = lsp_types::RegistrationParams { registrations: vec![registration] }; self.send_request::<lsp_types::request::RegisterCapability>(
let request = self.req_queue.outgoing.register( lsp_types::RegistrationParams { registrations: vec![registration] },
lsp_types::request::RegisterCapability::METHOD.to_string(),
params,
|_, _| (), |_, _| (),
); );
self.send(request.into());
} }
let mut change = AnalysisChange::new(); let mut change = AnalysisChange::new();