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.

10 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.

2 Likes

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.

2 Likes

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.

5 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.

1 Like

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.:

1 Like

@non-Jedi After following those steps it keeps happening. I only changed all the instances of “1.1” to “1.2” since it is my Julia version.

Which versions of LanguageServer, CSTParser, and SymbolServer are installed in the environment you’re running the language server from? I’ve had issues with some of the released versions. It might be worth looking at the versions used with lsp-julia since gdkrmr has tracked down the exact git commits used with the VSCode plugin.

I just realized you’re trying to edit a file outside of a “Project” with eglot. All of my work is encapsulated in projects, so I haven’t tried that yet. Will mess around with it when I get a chance and see if I can replicate your experience. In the mean time, could you try to create a simple project for your code and try running eglot in a *.jl buffer under src? A simple project can be generated with:

using Pkg
Pkg.generate("ExampleProject")
1 Like

Hi.
First of all, thank your for trying to help me.
So, I tried running eglot in a *.jl buffer under src but the problem persists.
About the versions, I have LanguageServer 0.6.1, CSTParser 0.6.2, and SymbolServer 0.2.5.

One other thing to try is to set eglot-connect-timeout to a very large value, say 300 or so. It’s set to 60 in my config right now.

Like I said before, the team behind LanguageServer.jl doesn’t seem to always make sure their releases all work together properly. LanguageServer.jl is mostly used for the VSCode plugin which historically has just grabbed random git commits of the packages involved. Directly matching those commits has historically been the best way of ensuring stable operation of the language server.

Looking now, it appears they’re actually using released versions of each package for their latest vscode plugin release candidate. Those versions are probably your best bet.

  • CSTParser@0.6.1
  • DocumentFormat@0.4.1
  • LanguageServer@0.6.1
  • StaticLint@0.2.2
  • SymbolServer@0.2.5
  • Tokenize@0.5.5

If you use those specific versions of the package in your language server environment and are still having issues, we can probably safely eliminate the language server as the cause of the problem (eglot sends the language server a message and does not receive a reply even though the language server appears to be ready).

In the end I managed to get it to work but it is still very slow so I wont use it for the time being. Thank you for your help.