Getting VS2019 to work with the Clangd LSP server

Continuing with my work in making Antlrvsix a Language Server Protocol server implementation, I created another extension for Visual Studio 2019 that uses the client API Microsoft.VisualStudio.LanguageServer.Client, this time with the Clangd LSP server. The extension source code is here. But, there is a hitch…

This client does not work out-of-the-box with version 9 of Clangd (LLVM). After a bit of bantering about what the LSP protocol means, I learned that the Clangd LSP server, while it follows the LSP spec, it does not follow the intent of the LSP protocol that any client can work with any server. In the spec, TextDocumentSyncKind is used to tell the client whether the server requires open requests and incremental vs. full file content updates. Clangd indicates that it is an “Incremental” server (“Documents are synced by sending the full content on open. After that only incremental updates to the document are send.”) Whether this also means that an open is required even for language feature calls, the spec isn’t so clear-cut, so anyone can interpret the spec however they want. But Clangd assumes it is required, even for language feature requests. As it goes, the server rejects all requests from client after the client/server initialization conversation, so the server never opens the files, never emits a diagnostics notification to the client, and the client never issues any open. It’s a complete stalemate.

The problem here is two-fold: (1) the spec is ambigous, and (2) Clangd assumes the worst case possible, that an open is required even for language feature requests. Unfortunately, open is essentially a locking mechanism, where the “truth” is a “write lock” on the entire contents of the file. Clearly, requiring a write lock on a file in order to use the server for language markup is ridiculous, but I can’t seem to convince anyone that there are multiple problems here. The fact is that Clangd already reads #include files on disk without locking each of those files, a fact the developers ignore.

When I made a change request to Clangd to back off on this assumption, a change that affects 0.5% of the source code, it was rejected based on being too complex. Apparently the authors insist that the client issue an open request immediately after initialization. But, the change does work.

Even with the change, the server took 60+ s to parse and perform semantic analysis of a 7-line “Hello World” program before able to process Language Feature requests. (It was a debug build, so I will check it against a release build.) Pre-indexing with the tool clangd-indexer did not improve the processing time. Surprisingly, in VSCode the server responds in a few seconds.

With changes in the master branch of LLVM post-release 9.x, the MS VS IDE client code (https://www.nuget.org/packages/Microsoft.VisualStudio.LanguageServer.Client/16.3.57) no longer works at all with Clangd due to an unrecognized initialization response packet returned from the server. Other operations such as “go to def”, “find all refs”, reformat, hover, and typing completions do work.

Based on my interactions with the folks developing Clangd and the LSP spec, it feels they’re okay working with one client–Visual Studio Code–and with a bad spec. If you aren’t aware, MS announced Visual Studio online, with a screenshot that essentially showing the Visual Studio Code UI. It looks the end of days for Visual Studio IDE, and why there needs to be an LSP implementation for Antlr.

One further sidenote: LLVM and the build procedure have completely reorganized since the source code is now on Github.com. To do a build:

git clone --branch release/9.x https://github.com/llvm/llvm-project.git
mkdir build
cd build
cmake -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;clangd;clangd-xpc-support" -G "Visual Studio 16 2019" -Thost=x64 ..\llvm-project\llvm
msbuild LLVM.sln /p:Configuration=Debug /p:Platform=x64

Posted in Tip