Skip to content

Add experimental eglot support (won't be merged)#58

Draft
johannes-mueller wants to merge 4 commits into
mainfrom
experimental-eglot-support
Draft

Add experimental eglot support (won't be merged)#58
johannes-mueller wants to merge 4 commits into
mainfrom
experimental-eglot-support

Conversation

@johannes-mueller

@johannes-mueller johannes-mueller commented Feb 28, 2026

Copy link
Copy Markdown
Owner

Exploring LSP support

This adds experimental support for eglot by doing the following:

  • advise eglot-path-to-uri and eglot-uri-to-path to translate back and forth the file path and file uris

  • provide the function devcontainer-eglot-server that sets up a language server for eglot inside the container if appropriate

  • advise eglot--connect to inject devcontainer exec -i to the LSP launch command

Example:

(add-to-list 'eglot-server-programs `((python-ts-mode) . ,(devcontainer-eglot-server '("rass" "python"))))

Open points

  • Can we somehow advise eglot to add the docker exec automatically if appropriate. The solution with devcontainer-eglot-server is somewhat clunky (probably better to advise eglot--connect)
  • The devcontainer must already be up, when eglot tries to start the container. Some UX optimization is needed there.
  • How to handle the scenario when the devcontainer is shutdown e.g. for a rebuild while the connection to the LSP-server is still up. eglot then would call eglot--reconnect and we must somehow deal with the fact, that at that point the devcontainer is most probably still not running.
  • Maybe we could inject some startup hook, that an LSP-server is started and connected to after the container has come up.
  • Real life testing

@selenil

selenil commented Mar 1, 2026

Copy link
Copy Markdown

Thanks for this!

I tried with two lsp, in the next days I'll be trying more (Note: I'm using a Linux distribution that symlinks /home to /var/home):

  • Go (gopls): The server starts and it processes a few messages like initialize and textDocument/didOpen but eventually returns an error when responding to the workspace/configuration[2]:
{"message":"Error loading packages: err: chdir /var/home/selenil/dev/go_devcontainer: no such file or directory: stderr: "}

When Eglot tries to do more work the server just keep replying with the same error. At some points this happens:

{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///workspaces/go_devcontainer/../../../../var/home/selenil/dev/go_devcontainer/main.go","diagnostics":[{"range":{"start":{"line":0,"character":8},"end":{"line":0,"character":12}},"severity":2,"source":"go list","message":"No active builds contain /workspaces/go_devcontainer/../../../../var/home/selenil/dev/go_devcontainer/main.go: consider opening a new workspace folder containing it"}]}}

And from there, the server keeps running but it ignores interactions with:

{"jsonrpc":"2.0","error":{"code":0,"message":"no views"},"id":3}
  • Gleam (built-in lsp): This one starts fine and I can do all the usual things and the server doesn't crash. However, some features are not reflected on the actual buffer. For example, I can run code actions and those messages will be processed successfully (Eglot even displays the "Edit successful!" message), but the actual contents of the file had not changed. Flymake don't display warnings or errors even if the code is invalid and the server has send a message with the exact error. xref finds correct definitions when doing jump-to-definition, but they pointed to invalid paths. Autocompletion and hover documentation work without issues.

Diggin into the messages I found that Eglot sends incorrect uris. For a file in /var/home/selenil/dev/my_project/src/app.gleam, Eglot sends: {"uri":"file:///var/home/selenil/var/home/selenil/dev/my_project/src/app.gleam"} so I guess that's why edits are not reflected in the file.

Can we somehow advise eglot to add the docker exec automatically if appropriate. The solution with devcontainer-eglot-server is somewhat clunky

In some early tests I did advising eglot--connect when in devcontainer-mode works fine.

@johannes-mueller

Copy link
Copy Markdown
Owner Author

Diggin into the messages I found that Eglot sends incorrect uris. For a file in /var/home/selenil/dev/my_project/src/app.gleam, Eglot sends: {"uri":"file:///var/home/selenil/var/home/selenil/dev/my_project/src/app.gleam"} so I guess that's why edits are not reflected in the file.

That's interesting. Not sure if it's related to the $HOME symlinking of your distribution. Maybe you can debug this by messaging out path, truepath, relative and in-container in devcontainer--path-to-uri-advice.

@johannes-mueller

Copy link
Copy Markdown
Owner Author

Another issue that I came across in real life testing is, that maybe not all LSPs can be expected to run in the container. So we need some way of deciding if we do the path uri translation or not depending on if the LSP server is running inside or outside the container.

@johannes-mueller johannes-mueller force-pushed the experimental-eglot-support branch from f1a8df1 to c085a3a Compare March 4, 2026 21:33
@selenil

selenil commented Mar 8, 2026

Copy link
Copy Markdown

I found that most of my issues were caused due to my distribution symlinking $HOME. The problem is that devcontainer--root returns paths starting with /home while in other places paths start with /var/home/, so when we get to this line the relative path is wrong: (relative (file-relative-name truepath (devcontainer--root))). Manually removing the /var prefix from the paths makes it work again.

Aside from that, the other issue I have is that Flymake doesn't report the error diagnostics in the UI. This doesn't seems to be related to the $HOME symlinking. I will dig into it when I have some time, but maybe we need to advice Flymake too.

This adds experimental support for eglot by doing the following:

* advice: `eglot-path-to-uri` and `eglot-uri-to-path` to translate back and
  forth the file path and file uris

* provide the function `devcontainer-eglot-server` that sets up a language
  server for eglot inside the container if appropriate
@johannes-mueller johannes-mueller force-pushed the experimental-eglot-support branch from c085a3a to 61046cd Compare March 14, 2026 08:09
@selenil

selenil commented Mar 14, 2026

Copy link
Copy Markdown

Aside from that, the other issue I have is that Flymake doesn't report the error diagnostics in the UI. This doesn't seems to be related to the $HOME symlinking. I will dig into it when I have some time, but maybe we need to advice Flymake too.

I was doing some debugging and it turns out that this issue is actually caused by the $HOME symlinking of my distribution. Currently I just patched the code to remove the /var prefix from the paths and with that all works fine.

It turns out that the overhead of figuring out the path inside the container
slows down `eglot-path-to-uri` and `eglot-uri-to-path` significantly.  Now we
are caching it for every file in a hash-table
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants