-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Add multi project support vscode with single server #3987
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
Add multi project support vscode with single server #3987
Conversation
… I was unable to set workspace folders in vscode. can be removed if solution is found.
…hat the folder actually contains the file.
…new document is opened. One caveat is vscode.workspace.textDocuments only provides the documents the user have clicked on: Issue: microsoft/vscode#15178
|
Does this do anything to address #2001? |
|
@jondo2010 It should fix #2001 but would be great to test which I haven't done :) |
I believe that there are some variables for that, like |
|
@davidolrik it only takes a single folder the parameter I need to set is for multiple folders it's part of the language server spec (microsoft/language-server-protocol#281) but it's unavailable in the vscode api |
| "default": true, | ||
| "description": "Whether to ask for permission before downloading any files from the Internet" | ||
| }, | ||
| "rust-analyzer.server.autoRestartOnNew": { |
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.
| "rust-analyzer.server.autoRestartOnNew": { | |
| "rust-analyzer.server.autoRestartOnNewProjectFound": { |
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.
Took a look. I must say this feels like a pretty big change to how everything works, which departs a lot from what is already implemented/what I envision, so it was a bit hard to review for me :)
The big piece is that we should strive to keep almost everything in Rust. I think the only thing here which genuinely belongs to the editor is didChangeWorkspaceFolders event, for everything else, I don't see a fundamental reason why it can't be done at the editor's side.
I still don't fully understand what specific use-cases this covers, but here's my ideas about what we can do to make handing multi-projects better
-
our current discovery scheme is very limited. For each ws folder, we look for Cargo.toml only up to one level deep. This handles auto-discovery for some projects, but not for all of them. We don't do full auto-discovery primarily because our current VFS is limited; but I also think that unconditional crawl of all files in the project is probably a bad idea. So, the two action items here are:
-
add a
cargoTomls: [Path]configuration to completely disable auto-discovery of Cargo tomls in favor of specified values. This should be an option in Config, which is fetched from initialization options -
for the case where we open rust-file deep in the project (due to onLangauge activation event), I think we can pass these file to the server as well (via the same initialization options), such that the server can crawl upwards from the file. Crawling upwards should always be OK. This is roughtly the logic that is implemented here, but it absolutely should be on the server's side. I wonder if implementing this would allow us to remove the one-level crawl? Might be a good idea to return to the original PR and check if those use-cases are covered.
-
-
we currently don't notice workspace structure changes
-
server-side, we should monitor for changes in Cargo.toml, Cargo.lock and implicit targets like
src/bin/foo.rs(the correct definition of implicit targets can be copy-pasted from IntelliJ) and ask the clients to show reload messages -
client side, we should monitor for the changes in workspace folders, and show the "needs reload" message (or, ideally, some kind of birhgt-red persistent status in the status bar)
-
| import { activateTaskProvider } from './tasks'; | ||
|
|
||
| let ctx: Ctx | undefined; | ||
| const foundProjects: Set<string> = new Set(); |
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 a general comment, global singletons should not be added without very good reason.
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.
100% agree,
I only used it here because it's not meant be shared outside main, and it didn't fit in Ctx since this is meant to live after a restart.
|
|
||
| impl Args { | ||
| fn get_roots(mut matches: Arguments) -> Result<Option<Vec<String>>> { | ||
| if matches.contains("--roots") { |
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.
Adding a command line option, given that this is the first command line option, massively changes the interface of the program. It's much better to stick to existing conventions, which, in this case, are the initializationOptions (not sure about the naming) field of the initialize requst
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.
massively changes the interface of the program
As I said I couldn't find a way to send it via the initialization options in vscode. It also shouldn't be a massive change to the interface since it's a completely optional parameter but I can see how it feels intrusive.
|
|
||
| foundProjects.add(cargoRoot.fsPath); | ||
| if (isMissing) { | ||
| if (config?.autoRestartOnNew) { |
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.
My experience in IntelliJ tells me that auto restart doesn't worth the trouble. The problem is that we should only restart in quiescent state, and only the user knows when this state is reached.
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.
Ie, setting a status "needs restart" is absolutely necessary, and a big missing piece in rust-analyzer at the moment, but it should be the user who decides to click the restart button.
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 added it after testing it a bit, since it got very old very quickly having to restart manually all the time, which is also why it's turned off by default.
| } | ||
|
|
||
| // searches up the folder structure until it finds a Cargo.toml | ||
| export async function nearestParentWithCargoToml( |
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.
We already have this implemented in rust (find_cargo_toml in ra_project_model), and I think we should strive for keeping as much as possible in rust, as it is sharable between the editors then.
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.
Took a look. I must say this feels like a pretty big change to how everything works, which departs a lot from what is already implemented/what I envision, so it was a bit hard to review for me :)
Not sure what it takes away from what is already implemented / what visions it's breaking?
Is it that nothing can be done on the client side?
The big piece is that we should strive to keep almost everything in Rust. I think the only thing here which genuinely belongs to the editor is didChangeWorkspaceFolders event, for everything else, I don't see a fundamental reason why it can't be done at the editor's side.
That's a good point, I saw this as a very minor change to improve QoL for vscode, but I can see how it might be better to do 100% server side for many other reasons. It took me a couple of hours to code up (mostly reading vscode issues). If that's what is ruining this pr for you I could redo it in the server, however there it will actually affect all users of it (but maybe that is what we want?).
still don't fully understand what specific use-cases this covers, but here's my ideas about what we can do to make handing multi-projects better
There are many use-cases
- You have a large project where rust is in a deep subfolder
- You open a random file from another project (outside your workspace)
- You have many projects in one folder which depend on each other (ala crates)
|
I can reimplement this PR on the Rust-Analyzer server(no more typescripting :D), but I am actually curious of is: Is this the behavior one would expect from a UX perspective? |
Yeah, I think we want all of these things:
Implementation wise, I'd probably suggested to start with 3, adding Than it makes sense to tackle 1, as it is somewhat tricky, but also I think will solve the largest practical issue. |
I still haven't been able to find an option in vscode to override the Also wouldn't it be cooler to have a file(ex. |
I think VS Code workspace roots and the cargo projects need not to be one-to-one. For example, I can imagine a setup where a single VS Code workspace folder actually contains several Cargo workspaces. For that reason, we don't need to override the workspace folders, rather, we need a separate, rust-analyzer specific config for specifying the set of Cargo.toml. This config needs to be added in three places:
The config value is available early at the server lifetime, and can be used to configure the initial workspaces: https://github.com/rust-analyzer/rust-analyzer/blob/fc0a47a0c19a1b58a586e30645cec4d80d700513/crates/rust-analyzer/src/main_loop.rs#L67
yeah, that would be ideal, but this really should be a protocol feature. We rely on standard LSP way to pass configuration, and that is specked to be editor-specific. |
|
Either I completely misunderstand or what you're proposing would not to solve the problem I am trying to solve. The way I initially understood your config proposal was that it would be super niche for people with very specific setup and not the normal way to use rust-analyzer. If the config is meant to be a sort of override then I completely agree. However the bread and butter I want to achieve with this change is Dynamic Rust-Analyzer Project Loading, working in whatever shape the files are laid out. Once that is in place I don't mind making an override config. I have written some user stories to make it more clear what problems I am trying to solve and why, I made them colorful so it's easier to feel the pain :) Imagine this scenario: and Situation A:I am working on the python project and I want to quickly make some quick changes to the rust projects A & B. Situation B:I just pulled down some repo and want to quickly make some changes to it. How it is todaySituation A:
Situation B:(Same story as A) How it is after this PRSituation A:
Situation B:(Same story as B) If config was the main way to work with Rust-AnalyzerSituation A:
Situation B:
|
|
Yes, the config is for a very niche use-case, but it makes sense to start with it implementation wise, as it will clearly separate the discovery aspect from project loading aspect. Additionally, I suspect the config might turn out to be the right tool to persist workspace configuration between restarts. You probably don't want to repeat the whole reloading dance every time you open the project. |
|
Ah right, that's fair, I guess there are some outstanding issues around how to deal with persisting the project discoveries. That said I am willing to slay that horse when I've created the "config setup" PR, and I've gained some credit. |
|
Submitted #3995 which hopefully makes the control flow in this area a little clearer. It also removes the "multiple equally valid Cargo.toml files" error, which existed simply because code was split into functions across wrong boundaries. |
|
сс #2649 |
|
Closing due to inactivity |
|
Yeah my bad been quite busy lately I will see if I can find some time soon to get this done :) |
Redesign based on discussion from: #3972
Redesigned multi project support entails:
Cargo.tomland start Rust-Analyzer with all their roots at activation timeonDidOpenTextDocumentand inform user to restart if a new Rust Project have been found (with option to auto restart Rust-Analyzer).Some caveats
git revert 53ed3831dd296edd660aeea7d6508e53f263c650should get rid of this change)Cargo.tomlin the root, with this implementation that would be ignored since every project incrates/*have their ownCargo.tomlwhich would take priority, I did try adding both the projectsCargo.tomland the root'sCargo.toml. But that seems to make Rust-Analyzer generate double outputs on highlight.Future work