Using LanguageServer.jl with eglot in emacs

This weekend I got LanguageServer.jl working with eglot (an alternative to lsp-mode), and I documented the process in the LanguageServer.jl wiki. I haven’t had enough time to play with it and see how it compares to lsp-mode, but it seems to be working pretty well; in general my impression of it so far is positive compared to lsp-mode, but I don’t know if that’s because my lsp-julia package is screwing something up, because LanguageServer.jl has improved in the several months since I last used lsp-julia, or because eglot actually works better. Let me know if you have trouble getting things working.

8 Likes

Thanks for the wiki writeup, it should be very useful! AFAICT LanguageServer has improved a lot recently.

I could not make this work: I see Julia using a lot of CPU for a while, then eglot timeouts. Even increasing eglot-connect-timeout to 600 didn’t help.

I am curious how I could debug this, and whether it is just normal “time to first X”, or something else. eglot-events-buffer reports

jsonrpc-error: "No current JSON-RPC connection", (jsonrpc-error-code . 32603), (jsonrpc-error-message . "No current JSON-RPC connection")

I’d recommend running something like

using LanguageServer, SymbolServer
server = LanguageServerInstance(stdin, stdout, true, "/home/you/.julia/environments/v1.1", "", Dict())
run(server)

in a normal REPL and wait for a message like “Symbol store set” or similar. The first run will take a long time since it needs to load (and inspect) all packages in your environment.

1 Like

Perfect, this solved the issue.

@non-Jedi: would you consider packaging the code you wrote up in the wiki, say, as eglot-julia on MELPA?

To be honest, I don’t see a whole lot of utility in eglot+julia until
the crashing on save
problem

is fixed. In any case, the right™ way of packaging this is probably to
push all of the project.el integration code into
julia-mode

and then add integrated support to the eglot package for Julia’s
language server.

I don’t have time/inclination to do either of those things at this
moment in time, but if you do (or want to package it separately), I
hereby release any code I’ve written in the LanguageServer.jl wiki
under
CC0.

I your opinion, what are the advantages of eglot over lsp-mode? I have the impression that very few people actually use lsp-julia and therefore wanted to try eglot but there is no eglot support in spacemacs, so I am a bit hesitant.

(I’ll respond only here instead of on both github and here to keep the conversation in one place)

Most of the difference is that I find eglot easier to grok:

  1. The codebase was cleaner and easier to read.
  2. The documentation was simpler.
  3. There’s no equivalent to lsp-ui that adds lots of noise and confusion (and was needed for flycheck integration).
  4. eglot integrates with components that are included with emacs instead of external packages: completion-at-point instead of company-mode, project.el instead of projectile, flymake instead of flycheck, etc.

Philosophically, eglot is closer to a “library” than a framework whereas I found lsp-mode more on the “framework” side of that divide. It’s much the same reason I switched from using Spacemacs to having a vanilla emacs config.

Half the time, I don’t want the fancy IDE features provided by LanguageServer.jl at all, so when I do using eglot feels much less intrusive and closer to my normal editing environment than lsp-mode ever did.

I may have time to do this in the coming weeks. Would it be helpful to you as well @gdkrmr if you could simply install “eglot-julia” from melpa and M-x eglot in a julia buffer?

If anyone was holding off on trying eglot because of this, that particular bug is fixed now.

3 Likes

The main issue for me is that I use spacemacs and AFAIK no one has done any work to integrate spacemacs with eglot. Spacemacs uses all the packages you mention (projectile, company, flycheck) and changes their keybindings, this can make stepping outside of these packages a bit annoying.

company-mode can work with completion-at-point through the capf backend. Only thing project.el is used for is choosing the environment for a package and should work out of the box with the snippets I included in the LanguageServer.jl wiki. And flymake errors can be accessed simply with M-x help-at-point. For trying something out, none of this should be disruptive to the normal spacemacs workflow.

Yes, it would be great.

