-
Notifications
You must be signed in to change notification settings - Fork 255
Conversation
There are a bunch of things I need some feedback on. I'll write code comments. |
Cargo.toml
Outdated
@@ -14,7 +14,7 @@ cargo = { git = "https://github.com/rust-lang/cargo" } | |||
env_logger = "0.4" | |||
failure = "0.1.1" | |||
jsonrpc-core = "8.0.1" | |||
languageserver-types = "0.16" | |||
languageserver-types = { git = "https://github.com/algesten/languageserver-types", branch="progress-params-016" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is because the latest languageserver-types
needs some work to integrate. I've done this PR against languageserver-types master.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, given that it might take a while for the next version of the LSP to be released, I would not wait for that and instead use a custom message with an issue and note to use this feature if/when it gets released
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes for now can be put inside lsp_data.rs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright. I make a local version.
src/actions/mod.rs
Outdated
} | ||
|
||
ProgressParams { | ||
id: format!("progress_{}", ID_COUNTER.fetch_add(1, Ordering::SeqCst)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this ID format enough? Or do we need it to be globally unique, maybe introduce uuid
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are the ids designed to be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I understand they're just to tie together multiple progress notices in one long chain – they have the same ID. Whether or not that ID needs to be globally unique I'm not sure.
src/actions/post_build.rs
Outdated
fn notify_end(&self); | ||
fn notify_publish(&self, PublishDiagnosticsParams); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reused this trait to now have notifications for both diagnostics and progress. I've renamed/reorded to make it clear what is what. However it got a bit messier than I wanted. For instance, I left the notifier implementation reference in the PostBuildHandler
, which means to notify progress we need a reference to pbh
in places like cargo
, which isn't great.
Maybe separate into two traits?
The order of the current ones is:
notify_begin
starts progressnotify_progress
... repeat for each updatenotify_begin_diagnostics
notify_publish_diagnostics
notify_end
(ends both diagnostics and progress)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is unfortunate how much the pbh
has to be propagated and would be nice to avoid that, but I don't see how we can do that.
src/actions/post_build.rs
Outdated
|
||
pub enum ProgressUpdate { | ||
Message(String), | ||
Percentage(f64), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially I imagined sending a message of what is being built also when we do a percentage. However it seems we can only send either a message OR a percentage. microsoft/vscode-languageserver-node#261 (comment)
src/build/cargo.rs
Outdated
|
||
if let Err(err) = plan.emplace_dep_with_filter(&unit, &cx, &only_primary) { | ||
if let Err(err) = plan.emplace_dep_with_filter(&unit, &cx, &is_primary) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. This change should be removed...
src/build/cargo.rs
Outdated
let progress_sender = self.progress_sender.lock().unwrap(); | ||
progress_sender.send(ProgressUpdate::Message(format!("{} {}", crate_name, target_name))) | ||
.expect("Failed to send progress update"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Progress in the first (non-cached) build sends messages on the form <crate> <source>
, i.e. infer_lib src/lib.rs
. Is this a good idea?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just the crate name should be enough - the source path is usually obvious from the crate
src/build/mod.rs
Outdated
let pbh = build.pbh; | ||
|
||
// window/progress notification that we are about to build | ||
pbh.notifier.notify_begin(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I said above, I guess it's not wonderful the notifier trait impl hangs off pbh
for progress updates...
It strikes me we don't have any tests for the cached build too see the percentage progress. Would be great to base it on Any pointers on how to do such a test? |
Thanks for working on this !
A few notes:
|
@sebastiencs Thank you!!!! That's super useful feedback. The 10 second delay and then all messages at once means I didn't get the threads/messaging right. I fix. Regarding %, the problem is that first build, RLS delegates the entire build to cargo, and doesn't get an easy way to calculate how many files are left to do. So my first stab just reports which file is being done. However the format with full path is not good. It would be interesting to know whether you get percentage on the second build, when the deps are cached and RLS runs rustc directly. Again thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When we send the last progress update, should we include some message that we're starting the analysis phase?
Cargo.toml
Outdated
@@ -14,7 +14,7 @@ cargo = { git = "https://github.com/rust-lang/cargo" } | |||
env_logger = "0.4" | |||
failure = "0.1.1" | |||
jsonrpc-core = "8.0.1" | |||
languageserver-types = "0.16" | |||
languageserver-types = { git = "https://github.com/algesten/languageserver-types", branch="progress-params-016" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, given that it might take a while for the next version of the LSP to be released, I would not wait for that and instead use a custom message with an issue and note to use this feature if/when it gets released
src/actions/mod.rs
Outdated
} | ||
|
||
ProgressParams { | ||
id: format!("progress_{}", ID_COUNTER.fetch_add(1, Ordering::SeqCst)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are the ids designed to be used?
src/build/cargo.rs
Outdated
let progress_sender = self.progress_sender.lock().unwrap(); | ||
progress_sender.send(ProgressUpdate::Message(format!("{} {}", crate_name, target_name))) | ||
.expect("Failed to send progress update"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just the crate name should be enough - the source path is usually obvious from the crate
src/build/cargo.rs
Outdated
@@ -627,10 +650,19 @@ pub fn make_cargo_config(build_dir: &Path, | |||
config | |||
} | |||
|
|||
fn parse_arg(args: &[OsString], arg: &str) -> Option<String> { | |||
fn parse_arg(args: &Vec<&str>, arg: &str) -> Option<String> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer &[&str]
to &Vec<&str>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
src/actions/post_build.rs
Outdated
fn notify_end(&self); | ||
fn notify_publish(&self, PublishDiagnosticsParams); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is unfortunate how much the pbh
has to be propagated and would be nice to avoid that, but I don't see how we can do that.
@nrc @Xanewok executive summary:
@sebastiencs if you got time to try again, I believe the updates should arrive in a timely fashion (not 10 seconds later). |
It’s great to see it working on a real-life example, good job! |
Oh wow @sebastiencs THANKS! That's so cool to see! I will remove the @Xanewok happy to remove those events. I do that. |
@Xanewok the current event order (as can be seen in the tests) is:
When I remove those events, do you expect the order to be: I.e. the final |
After chatting to @Xanewok on IRC, I've replaced the RLS specific Each build will result in two sequential "progress-chains", first one with
|
The two chains will have unique |
src/build/plan.rs
Outdated
@@ -352,7 +358,19 @@ impl JobQueue { | |||
.map(|x| x.into_string().unwrap()) | |||
.collect(); | |||
|
|||
args.insert(0, job.get_program().clone().into_string().unwrap()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this line split into let program = ...
and args.insert(0, program);
with progress update in between?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a remnant from when I sent the program
also as part of the percentage message in a previous iteration. I'll restore it as it was.
I want to do a git rebase once we've iterated this enough to make the commit log more sensible.
@@ -76,16 +76,16 @@ impl PostBuildHandler { | |||
t.unpark(); | |||
} | |||
|
|||
self.notifier.notify_end(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can call self.notifier.notify_begin_diagnostics()
self.notifier.notify_end_diagnostics()
only here, in case of BuildResult::Success
. Since the diagnosticsBegin/End changed meaning some time ago and only indicate indexing operation progress, I think it's fair only to do that when we have something meaningful to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I fix.
src/lsp_data.rs
Outdated
|
||
// Optional progress message to display. | ||
// If unset, the previous progress message (if any) is still valid. | ||
pub message: Option<String>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be good to add #[serde(skip_serializing_if = "Option::is_none")]
here not to send unset/irrelevant values to client
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough
src/lsp_data.rs
Outdated
|
||
// Optional progress percentage to display. | ||
// If unset, the previous progress percentage (if any) is still valid. | ||
pub percentage: Option<f64>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
src/lsp_data.rs
Outdated
|
||
// Set to true on the final progress update. | ||
// No more progress notifications with the same ID should be sent. | ||
pub done: Option<bool>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And here
I can't shake off a feeling that there's a simpler solution waiting to come out in terms of how we pass and manage progress senders, but I can't come up with anything better, so I'll stick with that. In #654 we also introduced integration tests and the tests themselves are at tests/tests.rs. Could you also modify those to reflect the changes made? (as CI is failing because of it) |
@Xanewok that's done. |
aaf03bf
to
658d0f8
Compare
macOS job is canceled, retrying (there's been some troubles with macOS CI lately so it'd be good to see it greenlit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests now pass and it's working as intended, however I believe it'd be good to separate introduced traits and implementations to a separate, common place.
src/actions/mod.rs
Outdated
struct BuildNotifier<O: Output> { | ||
|
||
lazy_static! { | ||
static ref PROGRESS_ID_COUNTER: AtomicUsize = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the counter, along with traits implementations using it would benefit from separating into a common file src/actions/progress.rs
?
src/actions/mod.rs
Outdated
}; | ||
} | ||
|
||
fn new_progress_params(id: String, title: &str) -> ProgressParams { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above, I believe this should also be moved together with PROGRESS_ID_COUNTER
. When we use this helper function we always generate a new ID atomically, so I'd suggest doing that inside the function instead (so we wouldn't need id: String
parameter) and accept title: String
if we always take ownership of it
src/actions/mod.rs
Outdated
fn notify_begin(&self) { | ||
self.out.notify(Notification::<DiagnosticsBegin>::new(NoParams {})); | ||
// base progress parameters for notification of the analysis. | ||
let diganostics_progress_params = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I would avoid interleaving the type definitions and implementations with variable definitions like done here (struct {...} let = {...}; impl {...}
) for clarity, but with moving the traits and/or impls elsewhere, this will no longer be a problem here
src/actions/mod.rs
Outdated
self.out.notify(Notification::<DiagnosticsBegin>::new(NoParams {})); | ||
// base progress parameters for notification of the analysis. | ||
let diganostics_progress_params = { | ||
let id = format!("diagnostics_{}", PROGRESS_ID_COUNTER.fetch_add(1, Ordering::SeqCst)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re generating ID inside fn new_progress_params
, I'd stick to just generating a number, since it's more of a implementation detail and we already distinguish using progress title
s
src/actions/mod.rs
Outdated
out: O, | ||
active_build_count: Arc<AtomicUsize>, | ||
progress_params: ProgressParams, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is always used as a template that's cloned for each message, so it'd be good to either add a doc comment explaining that or storing only relevant progress ID here
src/actions/mod.rs
Outdated
} | ||
|
||
// notifier of progress for the build (window/progress notifications) | ||
struct BuildProgressNotifier<O: Output> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly to BuildDiagnosticsNotifier
, this would probably benefit from moving it together to a separate place
src/actions/mod.rs
Outdated
@@ -356,6 +421,16 @@ impl<'ctx> FileWatch<'ctx> { | |||
} | |||
} | |||
|
|||
pub trait ProgressNotifier: Send { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These defs would also need moving
src/actions/post_build.rs
Outdated
fn notify_begin(&self); | ||
fn notify_end(&self); | ||
fn notify_publish(&self, PublishDiagnosticsParams); | ||
pub trait DiagnosticsNotifier: Send { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with this trait
src/build/plan.rs
Outdated
// FIXME. We could communicate the "program" being built here, but | ||
// it seems window/progress notification should have message OR percentage. | ||
{ | ||
let percentage = compiler_messages.len() as f64 / self.0.len() as f64; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're dividing by vector length here, it also might be a good idea to change the above assert!(self.0.is_empty() == false);
to equivalent assert!(self.0.len() > 0)
to better see that it's not 0 at a quick glance
tests/tests.rs
Outdated
@@ -44,10 +44,12 @@ fn test_infer_bin() { | |||
|
|||
rls.expect_messages(&[ | |||
ExpectedMessage::new(Some(0)).expect_contains("capabilities"), | |||
ExpectedMessage::new(None).expect_contains("beginBuild"), | |||
ExpectedMessage::new(None).expect_contains("diagnosticsBegin"), | |||
ExpectedMessage::new(None).expect_contains("progress").expect_contains("Build"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really important, but could we change it to check against "title": "Build"
for clarity? (and for others below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this and for quick iterations! 🙂
I'm happy with what we currently have here. Since we have used rustDocument/diagnostics{Begin, End}
for a long time now and the change, despite being conceptually simple, is rather significant, I'd also like for @nrc to re-review it.
}); | ||
} | ||
BuildResult::Squashed => { | ||
trace!("build - Squashed"); | ||
self.notifier.notify_end(); | ||
self.active_build_count.fetch_sub(1, Ordering::SeqCst); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like we shouldn't have to manually decrease the count here (possibly by passing the guard value only in BuildResult::Success
?), but that's outside of the scope of the PR now.
/// the RLS (and on to the client). | ||
// This trait only really exists to work around the object safety rules (Output | ||
// is not object-safe). | ||
pub trait DiagnosticsNotifier: Send { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One idea to deduplicate the traits and impl logic might be to introduce a RAII guard that both ensures that we have an active windows/progress
session that we finish it with "done": true
, which also exposes a way to report specific progress for it (be it publishing diagnostics or sending a progress message), but that'd be mostly exploratory - I'm fine with what we currently have here.
@algesten I'm afraid another PR caused a merge conflict - could you please resolve those and rebase this? |
I fix later. |
3719158
to
1d404c3
Compare
All looks good to me! @algesten sorry, this needs rebasing again. Could you ping me or Xanewok when you do so we can merge please (GitHub doesn't notify on rebases). |
I fix |
1d404c3
to
8ab908b
Compare
src/test/mod.rs
Outdated
.expect_contains(r#"[{"language":"rust","value":"&str"}]"#), | ||
ExpectedMessage::new(None).expect_contains("contents"), | ||
ExpectedMessage::new(None).expect_contains("progress").expect_contains(r#"title":"Diagnostics""#), | ||
// XXX why don't we get this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rebase is done. But I couldn't figure out why this test changed. I might have broken something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there another progress message which appears before the hover message? Could you post the actual and expected output as produced by the test please?
@nrc that's done. one problem, see comment above. |
8ab908b
to
0529d91
Compare
@nrc that test error was indeed a bug that is now fixed. now it's failing on |
but then master doesn't have this issue. so. sigh. i fix. |
@nrc there. i think we're set to land this. tests are passing 🎉 |
65b1747
to
8bab552
Compare
i686 panicked on one test, retriggering CI to see if it's spurious
|
Looks like it was spurious. Merged! Thanks for working on this @algesten ! |
🎉 |
Attempt to implement
window/progress
notifications.Fixes #377