Migrate flycheck to fully-lsp-compatible progress reports (introduce ra_progress crate)
This commit is contained in:
parent
0262dba97e
commit
2f8126fcac
6 changed files with 225 additions and 101 deletions
|
@ -241,8 +241,11 @@ impl Analysis {
|
|||
self.with_db(|db| status::status(&*db))
|
||||
}
|
||||
|
||||
pub fn prime_caches(&self, files: Vec<FileId>) -> Cancelable<()> {
|
||||
self.with_db(|db| prime_caches::prime_caches(db, files))
|
||||
pub fn prime_caches<P>(&self, files: Vec<FileId>, report_progress: P) -> Cancelable<()>
|
||||
where
|
||||
P: FnMut(usize) + std::panic::UnwindSafe,
|
||||
{
|
||||
self.with_db(|db| prime_caches::prime_caches(db, files, report_progress))
|
||||
}
|
||||
|
||||
/// Gets the text of the source file.
|
||||
|
|
|
@ -5,8 +5,13 @@
|
|||
|
||||
use crate::{FileId, RootDatabase};
|
||||
|
||||
pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) {
|
||||
for file in files {
|
||||
pub(crate) fn prime_caches(
|
||||
db: &RootDatabase,
|
||||
files: Vec<FileId>,
|
||||
mut report_progress: impl FnMut(usize),
|
||||
) {
|
||||
for (i, file) in files.into_iter().enumerate() {
|
||||
let _ = crate::syntax_highlighting::highlight(db, file, None, false);
|
||||
report_progress(i);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
mod handlers;
|
||||
mod subscriptions;
|
||||
pub(crate) mod pending_requests;
|
||||
mod progress;
|
||||
mod lsp_utils;
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
|
@ -44,6 +46,9 @@ use crate::{
|
|||
},
|
||||
Result,
|
||||
};
|
||||
pub use lsp_utils::show_message;
|
||||
use lsp_utils::{is_canceled, notification_cast, notification_is, notification_new, request_new};
|
||||
use progress::{IsDone, PrimeCachesProgressNotifier, WorkspaceAnalysisProgressNotifier};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LspError {
|
||||
|
@ -90,6 +95,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
|
|||
}
|
||||
|
||||
let mut loop_state = LoopState::default();
|
||||
|
||||
let mut global_state = {
|
||||
let workspaces = {
|
||||
if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found {
|
||||
|
@ -164,6 +170,12 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
|
|||
};
|
||||
|
||||
loop_state.roots_total = global_state.vfs.read().n_roots();
|
||||
loop_state.roots_scanned = 0;
|
||||
loop_state.roots_progress = Some(WorkspaceAnalysisProgressNotifier::begin(
|
||||
connection.sender.clone(),
|
||||
loop_state.next_request_id(),
|
||||
loop_state.roots_total,
|
||||
));
|
||||
|
||||
let pool = ThreadPool::default();
|
||||
let (task_sender, task_receiver) = unbounded::<Task>();
|
||||
|
@ -271,7 +283,7 @@ struct LoopState {
|
|||
pending_requests: PendingRequests,
|
||||
subscriptions: Subscriptions,
|
||||
workspace_loaded: bool,
|
||||
roots_progress_reported: Option<usize>,
|
||||
roots_progress: Option<WorkspaceAnalysisProgressNotifier>,
|
||||
roots_scanned: usize,
|
||||
roots_total: usize,
|
||||
configuration_request_id: Option<RequestId>,
|
||||
|
@ -372,7 +384,7 @@ fn loop_turn(
|
|||
}
|
||||
|
||||
if show_progress {
|
||||
send_startup_progress(&connection.sender, loop_state);
|
||||
send_workspace_analisys_progress(loop_state);
|
||||
}
|
||||
|
||||
if state_changed && loop_state.workspace_loaded {
|
||||
|
@ -385,7 +397,22 @@ fn loop_turn(
|
|||
pool.execute({
|
||||
let subs = loop_state.subscriptions.subscriptions();
|
||||
let snap = global_state.snapshot();
|
||||
move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
|
||||
|
||||
let total = subs.len();
|
||||
|
||||
let mut progress = PrimeCachesProgressNotifier::begin(
|
||||
connection.sender.clone(),
|
||||
loop_state.next_request_id(),
|
||||
total,
|
||||
);
|
||||
|
||||
move || {
|
||||
snap.analysis()
|
||||
.prime_caches(subs, move |i| {
|
||||
progress.report(i + 1);
|
||||
})
|
||||
.unwrap_or_else(|_: Canceled| ());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -744,55 +771,12 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state:
|
|||
}
|
||||
}
|
||||
|
||||
fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
|
||||
let total: usize = loop_state.roots_total;
|
||||
let prev = loop_state.roots_progress_reported;
|
||||
let progress = loop_state.roots_scanned;
|
||||
loop_state.roots_progress_reported = Some(progress);
|
||||
|
||||
match (prev, loop_state.workspace_loaded) {
|
||||
(None, false) => {
|
||||
let work_done_progress_create = request_new::<lsp_types::request::WorkDoneProgressCreate>(
|
||||
loop_state.next_request_id(),
|
||||
WorkDoneProgressCreateParams {
|
||||
token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()),
|
||||
},
|
||||
);
|
||||
sender.send(work_done_progress_create.into()).unwrap();
|
||||
send_startup_progress_notif(
|
||||
sender,
|
||||
WorkDoneProgress::Begin(WorkDoneProgressBegin {
|
||||
title: "rust-analyzer".into(),
|
||||
cancellable: None,
|
||||
message: Some(format!("{}/{} packages", progress, total)),
|
||||
percentage: Some(100.0 * progress as f64 / total as f64),
|
||||
}),
|
||||
);
|
||||
fn send_workspace_analisys_progress(loop_state: &mut LoopState) {
|
||||
if let Some(progress) = &mut loop_state.roots_progress {
|
||||
if loop_state.workspace_loaded || progress.report(loop_state.roots_scanned) == IsDone(true)
|
||||
{
|
||||
loop_state.roots_progress = None;
|
||||
}
|
||||
(Some(prev), false) if progress != prev => send_startup_progress_notif(
|
||||
sender,
|
||||
WorkDoneProgress::Report(WorkDoneProgressReport {
|
||||
cancellable: None,
|
||||
message: Some(format!("{}/{} packages", progress, total)),
|
||||
percentage: Some(100.0 * progress as f64 / total as f64),
|
||||
}),
|
||||
),
|
||||
(_, true) => send_startup_progress_notif(
|
||||
sender,
|
||||
WorkDoneProgress::End(WorkDoneProgressEnd {
|
||||
message: Some(format!("rust-analyzer loaded, {} packages", progress)),
|
||||
}),
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) {
|
||||
let notif =
|
||||
notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
|
||||
token: lsp_types::ProgressToken::String("rustAnalyzer/startup".into()),
|
||||
value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
|
||||
});
|
||||
sender.send(notif.into()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -918,7 +902,7 @@ where
|
|||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if is_canceled(&e) {
|
||||
if is_canceled(&*e) {
|
||||
Response::new_err(
|
||||
id,
|
||||
ErrorCode::ContentModified as i32,
|
||||
|
@ -945,7 +929,7 @@ fn update_file_notifications_on_threadpool(
|
|||
for file_id in subscriptions {
|
||||
match handlers::publish_diagnostics(&world, file_id) {
|
||||
Err(e) => {
|
||||
if !is_canceled(&e) {
|
||||
if !is_canceled(&*e) {
|
||||
log::error!("failed to compute diagnostics: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
@ -958,49 +942,6 @@ fn update_file_notifications_on_threadpool(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn show_message(
|
||||
typ: lsp_types::MessageType,
|
||||
message: impl Into<String>,
|
||||
sender: &Sender<Message>,
|
||||
) {
|
||||
let message = message.into();
|
||||
let params = lsp_types::ShowMessageParams { typ, message };
|
||||
let not = notification_new::<lsp_types::notification::ShowMessage>(params);
|
||||
sender.send(not.into()).unwrap();
|
||||
}
|
||||
|
||||
fn is_canceled(e: &Box<dyn std::error::Error + Send + Sync>) -> bool {
|
||||
e.downcast_ref::<Canceled>().is_some()
|
||||
}
|
||||
|
||||
fn notification_is<N: lsp_types::notification::Notification>(notification: &Notification) -> bool {
|
||||
notification.method == N::METHOD
|
||||
}
|
||||
|
||||
fn notification_cast<N>(notification: Notification) -> std::result::Result<N::Params, Notification>
|
||||
where
|
||||
N: lsp_types::notification::Notification,
|
||||
N::Params: DeserializeOwned,
|
||||
{
|
||||
notification.extract(N::METHOD)
|
||||
}
|
||||
|
||||
fn notification_new<N>(params: N::Params) -> Notification
|
||||
where
|
||||
N: lsp_types::notification::Notification,
|
||||
N::Params: Serialize,
|
||||
{
|
||||
Notification::new(N::METHOD.to_string(), params)
|
||||
}
|
||||
|
||||
fn request_new<R>(id: RequestId, params: R::Params) -> Request
|
||||
where
|
||||
R: lsp_types::request::Request,
|
||||
R::Params: Serialize,
|
||||
{
|
||||
Request::new(id, R::METHOD.to_string(), params)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::borrow::Cow;
|
||||
|
|
46
crates/rust-analyzer/src/main_loop/lsp_utils.rs
Normal file
46
crates/rust-analyzer/src/main_loop/lsp_utils.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use crossbeam_channel::Sender;
|
||||
use lsp_server::{Message, Notification, Request, RequestId};
|
||||
use ra_db::Canceled;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::error::Error;
|
||||
|
||||
pub fn show_message(typ: lsp_types::MessageType, message: impl Into<String>, sender: &Sender<Message>) {
|
||||
let message = message.into();
|
||||
let params = lsp_types::ShowMessageParams { typ, message };
|
||||
let not = notification_new::<lsp_types::notification::ShowMessage>(params);
|
||||
sender.send(not.into()).unwrap();
|
||||
}
|
||||
|
||||
pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool {
|
||||
e.downcast_ref::<Canceled>().is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
|
||||
notification: &Notification,
|
||||
) -> bool {
|
||||
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
|
||||
where
|
||||
N: lsp_types::notification::Notification,
|
||||
N::Params: Serialize,
|
||||
{
|
||||
Notification::new(N::METHOD.to_string(), params)
|
||||
}
|
||||
|
||||
pub(crate) fn request_new<R>(id: RequestId, params: R::Params) -> Request
|
||||
where
|
||||
R: lsp_types::request::Request,
|
||||
R::Params: Serialize,
|
||||
{
|
||||
Request::new(id, R::METHOD.to_string(), params)
|
||||
}
|
129
crates/rust-analyzer/src/main_loop/progress.rs
Normal file
129
crates/rust-analyzer/src/main_loop/progress.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
use super::lsp_utils::{notification_new, request_new};
|
||||
use crossbeam_channel::Sender;
|
||||
use lsp_server::{Message, RequestId};
|
||||
use lsp_types::{
|
||||
WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
|
||||
WorkDoneProgressReport,
|
||||
};
|
||||
|
||||
const PRIME_CACHES_PROGRESS_TOKEN: &str = "rustAnalyzer/primeCaches";
|
||||
const WORKSPACE_ANALYSIS_PROGRESS_TOKEN: &str = "rustAnalyzer/workspaceAnalysis";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PrimeCachesProgressNotifier(ProgressNotifier);
|
||||
|
||||
impl Drop for PrimeCachesProgressNotifier {
|
||||
fn drop(&mut self) {
|
||||
self.0.end("done priming caches".to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimeCachesProgressNotifier {
|
||||
pub(crate) fn begin(sender: Sender<Message>, req_id: RequestId, total: usize) -> Self {
|
||||
let me = Self(ProgressNotifier {
|
||||
sender,
|
||||
processed: 0,
|
||||
total,
|
||||
token: PRIME_CACHES_PROGRESS_TOKEN,
|
||||
label: "priming caches",
|
||||
});
|
||||
me.0.begin(req_id);
|
||||
me
|
||||
}
|
||||
|
||||
pub(crate) fn report(&mut self, processed: usize) -> IsDone {
|
||||
self.0.report(processed)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct WorkspaceAnalysisProgressNotifier(ProgressNotifier);
|
||||
|
||||
impl Drop for WorkspaceAnalysisProgressNotifier {
|
||||
fn drop(&mut self) {
|
||||
self.0.end("done analyzing workspace".to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkspaceAnalysisProgressNotifier {
|
||||
pub(crate) fn begin(sender: Sender<Message>, req_id: RequestId, total: usize) -> Self {
|
||||
let me = Self(ProgressNotifier {
|
||||
sender,
|
||||
total,
|
||||
processed: 0,
|
||||
token: WORKSPACE_ANALYSIS_PROGRESS_TOKEN,
|
||||
label: "analyzing packages",
|
||||
});
|
||||
me.0.begin(req_id);
|
||||
me
|
||||
}
|
||||
|
||||
pub(crate) fn report(&mut self, processed: usize) -> IsDone {
|
||||
self.0.report(processed)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct IsDone(pub bool);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ProgressNotifier {
|
||||
sender: Sender<Message>,
|
||||
token: &'static str,
|
||||
label: &'static str,
|
||||
processed: usize,
|
||||
total: usize,
|
||||
}
|
||||
|
||||
impl ProgressNotifier {
|
||||
fn begin(&self, req_id: RequestId) {
|
||||
let create_req = request_new::<lsp_types::request::WorkDoneProgressCreate>(
|
||||
req_id,
|
||||
WorkDoneProgressCreateParams {
|
||||
token: lsp_types::ProgressToken::String(self.token.to_owned()),
|
||||
},
|
||||
);
|
||||
self.sender.send(create_req.into()).unwrap();
|
||||
self.send_notification(WorkDoneProgress::Begin(WorkDoneProgressBegin {
|
||||
cancellable: None,
|
||||
title: "rust-analyzer".to_owned(),
|
||||
percentage: Some(self.percentage()),
|
||||
message: Some(self.create_progress_message()),
|
||||
}));
|
||||
}
|
||||
|
||||
fn report(&mut self, processed: usize) -> IsDone {
|
||||
if self.processed != processed {
|
||||
self.processed = processed;
|
||||
|
||||
self.send_notification(WorkDoneProgress::Report(WorkDoneProgressReport {
|
||||
cancellable: None,
|
||||
percentage: Some(self.percentage()),
|
||||
message: Some(self.create_progress_message()),
|
||||
}));
|
||||
}
|
||||
IsDone(processed >= self.total)
|
||||
}
|
||||
|
||||
fn end(&mut self, message: String) {
|
||||
self.send_notification(WorkDoneProgress::End(WorkDoneProgressEnd {
|
||||
message: Some(message),
|
||||
}));
|
||||
}
|
||||
|
||||
fn send_notification(&self, progress: WorkDoneProgress) {
|
||||
let notif = notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
|
||||
token: lsp_types::ProgressToken::String(self.token.to_owned()),
|
||||
value: lsp_types::ProgressParamsValue::WorkDone(progress),
|
||||
});
|
||||
self.sender.send(notif.into()).unwrap();
|
||||
}
|
||||
|
||||
fn create_progress_message(&self) -> String {
|
||||
format!("{} ({}/{})", self.label, self.processed, self.total)
|
||||
}
|
||||
|
||||
fn percentage(&self) -> f64 {
|
||||
(100 * self.processed) as f64 / self.total as f64
|
||||
}
|
||||
}
|
|
@ -212,7 +212,7 @@ impl Server {
|
|||
ProgressParams {
|
||||
token: lsp_types::ProgressToken::String(ref token),
|
||||
value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)),
|
||||
} if token == "rustAnalyzer/startup" => true,
|
||||
} if token == "rustAnalyzer/workspaceAnalysis" => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue