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

Infer appropriate crate target from workspace #438

Merged
merged 2 commits into from
Aug 15, 2017

Conversation

Xanewok
Copy link
Member

@Xanewok Xanewok commented Aug 10, 2017

This is only a proof-of-concept and needs some ironing out. Most importantly it'd be good to handle properly inferred/default and explicitly stated values when updating the config via LSP message. Additionally, it'd be good not to duplicate code that creates a Workspace, although I'm not really sure how to do that, as Workspace needs to be created with borrows and I'm not sure how to cleanly separate a struct that would hold the data and Workspace without running into self-borrowing issues when initializing.

Right now it works like this: On RLS initialize, call infer_config_defaults which creates a Workspace and queries appropriate package's targets to see which crate targets are available (including inferred lib and bin ones). Then, these are set as follows: prioritize lib over bin, and prefer default bin over a first bin on the list. These are just mindlessly copied into current config and during config change, we just ignore those (which we shouldn't, it needs work).

Everything marked with TODO: I'd like to resolve first before merging.

@nrc
Copy link
Member

nrc commented Aug 11, 2017

So for inferred/default stuff, here are some thoughts: just to make sure, let's only do this where we need to (which is build_lib/bin for now, I think). I think then we want to split the config struct so we have a RawConfig which we can deserialise from LSP and then the Config we use internally. The raw one doesn't have the inferred/whatever thing. Then use a generic enum for build_bin, etc. - I think you need Specified/Inferred/None (where the last is used when we haven't inferred yet, or can't be inferred), then have an update method which takes the supplied input. This works for build_bin, but not build_lib, I think. You'll need to change the type of build_lib to Option and change the default in VSCode to null. Otherwise you don't know whether the user is using the default or not.

Copy link
Member

@nrc nrc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all the Cargo stuff looks fine, and generally this is a good approach, I think the only thing that needs work is the Config for Inferred vs Default and merging configs.

@@ -92,6 +98,10 @@ impl ActionHandler {
pub fn init<O: Output>(&self, init_options: InitializationOptions, out: O) {
trace!("init: {:?}", init_options);

if let Err(err) = self.infer_config_defaults(&mut *self.config.lock().unwrap()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we spawn a thread to do this, rather than doing it on the main thread?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tried to catch every Err and surface it out so we won't get a panic there (updated now, got rid of the .expect() and .unwrap()). Should I still spawn a new thread for that here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So my concern was that this might take a long time to run and block other main thread options, rather than about error handling. Given that we're doing a bunch of disk access, I think it is probably still too long running a task to be on the main thread.

@Xanewok
Copy link
Member Author

Xanewok commented Aug 13, 2017

Updated, added new test cases for inferring crate target types. Introduced Inferrable::{Specified(T), Inferred(T), None as per comment.
One minor caveat is that it'd be good to run infer_config_defaults when specifying options back to None via config, as these won't be downgraded from Specified variant with current implementation. Should I add that?

Copy link
Member

@nrc nrc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! A couple of nits inline.

src/config.rs Outdated
}

/// Deserialize as if it's `Option<T>` and use `None` variant if it's `None`,
/// otherwise use `Specified` variant for deserialized value.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think this should be a regular comment, not a doc comment

src/config.rs Outdated
match self {
&Inferrable::Inferred(ref value) |
&Inferrable::Specified(ref value) => value,
&Inferrable::None => unreachable!(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably expect here so we get a useful message

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, I wonder that we might hit this in real life - either because inference has not completed yet, or the user resets to null. In which case we should probably be able to continue without crashing - either just skip the build or build with some defaults or something

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently it's impossible to reset the config value to None, it's only a way to not get Specified value when deserializing. However if user desires to fall back to auto-detecting, we'd still have to run the infer_... again after config change, which isn't done yet.
I made an assumption, that all values (including default ones, which I set to Inferred) should not ever be None, but I have a hard time trying to enforce that at compile-time. Splitting the config into deserialized/actually used would work, but we'd lose compile-time guarantee that we copy every value and would have to duplicate a lot of code or write less straightforward macro, that's why I went with this approach. How should I tackle this?

@nrc
Copy link
Member

nrc commented Aug 14, 2017

One minor caveat is that it'd be good to run infer_config_defaults when specifying options back to None via config, as these won't be downgraded from Specified variant with current implementation. Should I add that?

Yes please.

On RLS initialize, call `infer_config_defaults` which creates a `Workspace` and queries appropriate package's targets to see which crate targets are available (including inferred `lib` and `bin` ones).
Whenever received changed config has some `null` values (most probably the user wants to use inferred values again), set them to the default values and run `infer_config_defaults` later as well.
@@ -812,6 +850,76 @@ impl ActionHandler {
}
}

fn infer_config_defaults(project_dir: &Path, config: &mut Config) -> CargoResult<()> {
Copy link
Member Author

@Xanewok Xanewok Aug 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in an impl Config? This would need more Cargo-related bits moved to config.rs, not sure if it's good from in terms of the module split/architecture

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think I would expect this to be in the cargo module and probably a method on Config. Taking a mut ref to Config is a bit strange, I guess I would expect to take a Config and return a new one? And that might make some of the referencing calling this easier? Anyway, I'm going to merge this now, we can refactor later if you like because I think this is polish.

@Xanewok
Copy link
Member Author

Xanewok commented Aug 14, 2017

Addressed every point, rebased and force-pushed now. Is spawning a thread here and here is okay?

@nrc nrc merged commit 2e5ba48 into rust-lang:master Aug 15, 2017
@Xanewok
Copy link
Member Author

Xanewok commented Aug 15, 2017

I guess it'd be also good to introduce a test case to detect inference user sends a DidChangeConfiguration with a null option, I can do that a bit later.
Thanks for merging!

@Xanewok Xanewok deleted the infer-targets branch August 15, 2017 06:49
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants