Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit 870d294

Browse files
authored
Merge pull request #653 from algesten/progress-params
window/progress notification
2 parents 3b62d15 + 8bab552 commit 870d294

File tree

11 files changed

+446
-160
lines changed

11 files changed

+446
-160
lines changed

contributing.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -306,14 +306,14 @@ The RLS uses some custom extensions to the Language Server Protocol.
306306
These are all sent from the RLS to an LSP client and are only used to improve
307307
the user experience by showing progress indicators.
308308

309-
310-
* `rustDocument/beginBuild`: notification, no arguments. Sent before a
311-
build starts.
312-
* `rustDocument/diagnosticsBegin`: notification, no arguments. Sent before
313-
indexing or any diagnostics from a build are sent (build is likely in progress).
314-
* `rustDocument/diagnosticsEnd`: notification, no arguments. Sent when a build
315-
is complete (successfully or not, or even skipped) and all post-build analysis
316-
by the RLS is complete.
309+
* `window/progress`: notification, `title: "Build"`. Sent before build starts.
310+
* `window/progress`: notification with `title: "Build"`, repeated for each compile target.
311+
* When total amount of work is not known, has field `message` set to the current crate name.
312+
* When total amount of work is known, has field `percentage` set to how much of build has started.
313+
* `window/progress`: notification, `title: "Build"`, `"done": true`. Sent when build ends.
314+
* `window/progress`: notification, `title: "Diagnostics"`. Sent before analysis of build starts.
315+
* ... standard LSP `publishDiagnostics`
316+
* `window/progress`: notification, `title: "Diagnostics"`, `"done": true`. Sent when analysis ends.
317317

318318
#### LSP Client to RLS
319319

src/actions/mod.rs

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ use url::Url;
2121
use span;
2222
use Span;
2323

24-
use actions::post_build::{BuildResults, PostBuildHandler, Notifier};
24+
use actions::post_build::{BuildResults, PostBuildHandler};
25+
use actions::progress::{BuildProgressNotifier, BuildDiagnosticsNotifier};
2526
use build::*;
2627
use lsp_data;
2728
use lsp_data::*;
28-
use lsp_data::notification::{PublishDiagnostics, ShowMessage};
29-
use server::{Output, Notification};
29+
use server::Output;
3030

3131
use std::collections::HashMap;
3232
use std::path::{Path, PathBuf};
@@ -57,6 +57,7 @@ pub mod work_pool;
5757
pub mod post_build;
5858
pub mod requests;
5959
pub mod notifications;
60+
pub mod progress;
6061

