-
Notifications
You must be signed in to change notification settings - Fork 164
reintroduce multi project setup #638
reintroduce multi project setup #638
Conversation
3f814f0
to
3eb24b2
Compare
* enable multiple projects with multiple rls running * change the URI key back to string for workspaces (as it's unreliable) * add selectors such that only the correct rls is used for a given project * take great care to ensure the old setup works normally * add a configuration and turn it off by default.
3eb24b2
to
a8b652a
Compare
forced pushed to make it more clear what I am actually changing :) |
a8b652a
to
6684a08
Compare
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 suggested some really small changes, I don't know if they make sense regarding to tslint and variable scope as I did not have time to clone the repo.
I also suggested another workflow I'd like to discuss if possible.
runRlsCommand(this.folder, cmd), | ||
), | ||
commands.registerCommand('rls.run', (cmd: Execution) => { | ||
const ws = |
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.
Reading this took me some second to understand that ws was workspace, also personal preference but I find ternary easier to read like so :
const workspace = (multiProjectEnabled && activeWorkspace)
? activeWorkspace
: this;
runRlsCommand(workspace.folder, cmd);
I don't know if it's ok with the tslint configs.
}, | ||
); | ||
this.disposables.push(rustupUpdateDisposable); | ||
|
||
const restartServer = commands.registerCommand('rls.restart', async () => { | ||
await this.stop(); | ||
return this.start(context); | ||
const ws = |
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 comment about ws -> workspace here 🙂
@@ -219,6 +219,11 @@ | |||
"default": true, | |||
"description": "If one root workspace folder is nested in another root folder, look for the Rust config in the outermost root." | |||
}, | |||
"rust-client.enableMultiProjectSetup": { |
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 don't know much about the extension, didn't have the time to clone it yet, but wouldn't it be possible to make the extension work like :
- Normal cargo.toml at root of the project is tried
- If it fails the extension tries to determine if it's not a monorepo
I'd like your opinion on this, if it is quite some work I'd happily lend you a hand 🙂
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.
Normal cargo.toml at root of the project is tried
With this change the cargo.toml at root should also just work.
If it fails the extension tries to determine if it's not a monorepo
How would you detect that? I could imagine some wtf moments where you add a folder and all of the sudden the tooling stops working. I would rather make configuration for stuff like that.
I'd like your opinion on this, if it is quite some work I'd happily lend you a hand
In general I would estimate that 99% will just always have it turned on. So mostly this is about not breaking it for the 1%.
and thx for the offer :)
src/extension.ts
Outdated
if ( | ||
workspace | ||
.getConfiguration() |
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.
workspace.getConfiguration
is also called later on, maybe consider storing it into a const configuration
and reusing the same variable ?
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.
ye, the problem is the function was clearly never meant to take configuration arguments. However it would take some re-architecture to make it nice, and I didn't want to push for too many changes.
workspace.start(context); | ||
} else { | ||
const ws = workspaces.get(folderPath); | ||
activeWorkspace = typeof ws === 'undefined' ? null : ws; |
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.
const workspace = workspaces.get(folderPath);
activeWorkspace = (typeof workspace === 'undefined')
? null
: workspace;
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 formatter, which is build server checked, decides stuff like that.
const workspaces: Map<Uri, ClientWorkspace> = new Map(); | ||
// Don't use URI as it's unreliable the same path might not become the same URI. | ||
const workspaces: Map<string, ClientWorkspace> = new Map(); | ||
let activeWorkspace: ClientWorkspace | null; |
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.
Typescript has a builtin for nullable variables :
let activeWorkspace?: ClientWorkspace;
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 per above, it'd be great if we could extract this 'global' state somehow to a dedicated module, similar to any other code that manages/fetches appropriate workspace servers.
return rustupUpdate(this.config.rustupConfig()); | ||
const ws = | ||
multiProjectEnabled && activeWorkspace ? activeWorkspace : this; | ||
return rustupUpdate(ws.config.rustupConfig()); |
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.
const workspace = (multiProjectEnabled && activeWorkspace)
? activeWorkspace
: this;
return rustupUpdate(workspace.config.rustupConfig());
Yep! Before merging, I'd like to release a 0.6.2 first with a fix for #639 |
@Xanewok Are you ready to review now? |
I've built your branch from source and have been using it for over a week without any issues, in both a multi-project setup and single project. |
👋 |
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.
Thank you for working on it, it's a highly requested feature as you can tell!
I left some comments below - it'd be great to extract the workspace managing bits outside before we delve into details here since it's getting a bit conflated and harder to see which path we should take with the details of the implementation.
Are URIs unreliable for workspaces because they can be not normalized/canonicalized?
src/extension.ts
Outdated
|
||
commandsUnregistered = false; |
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.
nit: I think it'd be more clear to have commandsRegistered
instead if we're tracking state like that
src/extension.ts
Outdated
|
||
// return cur_workspace; | ||
// } | ||
function getCargoTomlWorkspace( |
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 document this briefly since it's not immediately obvious (at least for me) what is this doing without analyzing the body thoroughly
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'll add some comments.
However even though the body might look a bit big, I actually kept it like that to make it simplistic, mostly because it's a function that will run fairly often and I didn't want to risk busy loops.
if (!workspaces.has(folder.uri)) { | ||
const folderPath = folder.uri.toString(); | ||
|
||
if (!workspaces.has(folderPath)) { |
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 should definitely move this logic out of didOpenTextDocument
. Anything related to organizing and identifying instances across different folders/workspaces should be under src/workspace.ts
perhaps? (name open for bikeshedding)
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.
While I'm not against refactoring some of the code out, I'm afraid that this will delay this PR even more, as it's now doing two things at once.
const workspaces: Map<Uri, ClientWorkspace> = new Map(); | ||
// Don't use URI as it's unreliable the same path might not become the same URI. | ||
const workspaces: Map<string, ClientWorkspace> = new Map(); | ||
let activeWorkspace: ClientWorkspace | null; |
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 per above, it'd be great if we could extract this 'global' state somehow to a dedicated module, similar to any other code that manages/fetches appropriate workspace servers.
No, I tried with identical URI's but I noticed that there was some caching going on because when I ran the code in debug I could see that the some private field were being set the second time I used a URI for the same string. Which I concluded was why they hash'ed differently. I couldn't get it to work on my machine but maybe I was using it wrong, so if someone knows the answer I'm happy to hear it. |
It's good comments and I don't disagree, however I feel a requirement of refactoring some of the designs decisions already made in the code base is a bit much. I'm not against refactoring some of the code however I am very afraid I'll spent a good few hours doing that only to have my PR linger for the next 6 months. I'll do it but I'll need your word on it not happening. |
4c2c97a
to
47c66be
Compare
I looked into it: I started the work on moving to a "workspace" module the problem is that almost 80% of extensions is about the workspace, more than that the code is littered with global state. Now If one must have global state at least keep it contained to one file. As such I ended up just moving my own function, along with writing some TODOs. With that said I do think it's a good idea to look at this code and do some refactoring however I don't think this is the PR for it. |
👋 |
Changes look good and I won't block this any longer - sorry for the delay! |
@jannickj thanks so much for the contribution! Everything appears to be working with my nested subdirectory. However, running the "cargo build" task doesn't seem to work. Do you know if this is meant to be supported? (more details here: #419 (comment) ) |
This will allow you to have project structures like this:
when you open a file it will search up parent folders until it finds a Cargo.toml, and either reuse the RLS instance for that one or create one if it doesn't exists.
Changes
URI
key back tostring
forworkspaces
(as it's unreliable)Second attempt at: #457