jacobdufault / cquery

C/C++ language server supporting multi-million line code base, powered by libclang. Emacs, Vim, VSCode, and others with language server protocol support. Cross references, completion, diagnostics, semantic highlighting and more
MIT License
2.35k stars 163 forks source link

Support for multiple root workspaces as Language Server Protocol describes #633

Open cstrike opened 6 years ago

cstrike commented 6 years ago

Currently, cquery supports only one workspace folder, while Language Server Protocol has been updated to support multiple workspace folders. Can cquery follow this update?

jacobdufault commented 6 years ago

A PR for this would be appreciated

cstrike commented 6 years ago

Sorry to make you miss-understand. It is just my requirement. :) @jacobdufault

jacobdufault commented 6 years ago

I'm happy to support this, but it is unlikely I'll have the time to get to it in the near future.

jlippuner commented 6 years ago

I need this feature too and I'd be happy to help, but I don't know where to start. In the meantime, is there a workaround? I'm using a multi-root workspace in VS Code and cquery doesn't work at all (I doesn't find the compile_command.json for the individual projects). If I open only one project, it works.

jacobdufault commented 6 years ago

Likely have cquery send a workspace/workspaceFolders request and then run the project import logic (in project.cc) on all of the folders in the returned results. The request can probably be sent from src/messages/initialize.cc

https://microsoft.github.io/language-server-protocol/specification#workspace_workspaceFolders

decimad commented 6 years ago

Wouldn't it really make sense to have a language server per directory? It seems strange to replicate the duality down the whole stack...

jacobdufault commented 6 years ago

Maybe that's how it works and the vscode client just needs to be updated; I haven't done much research into it.

jlippuner commented 6 years ago

So, I've done some digging and testing. There is no need to make an extra request. The list of workspace folders is passed in the InitializeParams and it's trivial to read them in the lsInitializeParams struct. The question now is how to handle multiple projects in the same Handler_Initialize since the parent class MessageHandler seems to assume that there is only one project.

Furthermore, the rootUri field of the InitializeParams is simply the first workspace folder in the list (not a parent directory). So if we just run project->Load on each workspace folder, then the index cache for all of them would end up in the index of the first workspace folder. I think this might actually work in the sense that all files of all workspace folders get parsed correctly and there is in some sense just one project for the entire workspace. But it doesn't seem like a particularly elegant solution.

Maybe we can just run the logic in initializer.cc between lines 624 and 672 for each workspace folder, but I don't know whether that would have undesired side effects. Maybe then cquery would have to know which project is being used each time it's called subsequently. (I don't really know how language servers work.)

jacobdufault commented 6 years ago

The init changes and propagating the set of folders to all handlers sound good to me. For the cache changes, I think modifying GetCachePath to resolve the correct cache directory should be good enough.

https://github.com/cquery-project/cquery/blob/4557eb7f2b0d0428832d85eb54af629d931602c4/src/cache_manager.cc#L45-L58

jlippuner commented 6 years ago

I think the correct solution would be to have a separate Project for each workspace folder and the message handler base class would own the collection of all these projects. That would require changes in the code in many different places and unfortunately, I don't currently have the time to implement this. It would also require handling DidChangeWorkspaceFolders messages (https://microsoft.github.io/language-server-protocol/specification#workspace_didChangeWorkspaceFolders) when new workspace folders, i.e. projects are added or removed.

Mainly for my own benefit, I have implemented a quick and dirty hack (basically the first solution I described in my previous comment): https://github.com/jlippuner/cquery/commit/1140fbf9559ddc70b9a656bfb203c2a8f6b14c86 I've modified the Project class so that the Load method appends to the list of entries and include directories and then the initialization handler simply calls Load for each workspace folder. So there is only one index cache named after the first workspace folder. It seems to work for my case, but I'm not sure if there could be some interference between different workspace folders (which are essentially different projects) being part of the same cquery project...