TLDR - [here's](https://github.com/zsol/buck-py-demo/tree/main/.vscode) a bunch of JSON that automatically enables basic code navigation for buck2 in VSCode.
I'm spending some time making a [demo repo](https://github.com/zsol/buck-py-demo) for buck2 & Python (more on that maybe later), and have started to miss a decent LSP. A lack of a good open-source VSCode language extension for buck2 meant that basic code navigation like jump-to-definition didn't work. This was annoying because I remembered that `buck2 lsp` is a perfectly capable language server that's already available in open-source.
## Generic LSP Client
I looked for a VSCode extension that provides simple language features using the [languageclient](https://www.npmjs.com/package/vscode-languageclient) module, and found [this promising one](https://github.com/llllvvuu/vscode-glspc) by @llllvvuu, but configuring it was a bit painful. I kept running into a mysterious error about a missing `notebookType` in the bowels of VSCode's extension framework.
So I [forked it](https://github.com/zsol/vscode-glspc) and started rebuilding parts of it while debugging, trying to make sure the next time I want to hook this up, I'll never ever have to attach a debugger to my VSCode window:
* the default settings no longer cause the extension to crash (in particular, `languageId` no longer defaults to an empty string)
* invalid server executable, or server crashes no longer crash the extension
* the language server is automatically restarted when any settings change
* exceptions during language server shutdown are caught and logged instead of causing an extension crash
* there's now an easy dropdown that lets you configure logging of all LSP traffic
* this takes effect immediately
* all output is captured in a single output pane (instead of one per LSP server restart)
* all output is now timestamped and errors are easily distinguishable from regular informational messages
* everything is configurable using the "Extension Settings" UI instead of having to drop down to `settings.json`
* except for sending custom initialization options, as that can be an arbitrary JSON object, so that's not possible to do from the UI
* there are no more obnoxious popups if there are no errors, only a subtle status bar update after the language server has successfully started up (or restarted)
The result is [here](https://marketplace.visualstudio.com/items?itemName=zsol.vscode-glspc), but I'll see if I can merge back to the original version.
## Language IDs
The extension only gets activated for files that match the language IDs specified in the configuration. For buck2, I need the extension to provide language services for any files named `BUCK` or `.bzl`.
Initially I did this by opening one of each type, and then `Ctrl+Shift+P` > `Change Language Mode` > `Configure File Association for BUCK` (or `.bzl`), and picking `starlark` from the dropdown (I got lucky because the Bazel extension already defined the `starlark` language id for me).
But then I realized you can do this from your VSCode settings. All you need to do is drop this snippet in there:
```json
"files.associations": {
"*.bzl": "starlark",
"BUCK": "starlark"
}
```
This is nicer because I can plop this into `.vscode/settings.json` **in the repo**, and everyone who uses VSCode in my repo gets these.
## Configuring the Extension
From here on, it was smooth sailing - all I had to make sure was to use my new extension for all files with the `starlark` language ID, and have the extension call `buck2 lsp` to fire up the server. I clicked this together for the workspace in the Extension Settings UI, but the relevant `settings.json` snippet is:
```json
{
"glspc.server.command": "buck2",
"glspc.server.commandArguments": ["lsp"],
"glspc.server.languageId": ["starlark"],
}
```
This goes into the repo next to the above snippet, and now anyone who already has my extension installed will automatically get code navigation powered by the `buck2` daemon.
## Installing the Extension
But what if you don't have the extension installed? Well, I could add this in the instructions into the repo's `README.md` (which I absolutely will), but I think that's just a last resort. Ideally, things should work out of the box.
As far as I can tell, VSCode doesn't actually allow repositories to install extensions on behalf of the user automatically, so the closest we can get is to show this to anyone opening the repo who don't already have the extension:
![[Extension Recommendations screenshot.png]]
At least this is hard to miss, and with a single click you'll get on your way. To make this happen, I needed to put this json snippet into `.vscode/extensions.json`:
```json
{
"recommendations": ["zsol.vscode-glspc"]
}
```
Finally, we need to ensure that the `starlark` language ID is actually recognized by VSCode, which unfortunately isn't possible to do in a generic language extension, so I've added another extension that recognizes starlark files (and provides syntax highlighting for them).