mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-12 09:57:43 +00:00
Add NotificationDispatcher
This commit is contained in:
parent
22098127c4
commit
f5ea35a271
@ -1,3 +1,4 @@
|
|||||||
|
//! A visitor for downcasting arbitrary request (JSON) into a specific type.
|
||||||
use std::{panic, time::Instant};
|
use std::{panic, time::Instant};
|
||||||
|
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
@ -135,3 +136,41 @@ where
|
|||||||
};
|
};
|
||||||
Task::Respond(response)
|
Task::Respond(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct NotificationDispatcher<'a> {
|
||||||
|
pub(crate) not: Option<lsp_server::Notification>,
|
||||||
|
pub(crate) global_state: &'a mut GlobalState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NotificationDispatcher<'a> {
|
||||||
|
pub(crate) fn on<N>(
|
||||||
|
&mut self,
|
||||||
|
f: fn(&mut GlobalState, N::Params) -> Result<()>,
|
||||||
|
) -> Result<&mut Self>
|
||||||
|
where
|
||||||
|
N: lsp_types::notification::Notification + 'static,
|
||||||
|
N::Params: DeserializeOwned + Send + 'static,
|
||||||
|
{
|
||||||
|
let not = match self.not.take() {
|
||||||
|
Some(it) => it,
|
||||||
|
None => return Ok(self),
|
||||||
|
};
|
||||||
|
let params = match not.extract::<N::Params>(N::METHOD) {
|
||||||
|
Ok(it) => it,
|
||||||
|
Err(not) => {
|
||||||
|
self.not = Some(not);
|
||||||
|
return Ok(self);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
f(self.global_state, params)?;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn finish(&mut self) {
|
||||||
|
if let Some(not) = &self.not {
|
||||||
|
if !not.method.starts_with("$/") {
|
||||||
|
log::error!("unhandled notification: {:?}", not);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
//! Utilities for LSP-related boilerplate code.
|
//! Utilities for LSP-related boilerplate code.
|
||||||
use std::{error::Error, ops::Range};
|
use std::{error::Error, ops::Range};
|
||||||
|
|
||||||
use crate::from_proto;
|
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use lsp_server::{Message, Notification};
|
use lsp_server::{Message, Notification};
|
||||||
use ra_db::Canceled;
|
use ra_db::Canceled;
|
||||||
use ra_ide::LineIndex;
|
use ra_ide::LineIndex;
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::from_proto;
|
||||||
|
|
||||||
pub fn show_message(
|
pub fn show_message(
|
||||||
typ: lsp_types::MessageType,
|
typ: lsp_types::MessageType,
|
||||||
@ -29,14 +30,6 @@ pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
|
|||||||
notification.method == N::METHOD
|
notification.method == N::METHOD
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn notification_cast<N>(notification: Notification) -> Result<N::Params, Notification>
|
|
||||||
where
|
|
||||||
N: lsp_types::notification::Notification,
|
|
||||||
N::Params: DeserializeOwned,
|
|
||||||
{
|
|
||||||
notification.extract(N::METHOD)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn notification_new<N>(params: N::Params) -> Notification
|
pub(crate) fn notification_new<N>(params: N::Params) -> Notification
|
||||||
where
|
where
|
||||||
N: lsp_types::notification::Notification,
|
N: lsp_types::notification::Notification,
|
||||||
|
@ -6,8 +6,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crossbeam_channel::{never, select, Receiver};
|
use crossbeam_channel::{never, select, Receiver};
|
||||||
use lsp_server::{Connection, Notification, Request, RequestId, Response};
|
use lsp_server::{Connection, Notification, Request, Response};
|
||||||
use lsp_types::{notification::Notification as _, request::Request as _, NumberOrString};
|
use lsp_types::{notification::Notification as _, request::Request 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;
|
||||||
@ -16,13 +16,12 @@ use ra_project_model::{PackageRoot, ProjectWorkspace};
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::{Config, FilesWatcher, LinkedProject},
|
config::{Config, FilesWatcher, LinkedProject},
|
||||||
diagnostics::DiagnosticTask,
|
diagnostics::DiagnosticTask,
|
||||||
dispatch::RequestDispatcher,
|
dispatch::{NotificationDispatcher, RequestDispatcher},
|
||||||
from_proto,
|
from_proto,
|
||||||
global_state::{file_id_to_url, GlobalState, Status},
|
global_state::{file_id_to_url, GlobalState, Status},
|
||||||
handlers, lsp_ext,
|
handlers, lsp_ext,
|
||||||
lsp_utils::{
|
lsp_utils::{
|
||||||
apply_document_changes, is_canceled, notification_cast, notification_is, notification_new,
|
apply_document_changes, is_canceled, notification_is, notification_new, show_message,
|
||||||
show_message,
|
|
||||||
},
|
},
|
||||||
request_metrics::RequestMetrics,
|
request_metrics::RequestMetrics,
|
||||||
Result,
|
Result,
|
||||||
@ -240,9 +239,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<()> {
|
||||||
let mut pool_dispatcher =
|
RequestDispatcher { req: Some(req), global_state: self, request_received }
|
||||||
RequestDispatcher { req: Some(req), global_state: self, request_received };
|
|
||||||
pool_dispatcher
|
|
||||||
.on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.collect_garbage()))?
|
.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::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))?
|
||||||
.on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
|
.on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))?
|
||||||
@ -298,56 +295,47 @@ impl GlobalState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn on_notification(&mut self, not: Notification) -> Result<()> {
|
fn on_notification(&mut self, not: Notification) -> Result<()> {
|
||||||
let not = match notification_cast::<lsp_types::notification::Cancel>(not) {
|
NotificationDispatcher { not: Some(not), global_state: self }
|
||||||
Ok(params) => {
|
.on::<lsp_types::notification::Cancel>(|this, params| {
|
||||||
let id: RequestId = match params.id {
|
let id: lsp_server::RequestId = match params.id {
|
||||||
NumberOrString::Number(id) => id.into(),
|
lsp_types::NumberOrString::Number(id) => id.into(),
|
||||||
NumberOrString::String(id) => id.into(),
|
lsp_types::NumberOrString::String(id) => id.into(),
|
||||||
};
|
};
|
||||||
if let Some(response) = self.req_queue.incoming.cancel(id) {
|
if let Some(response) = this.req_queue.incoming.cancel(id) {
|
||||||
self.send(response.into())
|
this.send(response.into());
|
||||||
}
|
}
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
})?
|
||||||
Err(not) => not,
|
.on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
|
||||||
};
|
|
||||||
let not = match notification_cast::<lsp_types::notification::DidOpenTextDocument>(not) {
|
|
||||||
Ok(params) => {
|
|
||||||
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
|
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
|
||||||
if !self.mem_docs.insert(path.clone()) {
|
if !this.mem_docs.insert(path.clone()) {
|
||||||
log::error!("duplicate DidOpenTextDocument: {}", path)
|
log::error!("duplicate DidOpenTextDocument: {}", path)
|
||||||
}
|
}
|
||||||
self.vfs
|
this.vfs
|
||||||
.write()
|
.write()
|
||||||
.0
|
.0
|
||||||
.set_file_contents(path, Some(params.text_document.text.into_bytes()));
|
.set_file_contents(path, Some(params.text_document.text.into_bytes()));
|
||||||
}
|
}
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
})?
|
||||||
Err(not) => not,
|
.on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
|
||||||
};
|
|
||||||
let not = match notification_cast::<lsp_types::notification::DidChangeTextDocument>(not) {
|
|
||||||
Ok(params) => {
|
|
||||||
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
|
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
|
||||||
assert!(self.mem_docs.contains(&path));
|
assert!(this.mem_docs.contains(&path));
|
||||||
let vfs = &mut self.vfs.write().0;
|
let vfs = &mut this.vfs.write().0;
|
||||||
let file_id = vfs.file_id(&path).unwrap();
|
let file_id = vfs.file_id(&path).unwrap();
|
||||||
let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
|
let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap();
|
||||||
apply_document_changes(&mut text, params.content_changes);
|
apply_document_changes(&mut text, params.content_changes);
|
||||||
vfs.set_file_contents(path, Some(text.into_bytes()))
|
vfs.set_file_contents(path, Some(text.into_bytes()))
|
||||||
}
|
}
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
})?
|
||||||
Err(not) => not,
|
.on::<lsp_types::notification::DidCloseTextDocument>(|this, params| {
|
||||||
};
|
|
||||||
let not = match notification_cast::<lsp_types::notification::DidCloseTextDocument>(not) {
|
|
||||||
Ok(params) => {
|
|
||||||
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
|
if let Ok(path) = from_proto::vfs_path(¶ms.text_document.uri) {
|
||||||
if !self.mem_docs.remove(&path) {
|
if !this.mem_docs.remove(&path) {
|
||||||
log::error!("orphan DidCloseTextDocument: {}", path)
|
log::error!("orphan DidCloseTextDocument: {}", path)
|
||||||
}
|
}
|
||||||
if let Some(path) = path.as_path() {
|
if let Some(path) = path.as_path() {
|
||||||
self.loader.invalidate(path.to_path_buf());
|
this.loader.invalidate(path.to_path_buf());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let params = lsp_types::PublishDiagnosticsParams {
|
let params = lsp_types::PublishDiagnosticsParams {
|
||||||
@ -356,25 +344,19 @@ impl GlobalState {
|
|||||||
version: None,
|
version: None,
|
||||||
};
|
};
|
||||||
let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params);
|
let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params);
|
||||||
self.send(not.into());
|
this.send(not.into());
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
})?
|
||||||
Err(not) => not,
|
.on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| {
|
||||||
};
|
if let Some(flycheck) = &this.flycheck {
|
||||||
let not = match notification_cast::<lsp_types::notification::DidSaveTextDocument>(not) {
|
|
||||||
Ok(_params) => {
|
|
||||||
if let Some(flycheck) = &self.flycheck {
|
|
||||||
flycheck.0.update();
|
flycheck.0.update();
|
||||||
}
|
}
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
})?
|
||||||
Err(not) => not,
|
.on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
|
||||||
};
|
|
||||||
let not = match notification_cast::<lsp_types::notification::DidChangeConfiguration>(not) {
|
|
||||||
Ok(_) => {
|
|
||||||
// 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 = self.req_queue.outgoing.register(
|
let request = this.req_queue.outgoing.register(
|
||||||
lsp_types::request::WorkspaceConfiguration::METHOD.to_string(),
|
lsp_types::request::WorkspaceConfiguration::METHOD.to_string(),
|
||||||
lsp_types::ConfigurationParams {
|
lsp_types::ConfigurationParams {
|
||||||
items: vec![lsp_types::ConfigurationItem {
|
items: vec![lsp_types::ConfigurationItem {
|
||||||
@ -403,30 +385,21 @@ impl GlobalState {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.send(request.into());
|
this.send(request.into());
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
})?
|
||||||
Err(not) => not,
|
.on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| {
|
||||||
};
|
|
||||||
let not = match notification_cast::<lsp_types::notification::DidChangeWatchedFiles>(not) {
|
|
||||||
Ok(params) => {
|
|
||||||
for change in params.changes {
|
for change in params.changes {
|
||||||
if let Ok(path) = from_proto::abs_path(&change.uri) {
|
if let Ok(path) = from_proto::abs_path(&change.uri) {
|
||||||
self.loader.invalidate(path)
|
this.loader.invalidate(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
})?
|
||||||
Err(not) => not,
|
.finish();
|
||||||
};
|
|
||||||
if not.method.starts_with("$/") {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
log::error!("unhandled notification: {:?}", not);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// TODO
|
|
||||||
pub(crate) fn on_task(&mut self, task: Task) {
|
pub(crate) fn on_task(&mut self, task: Task) {
|
||||||
match task {
|
match task {
|
||||||
Task::Respond(response) => {
|
Task::Respond(response) => {
|
||||||
@ -481,7 +454,6 @@ impl GlobalState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum Task {
|
pub(crate) enum Task {
|
||||||
Respond(Response),
|
Respond(Response),
|
||||||
|
Loading…
Reference in New Issue
Block a user