-
-
Notifications
You must be signed in to change notification settings - Fork 2.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
fix(core): broken LSPs on monorepos #4439
Conversation
"Top-most" in Line 45 in 801984c
means highest in the directory hierarchy but this changes it to lowest. The top-most heuristic is geared towards umbrella / workspace setups (for example helix) where there's usually a root marker in the same directory as The problem here looks essentially the same as #4342 |
@the-mikedavis the current algorithm seems to be geared towards the same solution I described. The exact same situation with multiple apps in the same root directories would be broken with the current setup in most languages. If using Elixir with Umbrella apps, would the LSP be expected to be started in the umbrella root and not in one of the sub-directories? Wouldn't this be achieved by starting helix on the root directory? In the current scenario the LSP is unable to be fixed by the user if I want to start a LSP in a subfolder of a say a monorepo with multiple |
My impression was that this is the case - that the umbrella dir should be the workspace root - but I gave it a try and
No, the CWD is used as a fallback when we can't determine a workspace directory from markers or find a |
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 docs for this function also need a change with the behavior
@the-mikedavis I've made the requested changes + updated the docs. Let me know if it works for you! I've tested it locally after the changes and everything seems good. |
Folks - anything I can do to help move this PR? I know this doesn't seem urgent but it is forbidding a few people at my workplace from using Helix due to our usage of monorepos. It may cause a similar reaction to new users as they won't understand why LSP's are just not working for some projects. |
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 will break typescript/javascript language servers where each package has it's own package.json
, but you're expected to start the LSP at the workspace root package.json
helix-core/src/lib.rs
Outdated
/// * Top-most folder containing a root marker in current git repository | ||
/// * Git repository root if no marker detected | ||
/// * Top-most folder containing a root marker if not git repository detected | ||
/// * Top-most folder containing either a root marker or a git repository root |
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 seems to be the closest parent folder, not the top-most anymore.
@archseer how can I make it so it behaves as expected on both typescript and elm.json (as in the example given) ? Maybe a language flag that would decide if you should use the top most or closest root file? As mentioned before, the "closest" approach is apparently also used by neovim. My experience there with both typescript and elm projects was good. So maybe the LSPs that support workspace like features have their own logic for discovering the closest root? The current problem is that there is no escape hatch if I don't want to use the top most root file. And even on typescript that is sometimes the case (e.g. if I want to create an example folder with projects that are not part of a npm/pnpm workspace but meant to be run in isolation) |
Basically its the same problem as is popping up everywhere regarding this. There is unfortunately no single solution that fixes all. Neovim solution that worked best for me was simply not doing any of the smart finding of workspace root, but more just always start neovim in the correct folder. If stuff didnt work, I had an easy fix, start the program in the right folder. Then comes other issues, because maybe you have to start helix in a folder which is inconvenient in regards to file pickers or similiar. So either you force all projects to conform to predefined folder/project structures, and have the LSP and helix detect all of them. If we want the smart workspace detector, we also need the escape-hatch that @georgesboris mentions. |
On neovim, I was able to aliviate the file pickers problem by using both git based file pickers and pwd based ones. But if the logic for choosing between those is also based on the roots config them this escape hatch would also not exist. |
My two cents (with breaking changes):
From my thoughts so far the only easy way to make this failsafe would be for helix to use project-based configs as well but using priority-based roots we get something similar to do this as I can use a dummy file to ignore workspace root per-project. |
@archseer @the-mikedavis folks - do you see any short term strategy to get this merged? totally available for working on other strategies that would stop helix from breaking lsp on this types of projects. it is just too much of a pain for it not to work even with local hacks (like creating a specific file for forcing correct root detection)... only way I can make it work today is by running helix built with this PR's branch. |
The current behaviour also seems to break for Golang monorepos which have a mix of
When I open the subfolder, Golang's LSP (Gopls) goes into overdrive and tries to load to LSP in the root monorepo which has hundreds of subfolders, and it ends up hanging. Perhaps one naive way of solving this is as @georgesboris says, have an optional hidden file that lets users force correct root detection? E.g. |
There is #4363 |
@jlloh On the other hand, finding the toplevel on a Go repo with |
We already have support for a local config by looking for a Lines 93 to 107 in f41f28b
I think the behaviour should be: Loop through ancestors:
WDYT @the-mikedavis? |
Using As a minor point, we may want to remove the fallback behavior of returning the CWD as the default: #4436. I think this root detection function can return an Option and if there is no |
Just as a review with use cases: umbrella-app (go, elixir, rust?)
umbrella-app subfolder
monorepo not umbrella (elm lib+example, js lib+example)
those are the scenarios, right? just to illustrate the broken case a bit more - sometimes there are projects with a subfolder that is completely standalone. it is not part of a workspace or umbrella. it is just versioned together. so:
so in the current proposal the fix would for someone to create a the problems I see are:
|
+1 to the convo, I do think a folder or file might be better than an environment variable (as an environment variable is global). Re: @georgesboris, my two cents:
I guess Helix's existing behaviour will still work fine for most cases, but for folks having problems with nested monorepo structures, we can always just explicitly state it in the documentation that they would need this extra folder/file to force the LSP to start at the right folder?
I feel it's probably fine, because other IDEs do something similar in terms of supporting some form of hidden folders/files. VSCode has p.s
@archseer I tried |
We already use this folder for local configuration and I think it's not uncommon in other editors. You could add it to your global |
I see your point about starting |
@archseer on neovim (telescope) I can deal with that by opening my file picker using CWD or through git. I have both keybindings so I can still "peek" through my whole project even when starting it in a subfolder. |
the find_root function should match the top most root directory for a given file. However, due to not checking if the root was already present, it was overwriting the response so it always matched the bottom most root directory (most commonly the git directory). This broke the expected behavior for projects like monorepos with multiple root directories.
2fcaa78
to
2574dfd
Compare
@@ -42,11 +42,13 @@ pub struct Client { | |||
|
|||
impl Client { | |||
#[allow(clippy::type_complexity)] | |||
#[allow(clippy::too_many_arguments)] |
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 searched the codebase and noticed we already use this flag in a few places so I took the liberty to add it here as well.
Sorry for the delay, I've done some more thinking (and looked over #3993 again -- the issue even mentions that this used to be fixable by just creating a I propose we make this a user config option, then it's possible for you to either change this globally, or keep the same default and change it in a single workspace with a |
@archseer so the idea would be something like this?
Currently,
that way we would match the project path and force that to be recognized? is that it? where do you think that setting should live? I'd be glad to make that change once we settle on a solution. |
Here are some of my thaughts:
For the specific file layout I had been thinking: [workspace]
roots = ["examples"]
roots.rust = ["examples/rust", "fuzz"] # overwrites roots for rust Using paths that fallback to a parent path is also used for themes so it seems fitting to use that here aswell. |
Another PR popped up: #5418 Adding this to the next milestone since I'd like to resolve this by next release |
I implemented a solution based on the ideas from my comment above in #5748. I added that to the next milestone in favor of this PR |
I'm closing this in favor of #5748 |
Problem
We're currently matching the top-most root for all projects and languages, but a few of them are not workspace oriented and the current behavior prevents the LSP from working in nested projects (a monorepo or a library with an example folder) when working in said languages.
e.g.
lib/elm.json
would always be the root even if Helix was opened atexample
– that breaks LSP integrations, auto-formatting, etc.Solution
For a non-breaking change solution that could be defined as the default for some languages and even configured case-by-case by the user (through a project
/.helix
folder), I propose adding amatch_closest_root
language option that isfalse
by default (maintaining current behavior for all languages) but can be marked astrue
for languages like elm.