Use WorkDoneProgress LSP API for initial load

Addresses #3283

Rather than using custom UI for showing the loaded state. Rely
on the WorkDoneProgress API in 3.15.0
https://microsoft.github.io/language-server-protocol/specification#workDoneProgress.

No client-side work was necessary. The UI is not exactly what is
described in the issue but afaict that's how VS Code implements the LSP
API.

- The WorkDoneProgressEnd does not appear to display its message
  contents (controlled by vscode)
This commit is contained in:
Steffen Lyngbaek 2020-03-13 16:03:02 -07:00
parent 2f9f409538
commit 083035fd06

View File

@ -16,7 +16,10 @@ use std::{
use crossbeam_channel::{select, unbounded, RecvError, Sender};
use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
use lsp_types::{ClientCapabilities, NumberOrString};
use lsp_types::{
ClientCapabilities, NumberOrString, WorkDoneProgress, WorkDoneProgressBegin,
WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport,
};
use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask};
use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId};
use ra_prof::profile;
@ -329,6 +332,7 @@ struct LoopState {
in_flight_libraries: usize,
pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>,
workspace_loaded: bool,
roots_scanned_progress: Option<usize>,
}
impl LoopState {
@ -428,11 +432,6 @@ fn loop_turn(
&& loop_state.in_flight_libraries == 0
{
loop_state.workspace_loaded = true;
let n_packages: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum();
if world_state.feature_flags.get("notifications.workspace-loaded") {
let msg = format!("workspace loaded, {} rust packages", n_packages);
show_message(req::MessageType::Info, msg, &connection.sender);
}
world_state.check_watcher.update();
pool.execute({
let subs = loop_state.subscriptions.subscriptions();
@ -440,6 +439,7 @@ fn loop_turn(
move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
});
}
send_startup_progress(&connection.sender, loop_state, world_state);
if state_changed {
update_file_notifications_on_threadpool(
@ -703,6 +703,65 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state:
}
}
fn send_startup_progress(
sender: &Sender<Message>,
loop_state: &mut LoopState,
world_state: &WorldState,
) {
if !world_state.feature_flags.get("notifications.workspace-loaded") {
return;
}
let total: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum();
let progress = total - world_state.roots_to_scan;
if loop_state.roots_scanned_progress == Some(progress) {
return;
}
loop_state.roots_scanned_progress = Some(progress);
match (progress, loop_state.workspace_loaded) {
(0, false) => {
let work_done_progress_create = request_new::<req::WorkDoneProgressCreate>(
loop_state.next_request_id(),
WorkDoneProgressCreateParams {
token: req::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 as f64 * progress as f64 / total as f64),
}),
);
}
(_, false) => send_startup_progress_notif(
sender,
WorkDoneProgress::Report(WorkDoneProgressReport {
cancellable: None,
message: Some(format!("{}/{} packages", progress, total)),
percentage: Some(100 as f64 * 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::<req::Progress>(req::ProgressParams {
token: req::ProgressToken::String("rustAnalyzer/startup".into()),
value: req::ProgressParamsValue::WorkDone(work_done_progress),
});
sender.send(notif.into()).unwrap();
}
struct PoolDispatcher<'a> {
req: Option<Request>,
pool: &'a ThreadPool,