6162
/// Persistent context shared across all requests and notifications.
6263
pub enum ActionContext {
@@ -214,29 +215,6 @@ impl InitActionContext {
214215
}
215216

216217
fn build<O: Output>(&self, project_path: &Path, priority: BuildPriority, out: &O) {
217-
struct BuildNotifier<O: Output> {
218-
out: O,
219-
active_build_count: Arc<AtomicUsize>,
220-
}
221-
222-
impl<O: Output> Notifier for BuildNotifier<O> {
223-
fn notify_begin(&self) {
224-
self.out.notify(Notification::<DiagnosticsBegin>::new(()));
225-
}
226-
fn notify_end(&self) {
227-
self.active_build_count.fetch_sub(1, Ordering::SeqCst);
228-
self.out.notify(Notification::<DiagnosticsEnd>::new(()));
229-
}
230-
fn notify_publish(&self, params: PublishDiagnosticsParams) {
231-
self.out.notify(Notification::<PublishDiagnostics>::new(params));
232-
}
233-
fn notify_error(&self, msg: &str) {
234-
self.out.notify(Notification::<ShowMessage>::new(lsp_data::ShowMessageParams {
235-
typ: lsp_data::MessageType::Error,
236-
message: msg.to_owned(),
237-
}));
238-
}
239-
}
240218

241219
let pbh = {
242220
let config = self.config.lock().unwrap();
@@ -246,19 +224,18 @@ impl InitActionContext {
246224
project_path: project_path.to_owned(),
247225
show_warnings: config.show_warnings,
248226
shown_cargo_error: self.shown_cargo_error.clone(),
227+
active_build_count: self.active_build_count.clone(),
249228
use_black_list: config.use_crate_blacklist,
250-
notifier: Box::new(BuildNotifier {
251-
out: out.clone(),
252-
active_build_count: self.active_build_count.clone(),
253-
}),
229+
notifier: Box::new(BuildDiagnosticsNotifier::new(out.clone())),
254230
blocked_threads: vec![],
255231
}
256232
};
257233

258-
out.notify(Notification::<BeginBuild>::new(()));
234+
let notifier = Box::new(BuildProgressNotifier::new(out.clone()));
235+
259236
self.active_build_count.fetch_add(1, Ordering::SeqCst);
260237
self.build_queue
261-
.request_build(project_path, priority, pbh);
238+
.request_build(project_path, priority, notifier, pbh);
262239
}
263240

264241
fn build_current_project<O: Output>(&self, priority: BuildPriority, out: &O) {
@@ -392,7 +369,6 @@ impl<'ctx> FileWatch<'ctx> {
392369
}
393370
}
394371

395-
396372
#[cfg(test)]
397373
mod test {
398374
use super::*;

src/actions/post_build.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515
use std::collections::HashMap;
1616
use std::path::{Path, PathBuf};
1717
use std::sync::{Arc, Mutex};
18-
use std::sync::atomic::{AtomicBool, Ordering};
18+
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1919
use std::thread;
2020

2121
use build::BuildResult;
2222
use lsp_data::{ls_util, PublishDiagnosticsParams};
23+
use actions::progress::DiagnosticsNotifier;
2324

2425
use analysis::AnalysisHost;
2526
use data::Analysis;
@@ -37,28 +38,20 @@ pub struct PostBuildHandler {
3738
pub show_warnings: bool,
3839
pub use_black_list: bool,
3940
pub shown_cargo_error: Arc<AtomicBool>,
40-
pub notifier: Box<Notifier>,
41+
pub active_build_count: Arc<AtomicUsize>,
42+
pub notifier: Box<DiagnosticsNotifier>,
4143
pub blocked_threads: Vec<thread::Thread>,
4244
}
4345

44-
/// Trait for communication back to the rest of the RLS (and on to the client).
45-
// This trait only really exists to work around the object safety rules (Output
46-
// is not object-safe).
47-
pub trait Notifier: Send {
48-
fn notify_begin(&self);
49-
fn notify_end(&self);
50-
fn notify_publish(&self, PublishDiagnosticsParams);
51-
fn notify_error(&self, &str);
52-
}
5346

5447
impl PostBuildHandler {
5548
pub fn handle(mut self, result: BuildResult) {
56-
self.notifier.notify_begin();
5749

5850
match result {
5951
BuildResult::Success(cwd, messages, new_analysis, _) => {
6052
thread::spawn(move || {
6153
trace!("build - Success");
54+
self.notifier.notify_begin_diagnostics();
6255

6356
// Emit appropriate diagnostics using the ones from build.
6457
self.handle_messages(&cwd, &messages);
@@ -71,26 +64,33 @@ impl PostBuildHandler {
7164
self.reload_analysis_from_memory(&cwd, new_analysis);
7265
}
7366

67+
// the end message must be dispatched before waking up
68+
// the blocked threads, or we might see "done":true message
69+
// first in the next action invocation.
70+
self.notifier.notify_end_diagnostics();
71+
7472
// Wake up any threads blocked on this analysis.
7573
for t in self.blocked_threads.drain(..) {
7674
t.unpark();
7775
}
7876

79-
self.notifier.notify_end();
8077
self.shown_cargo_error.store(false, Ordering::SeqCst);
78+
self.active_build_count.fetch_sub(1, Ordering::SeqCst);
8179
});
8280
}
8381
BuildResult::Squashed => {
8482
trace!("build - Squashed");
85-
self.notifier.notify_end();
83+
self.active_build_count.fetch_sub(1, Ordering::SeqCst);
8684
}
8785
BuildResult::Err(cause, cmd) => {
8886
trace!("build - Error {} when running {:?}", cause, cmd);
87+
self.notifier.notify_begin_diagnostics();
8988
if !self.shown_cargo_error.swap(true, Ordering::SeqCst) {
9089
let msg = format!("There was an error trying to build, RLS features will be limited: {}", cause);
91-
self.notifier.notify_error(&msg);
90+
self.notifier.notify_error_diagnostics(&msg);
9291
}
93-
self.notifier.notify_end();
92+
self.notifier.notify_end_diagnostics();
93+
self.active_build_count.fetch_sub(1, Ordering::SeqCst);
9494
}
9595
}
9696
}
@@ -165,7 +165,7 @@ impl PostBuildHandler {
165165
.collect(),
166166
};
167167

168-
self.notifier.notify_publish(params);
168+
self.notifier.notify_publish_diagnostics(params);
169169
}
170170
}
171171
}

src/actions/progress.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::sync::atomic::{AtomicUsize, Ordering};
12+
13+
use lsp_data::{ProgressParams, PublishDiagnosticsParams, Progress, ShowMessageParams, MessageType};
14+
use server::{Output, Notification};
15+
use ls_types::notification::{PublishDiagnostics, ShowMessage};
16+
17+
/// Trait for communication of build progress back to the client.
18+
pub trait ProgressNotifier: Send {
19+
fn notify_begin_progress(&self);
20+
fn notify_progress(&self, update: ProgressUpdate);
21+
fn notify_end_progress(&self);
22+
}
23+
24+
/// Kinds of progress updates
25+
pub enum ProgressUpdate {
26+
Message(String),
27+
Percentage(f64),
28+
}
29+
30+
/// Trait for communication of diagnostics (i.e. build results) back to the rest of
31+
/// the RLS (and on to the client).
32+
// This trait only really exists to work around the object safety rules (Output
33+
// is not object-safe).
34+
pub trait DiagnosticsNotifier: Send {
35+
fn notify_begin_diagnostics(&self);
36+
fn notify_publish_diagnostics(&self, PublishDiagnosticsParams);
37+
fn notify_end_diagnostics(&self);
38+
fn notify_error_diagnostics(&self, msg: &str);
39+
}
40+
41+
/// Generate a new progress params with a unique ID and the given title.
42+
fn new_progress_params(title: String) -> ProgressParams {
43+
44+
// counter to generate unique ID for each chain-of-progress notifications.
45+
lazy_static! {
46+
static ref PROGRESS_ID_COUNTER: AtomicUsize = {
47+
AtomicUsize::new(0)
48+
};
49+
}
50+
51+
ProgressParams {
52+
id: format!("progress_{}", PROGRESS_ID_COUNTER.fetch_add(1, Ordering::SeqCst)),
53+
title: Some(title),
54+
message: None,
55+
percentage: None,
56+
done: None,
57+
}
58+
}
59+
60+
/// Notifier of progress for the build (window/progress notifications).
61+
/// the same instance is used for the entirety of one single build.
62+
pub struct BuildProgressNotifier<O: Output> {
63+
out: O,
64+
// these params are used as a template and are cloned for each
65+
// message that is actually notified.
66+
progress_params: ProgressParams,
67+
}
68+
69+
impl<O: Output> BuildProgressNotifier<O> {
70+
pub fn new(out: O) -> BuildProgressNotifier<O> {
71+
BuildProgressNotifier {
72+
out,
73+
progress_params: new_progress_params("Build".into()),
74+
}
75+
}
76+
}
77+
78+
impl<O: Output> ProgressNotifier for BuildProgressNotifier<O> {
79+
fn notify_begin_progress(&self) {
80+
let params = self.progress_params.clone();
81+
self.out.notify(Notification::<Progress>::new(params));
82+
}
83+
fn notify_progress(&self, update: ProgressUpdate) {
84+
let mut params = self.progress_params.clone();
85+
match update {
86+
ProgressUpdate::Message(s) => params.message = Some(s),
87+
ProgressUpdate::Percentage(p) => params.percentage = Some(p),
88+
}
89+
self.out.notify(Notification::<Progress>::new(params));
90+
}
91+
fn notify_end_progress(&self) {
92+
let mut params = self.progress_params.clone();
93+
params.done = Some(true);
94+
self.out.notify(Notification::<Progress>::new(params));
95+
}
96+
}
97+
98+
99+
/// Notifier of diagnostics after the build has completed.
100+
pub struct BuildDiagnosticsNotifier<O: Output> {
101+
out: O,
102+
// these params are used as a template and are cloned for each
103+
// message that is actually notified.
104+
progress_params: ProgressParams,
105+
}
106+
107+
impl<O: Output> BuildDiagnosticsNotifier<O> {
108+
pub fn new(out: O) -> BuildDiagnosticsNotifier<O> {
109+
BuildDiagnosticsNotifier {
110+
out,
111+
progress_params: new_progress_params("Diagnostics".into()),
112+
}
113+
}
114+
}
115+
116+
impl<O: Output> DiagnosticsNotifier for BuildDiagnosticsNotifier<O> {
117+
fn notify_begin_diagnostics(&self) {
118+
let params = self.progress_params.clone();
119+
self.out.notify(Notification::<Progress>::new(params));
120+
}
121+
fn notify_publish_diagnostics(&self, params: PublishDiagnosticsParams) {
122+
self.out.notify(Notification::<PublishDiagnostics>::new(params));
123+
}
124+
fn notify_end_diagnostics(&self) {
125+
let mut params = self.progress_params.clone();
126+
params.done = Some(true);
127+
self.out.notify(Notification::<Progress>::new(params));
128+
}
129+
fn notify_error_diagnostics(&self, msg: &str) {
130+
self.out.notify(Notification::<ShowMessage>::new(ShowMessageParams {
131+
typ: MessageType::Error,
132+
message: msg.to_owned(),
133+
}));
134+
}
135+
}

0 commit comments

Comments
 (0)