elm-language-client-vscode: Multiple project folder error: Can't find file for formatting.
I’m very sorry but I haven’t been able to workout the reasons or situations under which this happens.
I have a multiple project folder, which has two elm projects in commerce and admin subdirectories. The plugin appears to work well when in the commerce sub-directory. When in the admin sub-directory everything starts up well and I get warnings/errors as expected. However, as soon as I save a file, I get an error in the output log as:
[Error - 12:48:10] Can't find file for formatting.
After that nothing seems to work, formatting is done, but also warnings and errors are not updated.
Everything seems to work fine if I open only the admin folder in VSCode.
Expected Behavior
The plugin continue to work after the file is saved.
Current Behavior
It stops working after a file is saved.
Possible Solution
Hmm, sorry, no idea.
Steps to Reproduce (for bugs)
As I said, I’m very sorry but I really cannot provide any additional information that may help solve this. I’ll report back in this issue if I discover something.
One extra piece of information. Both projects (in commerce and admin sub-directories) have some ‘sub-directories’ in common, in particular they both have modules in src/Types/ and src/Components, so my first thought was that perhaps the plugin is looking for admin/src/Components/Module.elm in commerce/src/Components/Module.elm. However, they both have a couple of module names say admin/src/Components/Common.elm and commerce/src/Components/Common.elm, and if I save the former I get the same error ([Error - 12:48:10] Can't find file for formatting.) as before, when I might have expected it to find the file albeit format the wrong one.
Context
Nothing special I can think of, other than that the open folder houses two separate elm projects.
Your Environment
- Version used: ElmLS 0.6.0
- Editor name and version (e.g. VSCode 1.36.1): VSCode 1.37.1
- Environment name and version (e.g. node.js 5.4): npm 6.11.2
- Operating System and version:Manjaro Linux 18.4
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 4
- Comments: 19 (16 by maintainers)
As I suspect @Razzeee may have predicted, this is a bigger job than I was expecting! Processing every client event in all detected workspaces, trusting that only one of them will actually do anything, is looking like it will generate too many potential problems.
This is more properly tracked as an issue of the language server (specifically, it looks like elm-tooling/elm-language-server#187 was already created for this). I’m going to start posting updates on my work there.
I’ve spent a couple of hours learning about the Language Server Protocol, getting a debuggable copy of elm-language-client-vscode and elm-language-server running on my machine, and tracing source code for how the document formatting request is handled.
I haven’t fully gotten my head around the layers of indirection, but here’s what I’ve understood so far:
The
Serverat startup creates anElmWorkspacefor each elm.json file it finds in your project’s directory tree. EachElmWorkspacefinds all *.elm files in the"source-directories"specified in the elm.json file, and follows theimportstatements they contain to also find all dependencies in elm-stuff and other directories.It then registers each *.elm file, with a bunch of ”providers”, one for each major feature of the language server (e.g. the
DocumentFormattingProviderfor requests to format the document). I haven’t yet found how the files get registered with the providers, but it seems to have something to do withTextDocumentEvents, which listens for"open"events (among others) for text documents.When a request to format a document comes in, the request is supposed to be routed to an instance of
DocumentFormattingProviderwith a reference to aTextDocumentEventsobject that knows about that specific document. In my debugging, all of my Elm files have their requests routed to aDocumentFormattingProviderthat has an empty collection of documents! Why that is, I haven’t yet been able to figure out.I can see that on your WIP branch you’re doing some rewrites of how the
DocumentFormattingProvideris found when a document formatting request comes in. I don’t yet understand the old mechanisms enough to see how your new approach differs, though.One hunch I now have after all this debugging is that a quirk of my elm.json files may be responsible for most of the language server features not working for me: While a stereotypical elm.json file starts with
"source-directories": [ "src" ](Elm files in a src subdirectory), all of our elm.json files start with"source-directories": [ "." ](Elm files in the root directory). When the Elm Language Server starts up, it reports all of our source files with/./in the path (e.g. “Adding /Users/kyank/code/murmur/app/client/modules/ca-workflow-header/./WorkflowHeader/Decoders.elm”). And because many of our elm.json files refer to shared library directories whose paths begin with../../, I see plenty of/../in the reported source file paths, too (e.g. “Adding /Users/kyank/code/murmur/app/client/modules/ca-workflow-header/…/…/…/…/lib/client/modules/ca-ui/ProgressDialog.elm”).Is it possible that all of our source files are getting registered in the server with these “non-normalized” paths, so that when the client later sends a request for a path like
/Users/kyank/code/murmur/app/client/modules/ca-workflow-header/WorkflowHeader/Decoders.elm(without the/./), the server is unable to match it to any of its registered files? If so, might there be a possible fix of ensuring that the server usespath.resolveto resolve all of the source file paths to absolute, normalized paths before registering them?I think I found and fixed the underlying problem (currently in a branch) but it also helped me to see some more problems, that are connected to multiple elmWorkspaces in one vscode workspace. So I will have to keep working on this some more.
Feel free to assign this to me. I’m planning to submit a fix in the next few days.
OK, I think I have an idea of what’s going on. Once each
ElmWorkspacefinds all of the *.elm files that it contains (and their dependencies), it records these in a “tree”. When theServeris told that all of the trees have been built, it callsregisterInitializedProviderson eachElmWorkspace, which creates a set of providers. Each of those providers registers one or more event handlers on theconnection, like this:https://github.com/elm-tooling/elm-language-server/blob/88e5021e88b7861a9beefa6e5ecfdffeda401c64/src/providers/documentFormatingProvider.ts#L19
and this:
https://github.com/elm-tooling/elm-language-server/blob/88e5021e88b7861a9beefa6e5ecfdffeda401c64/src/providers/hoverProvider.ts#L20
The problem is, there’s only one
connectionand multipleElmWorkspaceinstances, so each call toonDocumentFormattingoronHoverreplaces any previously-registered handler with the new one. As a result, only the lastElmWorkspace’s handlers actually remain registered!I think the fix here will be to write a single
onDocumentFormattinghandler (and a singleonHoverhandler, and so on) that calls the corresponding handlers for all of the activeElmWorkspaces (and only the one that recognises the file will handle it). Unless you have any better ideas, I’m happy to get started on a prototype of this!We’ve just finally upgraded the last of our Elm 0.18 code here at Culture Amp, so we have a bunch of engineers eager to switch to this new editor extension. Unfortunately, in my testing, we’re falling prey to issues like these that seem related to having more than one elm.json root in a single VSCode workspace (we have 27 of them).
Is there any way I can help, @Razzeee?