Packaging up for MELPA is not that much of a hassle, I did it for julia-repl. You just follow the guidelines (especially the linting), then get valuable feedback.

The thing I need to figure out is how to run a command on first install to instantiate the environment for LanguageServer.jl. Should be relatively straightforward other than that. Planning on looking at how emacs-jupyter/emacs-zmq do so.

1 Like

@non-Jedi I followed your process of making eglot work with LanguageServer.jl but it doesn’t seem to be working. This is what I get after launching eglot-mode in a *.jl named buffer.

*Messages*

[jsonrpc] Server exited with status 9
Error running timer: (error “[eglot] Timed out”)

EGLOT (tmp/julia-mode) events

client-request (id:1) Fri Sep 20 16:46:30 2019:
(:jsonrpc “2.0” :id 1 :method “initialize” :params
(:processId 26863 :rootPath “/tmp/” :rootUri “file:///tmp/” :initializationOptions nil :capabilities
(:workspace
(:applyEdit t :executeCommand
(:dynamicRegistration :json-false)
:workspaceEdit
(:documentChanges :json-false)
:didChangeWatchedFiles
(:dynamicRegistration t)
:symbol
(:dynamicRegistration :json-false))
:textDocument
(:synchronization
(:dynamicRegistration :json-false :willSave t :willSaveWaitUntil t :didSave t)
:completion
(:dynamicRegistration :json-false :completionItem
(:snippetSupport :json-false)
:contextSupport t)
:hover
(:dynamicRegistration :json-false)
:signatureHelp
(:dynamicRegistration :json-false :signatureInformation
(:parameterInformation
(:labelOffsetSupport t)))
:references
(:dynamicRegistration :json-false)
:definition
(:dynamicRegistration :json-false)
:documentSymbol
(:dynamicRegistration :json-false :symbolKind
(:valueSet
[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]))
:documentHighlight
(:dynamicRegistration :json-false)
:codeAction
(:dynamicRegistration :json-false :codeActionLiteralSupport
(:codeActionKind
(:valueSet
[“quickfix” “refactor” “refactor.extract” “refactor.inline” “refactor.rewrite” “source” “source.organizeImports”])))
:formatting
(:dynamicRegistration :json-false)
:rangeFormatting
(:dynamicRegistration :json-false)
:rename
(:dynamicRegistration :json-false)
:publishDiagnostics
(:relatedInformation :json-false))
:experimental nil)))
internal Fri Sep 20 16:47:00 2019:
(:message “Connection state changed” :change “killed\n”)
----------b—y---e—b---y—e----------

EGLOT (tmp/julia-mode) output

Activating environment at ~/.emacs.d/lisp/eglot-julia/Project.toml
Updating registry at ~/.julia/registries/General
Updating git-repo https://github.com/JuliaRegistries/General.git
e[?25le[2Ke[?25hContent-Length: 713
{“id”:1,“jsonrpc”:“2.0”,“result”:{“capabilities”:{“textDocumentSync”:2,“hoverProvider”:true,“completionProvider”:{“resolveProvider”:false,“triggerCharacters”:["."]},“signatureHelpProvider”:{“triggerCharacters”:["("]},“definitionProvider”:true,“typeDefinitionProvider”:false,“implementationProvider”:false,“referencesProvider”:true,“documentHighlightProvider”:false,“documentSymbolProvider”:true,“workspaceSymbolProvider”:true,“codeActionProvider”:false,“documentFormattingProvider”:true,“documentRangeFormattingProvider”:false,“renameProvider”:true,“colorProvider”:false,“executeCommandProvider”:{“commands”:},“workspace”:{“workspaceFolders”:{“supported”:true,“changeNotifications”:true}},“experimental”:null}}}

EGLOT (tmp/julia-mode) stderr

[ Info: Started symbol server
[ Info: store set
Process EGLOT (tmp/julia-mode) stderr finished

You’ll have to make sure that LanguageServer.jl is already compiled and cached before running eglot. See e.g.: