-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deeper rust-analyzer Integration with Non-Cargo Build Systems #13446
Comments
Does buck allow passing |
It does. At the moment, IDE-specific diagnostics are written to a deterministic folder in the output directory, and while I think that I'd need to convince the Buck team to revisit their stance on writing to, e.g., |
What exactly do you mean here? You can already fully overwrite the command that is being executed by flycheck (the flycheck code just mentions cargo everywhere still even though its really just some command with a specific json output at this point)
|
I stand corrected! I just assumed... that it didn't work. I'm seeing rust-analyzer invoke
I'm guessing that this refers to the currently hypothetical |
I feel like it would make more sense to support that setting in a
Yes sorry, I failed to copy the quoted text :) |
To chime in, because I'm working on the exact same thing (as you already linked)
|
@Veykril My feeling is that it makes sense to support this feature in both places—I don't think I can share exact numbers, but at least for my employer, it's not feasible to have a single For Buck users, To put what I said in a different, higher-level way: roughly speaking, (I assume that everything I said about Buck also applies to Bazel, but I've never used Bazel.) |
@davidbarsky Yup, it's pretty much the same. We discussed this a bit and a potential solution in #13437 |
Yes you are right, I was mainly thinking of |
@davidbarsky, Okay I got one more question regarding the |
@Veykril While it's not unreasonable to generate a I think it'd be neat if the Buck integration tooling can do all of this automatically, in the background, by placing the generated To step back a little, I view the When trying to add support for loading the build command to rust-analyzer yesterday, I found I could make the change to load the |
Would you also need to specify absolute paths for the
I don't think I'd want any config to be specifiable in the |
|
Yes, in general I agree that it makes sense. I mainly wanted to confirm whether I also just realized
can be read differently than intended, with |
I'm not sure, but I'm leaning towards a "no" from necessity standpoint but "yes" for ease of supporting and debugging people's environments. And yes, agreed on the generation of the As for the second point about hierarchal |
I opened #13529 for a discussion regarding a |
Getting back to this, I think that working around the core difference between r-a's assumption ("we have many small Would it not be possible instead to
? |
rust-project.json is equivalent to a cargo workspace. Internally rust-analyzer effectively produces an in memory equivalent to rust-project.json based on running |
Ah, I see, thanks! Then it makes sense that there's not much point in splitting it back up again.
This doesn't sound viable to me - afaiu, it means that whenever I jump across to a different (non-dependent) crate, a file would have to be regenerated(already big overhead here), which at least now, takes a non-trivial amount of time for In the first place, what's the thing that allows r-a to know which crate to run e.g. |
I meant that you tell the build system which project inside the mono repo you are working on and then the build system generates a rust-project.json file containing just what you need.
Rust-analyzer searches for all rust-analyzer/crates/project-model/src/lib.rs Lines 134 to 145 in 599142c
cargo metadata on each one and considers it a separate workspace. I am quite certain there is code to prevent every workspace member from being considered a separate workspace, but I can't quickly find it. In any case rust-analyzer simply runs cargo check in the root of every workspace it knows about, just like for rust-project.json. #13336 will add an option to limit cargo check to just the crate that you changed. This does not limit rust-analyzer itself to just that crate. Rust-analyzer always loads everything and any change to the set of crates requires a full reload.
|
Since this issue got some additional activity today, I wanted to re-raise an idea for rust-analyzer that I mentioned in the issue:
Does any one have any objections to such an approach? I think I'd like to tackle this sooner than I otherwise expected since one of the most common bits of feedback I've received from users is that they don't like having to manually run
|
Ye, I think we can go with that and see how it goes. On paper it makes sense to have something like this I think (with the future addition of a |
Just to be on the same page, isn't your use case for buck with a big rust monorepo which is all under the same Not that it's directly related to your use case, but for |
Cool! I'll try to get to this next week.
@googleson78 sorry for the delay. Buck doesn't do things quite like how Bazel does it: there's no big WORKSPACE file, just arbitrarily many
When it's not writing to a virtual file system (e.g., EdenFS), rust-project is able to complete in about 2-3 seconds: it's generally pretty fast. When it's writing to EdenFS, it's about 10-13 seconds, which is not ideal. It's not currently open source, but I don't have any objections to potentially open-sourcing it. The code's a bit gross, though, so I'd like to clean that up first. Having thought about this implementation, I thought of a few questions pertaining to Rust Analyzer for @Veykril, though:
(Sorry if I @'d anyone in appropriately: I'm not entirely sure what the norms are for rust-analyzer, and I'm certainly not close to enough to @ y'all freely. Please let me know if I should be more conservative with my usage of mentions!) |
The project layout is an input of the query/caching system. Changing is_workspace_member necessarily requires changing this input. Reloading the project structure is implemented as setting a new value for this input too: https://github.com/rust-lang/rust-analyzer/blob/0dd0dfb7dfe723bbfa81197c23d73c9bdc4022c1/crates/base-db/src/change.rs#LL74C79-L74C79 So changing is_workspace_member after loading is almost equivalent to reloading as far as I understand. Especially for is_workspace_member as it affects a lot of things.
That again is pretty much equivalent to reloading as it requires invalidating the macro expansion result and everything derived from this. |
That's true, but RA's analysis is incremental across these reloads. Small changes in the crate graph should not cause a lot of recalculation as long as the crate IDs stay the same. I'm not sure changing (I don't really understand why you would want to change |
R-a actually already does work while build scripts are still evaluating, it's just that once the build scripts are finished running /proc-macros are built, this again changes the workspace input (as now build script outputs are attached) retriggering some queries that have been invalidated. |
Some high level comments: checkOnSave: architecturally, checkOnSave is a hack. “run the real build using the real build system and display errors” is an important feature to have, buts its really none of rust-analyzer’s business to arrange that. We do that for cargo just to make more of the things work out of the box for more users, but this isn’t how it all should work. for monorepo case, what you want to do is to set checkOnSave=false in rust-analyzer, and than have a separate vscode plugin which watches open files and code modifications, schedules buck/bazel/pants/whatever builds and displays errors in the editor. |
On rust-project.json file For the monorepo case, there shouldn’t be project.json file. rust-analyzer the binary supports getting the project structure as JSON object in config sent as an LSP message. So, for monorepo, what should happen is that every time a user opens a new project, rust analyzer should receive an LSP message with a new config embedded. I think one approach one company used here is to have an extra proxy on the wire between vscode extension and rust-analyzer binary, with the proxy injecting configs. This is a bit roundabout. We should add some explicit hook to vscode extension to allow injecting the config. So, again, we have a monorepo-specific extension in VS Code which watches what user is doing and pokes rust-analyzer with an updated config when appropriate. |
(I think an elegant way to make this happen would be for hypothetical vsbuck extension to just modify vscode workspace settings dynamically, but it looks like vscode lacks APIs for configuration providers https://stackoverflow.com/questions/72194332/need-a-better-way-to-programmatically-clear-and-set-user-settings-in-vscode) (I think these days rust-analyzer still shows “you must reload extension when you change this setting”, but I think this is accidental? I believe on the server side all configs are dynamic these days) |
It also controlled salsa durability. Non-workspace members are “durable”, which means ra optimizes for code there to not change often. |
On dynamic world: rust-analyzer makes a soft assumption that it knows all the crates there are. While we could incrementally discover new crates, we try to push users towards specyfing all crates up-front. This is important for “find usages” and for all the reactors which build on top of that. It would suck if you do a rename, and then the build fails because you haven’t yet open some downstream crate, and usages there weren’t refactored. I think this is a right assumption for basically everything hosted on GitHub, but is wrong for giant monorepos. In particular, in monorepos find usages should be handled by some map reduce index and not by ra, and one does not ide-rename identifier across the whole monorepo. Luckily, this is a weak assumption, and everything should basically just work if you dynamically add new crates to the crate graphs. If you are careful with id assignment, this should also be very incremental, though I expect that we might be doing some needless recomputations here, as we didn’t really optimized this. (Eg, crate graph is atomic query, I think we need to add firewall q for “deps of crate X” to avoid re-resolving std once you add a crate) |
Gotcha, thanks all for responding. bjorn3/florian:
iirc,
I mean, there are some rough heuristics that I can take, but the Lukas:
that's good to know! matklad:
That's probably fair, but I'll need to look again at the Buck extension. For better or worse, the terminal-rendered output from rustc is much better than what can be rendered over JSON inside of VS Code, but I'll take a look.
My gut feeling is that this approach is the right one (and, indeed, is what I've setup for neovim users), but as you've mentioned in a subsequent comment, contribution points to VS Code extensions are weirdly limited. To get around this issue, my employer's forked VS Code and added shared memory between extensions to allow that sort of nudging. However, that feels gross, and I'd rather try to steer clear of that for maintenance reasons. If Microsoft added a contribution point for configuration, then that would be the ideal solution.
Yeah, I think that's the right call for rust-analyzer. When
I absolutely agree! Thankfully, most of the crates that I'm thinking of are things like "generated RPC definitions", which shouldn't be refactored by rust-analyzer anyways: changes like that should be made in the code generator, not in the downstream consumer.
I think that there are two different kinds of refactors:
Gotcha. Naively, it seems like inserting crates at the end of the collection, as opposed to in the middle, can provide the firewall that you're referring to by not allowing access to indices higher than |
rust-analyzer's vscode extension itself can provide a contribution point here. We already have an ad-hoc one: #5017. Though, that one is "ok as a temporary solution" :) A proper thing to do here would be to use
There's a huge design space for what we actually expose that way, and what stability guarantees we provide though. I think a reasonable start is "expose whatevere, but we provide zero guarantees" :D |
That's... extremely cool and I had no idea this was possible. I was looking through https://code.visualstudio.com/api/references/contribution-points and didn't see anything along these lines (except
Oh yeah, that's totally reasonable—I'm fine with whatever breaking changes that need to be made as this is sorted out—I'm able to control distribution, so at least for me, my users won't be impacted by breaking changes. I'm not entirely sure what should be exposed, but I can look into it. Looking at what the APIs allow exposing, it seems like it's possible for rust-analyzer's VS Code extension to try and call one of the APIs its exposed, and if it returns nothing (indicating no other extension imported it/extended it), then rust-analyzer can proceed normally. |
It, uh, took a bit longer than a week, but I hacked together something that works 😅. I'll post a PR in a day or two. Anyways, I'm not sure whether to post the following on this issue or on #13446, but given the discussion about extensions invoking build systems here, this issue will do. For context: I was thinking about to how add Buck1-specific Anyway! I didn't realize why @googleson78 asked for support for (The reason I'm proposing extending rust-analyzer, as opposed to doing this inside of a Buck extension, is that I think rendering Footnotes
|
Hm, but how would you even get a Buck extension to render diagnostics to a LSP client? Even with the |
The buck extension can directly pass the diagnostics to vscode. There doesn't need to be an LSP implementation for that. In fact vscode itself doesn't have any lsp client at all, rather the extension corresponding with the lsp server has an lsp client translating between LSP and the vscode API's. You can use those same vscode API's directly in the buck extension. |
@googleson78: yeah, what bjorn3 said. If there does need to extension-level coordination, then using the contribution points that matklad pointed out in #13446 (comment) can be another mechanism to get this functionality. However, for somewhat complicated reasons that are idiosyncratic to my employer, it's an approach that I'd rather stay away from (or at the very least, one that I'm a bit nervous about). |
This issue connects several disparate issues and discussions, such as #13437, #13128, #12105, #13226, and several others that I'm probably missing. This issue will be a bit handy-wavy, but I think there's value in describing my ideal user experience, which is to make rust-analyzer work with Buck/Bazel/et. al. as just as well as if they were Cargo.
The Current State
My current employer has one of the largest monorepos in the world and uses the Buck build systemto manage the vast majority of Rust builds. At the moment, most users connect to a remote dev server via VS Code and have their VS Code workspace be at the root of the monorepo (these are largely immutable user behavior and expectations). They then run a small CLI that spits out a
rust-project.json
(e.g.,rust-project develop //foo/bar/my-target
) and work on their project from the root of the workspace. To getcargo check
-style diagnostics, people need to manually invoke Buck with acheck
-flavored build as such:buck build :my-target[check]
.Possible Improvements
In no particular order, since I see these as roughly equivalent in impact:
rust-project
) that can query the build system for the owning TARGETs, generate arust-project.json
, and continue with the remainder of therust-project.json
-based workspace loading. This option would occur after rust-analyzer checks for aCargo.toml
or arust-project.json
, doesn't find them, and a "discover workspace" (name to be bikeshed!) command is defined.flycheck
crate should be configurable to run builds/checks through a non-Cargo-based build system. When I last looked at theflycheck
crate, it didn't seem like a particularly difficult change, just one that nobody has gotten around to designing or implementing.rust-analyzer.toml
(which I don't believe has an issue), which can be placed at the root of the monorepo, which would reduce how many configuration forks I need to make for the rust-analyzer extension.The text was updated successfully, but these errors were encountered: