Skip to content

Commit 5bdc249

Browse files
bors[bot]slyngbaek
andauthored
Merge #3587
3587: Use WorkDoneProgress LSP API for initial load r=matklad a=slyngbaek 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) Co-authored-by: Steffen Lyngbaek <steffenlyngbaek@gmail.com>
2 parents 2eb1677 + b6dec2e commit 5bdc249

File tree

2 files changed

+77
-11
lines changed

2 files changed

+77
-11
lines changed

crates/rust-analyzer/src/main_loop.rs

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ use std::{
1616

1717
use crossbeam_channel::{select, unbounded, RecvError, Sender};
1818
use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
19-
use lsp_types::{ClientCapabilities, NumberOrString};
19+
use lsp_types::{
20+
ClientCapabilities, NumberOrString, WorkDoneProgress, WorkDoneProgressBegin,
21+
WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport,
22+
};
2023
use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask};
2124
use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId};
2225
use ra_prof::profile;
@@ -330,6 +333,7 @@ struct LoopState {
330333
in_flight_libraries: usize,
331334
pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>,
332335
workspace_loaded: bool,
336+
roots_scanned_progress: Option<usize>,
333337
}
334338

335339
impl LoopState {
@@ -429,17 +433,15 @@ fn loop_turn(
429433
&& loop_state.in_flight_libraries == 0
430434
{
431435
loop_state.workspace_loaded = true;
432-
let n_packages: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum();
433-
if world_state.feature_flags.get("notifications.workspace-loaded") {
434-
let msg = format!("workspace loaded, {} rust packages", n_packages);
435-
show_message(req::MessageType::Info, msg, &connection.sender);
436-
}
437436
world_state.check_watcher.update();
438437
pool.execute({
439438
let subs = loop_state.subscriptions.subscriptions();
440439
let snap = world_state.snapshot();
441440
move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
442441
});
442+
send_startup_progress(&connection.sender, loop_state, world_state);
443+
} else if !loop_state.workspace_loaded {
444+
send_startup_progress(&connection.sender, loop_state, world_state);
443445
}
444446

445447
if state_changed {
@@ -704,6 +706,65 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state:
704706
}
705707
}
706708

709+
fn send_startup_progress(
710+
sender: &Sender<Message>,
711+
loop_state: &mut LoopState,
712+
world_state: &WorldState,
713+
) {
714+
if !world_state.feature_flags.get("notifications.workspace-loaded") {
715+
return;
716+
}
717+
718+
let total: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum();
719+
let prev_progress = loop_state.roots_scanned_progress;
720+
let progress = total - world_state.roots_to_scan;
721+
loop_state.roots_scanned_progress = Some(progress);
722+
723+
match (prev_progress, loop_state.workspace_loaded) {
724+
(None, false) => {
725+
let work_done_progress_create = request_new::<req::WorkDoneProgressCreate>(
726+
loop_state.next_request_id(),
727+
WorkDoneProgressCreateParams {
728+
token: req::ProgressToken::String("rustAnalyzer/startup".into()),
729+
},
730+
);
731+
sender.send(work_done_progress_create.into()).unwrap();
732+
send_startup_progress_notif(
733+
sender,
734+
WorkDoneProgress::Begin(WorkDoneProgressBegin {
735+
title: "rust-analyzer".into(),
736+
cancellable: None,
737+
message: Some(format!("{}/{} packages", progress, total)),
738+
percentage: Some(100.0 * progress as f64 / total as f64),
739+
}),
740+
);
741+
}
742+
(Some(prev), false) if progress != prev => send_startup_progress_notif(
743+
sender,
744+
WorkDoneProgress::Report(WorkDoneProgressReport {
745+
cancellable: None,
746+
message: Some(format!("{}/{} packages", progress, total)),
747+
percentage: Some(100.0 * progress as f64 / total as f64),
748+
}),
749+
),
750+
(_, true) => send_startup_progress_notif(
751+
sender,
752+
WorkDoneProgress::End(WorkDoneProgressEnd {
753+
message: Some(format!("rust-analyzer loaded, {} packages", progress)),
754+
}),
755+
),
756+
_ => {}
757+
}
758+
}
759+
760+
fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) {
761+
let notif = notification_new::<req::Progress>(req::ProgressParams {
762+
token: req::ProgressToken::String("rustAnalyzer/startup".into()),
763+
value: req::ProgressParamsValue::WorkDone(work_done_progress),
764+
});
765+
sender.send(notif.into()).unwrap();
766+
}
767+
707768
struct PoolDispatcher<'a> {
708769
req: Option<Request>,
709770
pool: &'a ThreadPool,

crates/rust-analyzer/tests/heavy_tests/support.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ use lsp_types::{
1212
notification::{DidOpenTextDocument, Exit},
1313
request::Shutdown,
1414
ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities,
15-
TextDocumentIdentifier, TextDocumentItem, Url,
15+
TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress,
1616
};
1717
use serde::Serialize;
1818
use serde_json::{to_string_pretty, Value};
1919
use tempfile::TempDir;
2020
use test_utils::{find_mismatch, parse_fixture};
2121

22+
use req::{ProgressParams, ProgressParamsValue};
2223
use rust_analyzer::{main_loop, req, ServerConfig};
2324

2425
pub struct Project<'a> {
@@ -201,10 +202,14 @@ impl Server {
201202
}
202203
pub fn wait_until_workspace_is_loaded(&self) {
203204
self.wait_for_message_cond(1, &|msg: &Message| match msg {
204-
Message::Notification(n) if n.method == "window/showMessage" => {
205-
let msg =
206-
n.clone().extract::<req::ShowMessageParams>("window/showMessage").unwrap();
207-
msg.message.starts_with("workspace loaded")
205+
Message::Notification(n) if n.method == "$/progress" => {
206+
match n.clone().extract::<ProgressParams>("$/progress").unwrap() {
207+
ProgressParams {
208+
token: req::ProgressToken::String(ref token),
209+
value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)),
210+
} if token == "rustAnalyzer/startup" => true,
211+
_ => false,
212+
}
208213
}
209214
_ => false,
210215
})

0 commit comments

Comments
 (0)